gRPC

gRPC 1.2.3をVisual Studio C++(2015)でビルドする

More than 1 year has passed since last update.

いつの間にかバージョン1がリリースされ、1.2まで出ていたgRPC。

ゴリゴリのサーバーサイドな方達以外でも、興味持っている人は多いんじゃないでしょうか。

C#や他の言語はパッケージをダウンロードすればすぐに開発が始められるのに対し、C++は自分でビルドしろという放置っぷりなので、改めてビルド方法をまとめておきます。

後述しますが、1.2.3ではプロジェクトファイルの一部にバグがあり、そのままではビルドできません。「open grpc.sln with Visual Studio and hit "Build".」とかドヤっといてこの有様かよ!いやでも、Windowsをサポートしてくれるだけありがたく思わなきゃいけませんね。


環境

このTIPSでは、以下の環境を使用して評価・検証を行っています。

なお、今回のtipsでビルドするのは32bit版ライブラリです。64bitが欲しい人は適宜読み替えてください。

項目
内容

OS
Windows 10 pro 64bit

Machine
intel core i5 2500K / memory 16GB

MSVC++
2015 update2

CMake
3.4.1

gRPC
1.2.3

protobuf
3.2.0


ビルド

GitHubから該当バージョンをダウンロードし、適当なディレクトリに展開します。以前はサブモジュールがあるからgit cloneしろと言ってましたが、今回環境にgit入れるのがめんどかったのでやんないです。基本的にはnugetで引っ張ってきてくれるし、手動でビルドしなきゃいけないのはprotobufだけになっていて、あんまり気になりません。


protobufのビルド

GitHubから、protobuf 3.2.0をダウンロードして展開し、中身をgrpc-1.2.3\third_party\protobuf配下にコピーします。protobuf配下にbenchmarksやらcmakeやら並ぶ感じです。


cmakeでvsprojectを生成

cmake guiを起動し、protobufのvsprojectを生成します。

Where is the source code:にはprotobuf/cmakeディレクトリを指定し、Where to build the binaries:には、protobuf/cmake/build/solutionと指定しとくと後々楽になります。

02.png

Configureを押すと、「お前が指定した出力先ねーから作るけどいいよな!?」と聞かれるのでYesします。

03.png

Visual Studioのバージョン聞かれるので選択します。Use default narive compilersで問題無いと思うけど、コンパイラ見つからなくてエラーになる場合は、specify narive compilersでvcコンパイラを指定してあげるとできます。

04.png

準備が整ったらGenerate!

なお、protobuf_MSVC_STATIC_RUNTIMEチェックが入ってるとMT(Multi Thread)、入ってないとMD(Multi Thread Dll)のプロジェクトファイルが生成されます。gRPCの方はデフォルトでは全てMT設定なので、ここはとりあえずMTのまま行きます。

05.png


protobuf.slnのビルド

grpc-1.2.3\third_party\protobuf\cmake\build\solution\protobuf.slnを開き、ReleaseとDebugでビルドする。なんか沢山エラーが出たけど、testモジュールなので無視します。(プロダクションに使用するならちゃんとテストビルドも通さないとやばいですね)なんか文字化けしてる。こわい。

06.png


gRPCのビルド


grpc_protoc_plugins.slnのビルド

grpc-1.2.3\vsprojects\grpc_protoc_plugins.slnを開き、ビルドします。CMakeに前述のパスを指定してGenerateしていれば、ヘッダやライブラリは勝手に見つけてくれます。これは自動生成ソースを生成するためのツール類なので、普通の人はRelease版だけあれば良いと思います。ちなみにDebug版をビルドしようとすると、lib名が間違っててビルドできません。お茶女。


grpc.slnのビルド

さていよいよgrpc本体をビルドします。ソリューション構成にRelase/DebugとRelease-DLL/Debug-DLLがありますが、-DLLのある無しで出力パスが変わるぐらいでした。何のためにあるんだろうこれ。

ビルドすると、以下のようなエラーが出ます。冒頭で言ってた奴ですね。

07.png

GitHubのissueには「これを削除するとビルドできる」と書いてありますが、実はそれだけだと後でアプリケーションにリンクするときにエラーが出ます。というわけで、grpc_dll->grpc.defを以下のように編集します。



  • 削除


    • census_set_trace_mask

    • census_trace_mask

    • census_trace_print

    • grpc_slice_buf_cmp




  • 追加


    • gpr_mpscq_init

    • gpr_mpscq_destroy

    • gpr_mpscq_push

    • gpr_mpscq_pop




grpc.def

EXPORTS

grpc_raw_byte_buffer_create
grpc_raw_compressed_byte_buffer_create
grpc_byte_buffer_copy
grpc_byte_buffer_length
grpc_byte_buffer_destroy
grpc_byte_buffer_reader_init
grpc_byte_buffer_reader_destroy
grpc_byte_buffer_reader_next
grpc_byte_buffer_reader_readall
grpc_raw_byte_buffer_from_reader
census_initialize
census_shutdown
census_supported
census_enabled
census_context_create
census_context_destroy
census_context_get_status
census_context_initialize_iterator
census_context_next_tag
census_context_get_tag
census_context_encode
census_context_decode
census_start_rpc_op_timestamp
census_start_client_rpc_op
census_set_rpc_client_peer
census_start_server_rpc_op
census_start_op
census_end_op
census_trace_scan_start
census_get_trace_record
census_trace_scan_end
census_define_resource
census_delete_resource
census_resource_id
census_record_values
grpc_compression_algorithm_parse
grpc_compression_algorithm_name
grpc_compression_algorithm_for_level
grpc_compression_options_init
grpc_compression_options_enable_algorithm
grpc_compression_options_disable_algorithm
grpc_compression_options_is_algorithm_enabled
grpc_metadata_array_init
grpc_metadata_array_destroy
grpc_call_details_init
grpc_call_details_destroy
grpc_register_plugin
grpc_init
grpc_shutdown
grpc_version_string
grpc_g_stands_for
grpc_completion_queue_create
grpc_completion_queue_next
grpc_completion_queue_pluck
grpc_completion_queue_shutdown
grpc_completion_queue_destroy
grpc_alarm_create
grpc_alarm_cancel
grpc_alarm_destroy
grpc_channel_check_connectivity_state
grpc_channel_watch_connectivity_state
grpc_channel_create_call
grpc_channel_ping
grpc_channel_register_call
grpc_channel_create_registered_call
grpc_call_start_batch
grpc_call_get_peer
grpc_call_set_load_reporting_cost_context
grpc_census_call_set_context
grpc_census_call_get_context
grpc_channel_get_target
grpc_channel_get_info
grpc_insecure_channel_create
grpc_lame_client_channel_create
grpc_channel_destroy
grpc_call_cancel
grpc_call_cancel_with_status
grpc_call_destroy
grpc_server_request_call
grpc_server_register_method
grpc_server_request_registered_call
grpc_server_create
grpc_server_register_completion_queue
grpc_server_register_non_listening_completion_queue
grpc_server_add_insecure_http2_port
grpc_server_start
grpc_server_shutdown_and_notify
grpc_server_cancel_all_calls
grpc_server_destroy
grpc_tracer_set_enabled
grpc_header_key_is_legal
grpc_header_nonbin_value_is_legal
grpc_is_binary_header
grpc_call_error_to_string
grpc_resource_quota_create
grpc_resource_quota_ref
grpc_resource_quota_unref
grpc_resource_quota_resize
grpc_resource_quota_arg_vtable
grpc_insecure_channel_create_from_fd
grpc_server_add_insecure_channel_from_fd
grpc_use_signal
grpc_auth_property_iterator_next
grpc_auth_context_property_iterator
grpc_auth_context_peer_identity
grpc_auth_context_find_properties_by_name
grpc_auth_context_peer_identity_property_name
grpc_auth_context_peer_is_authenticated
grpc_call_auth_context
grpc_auth_context_release
grpc_auth_context_add_property
grpc_auth_context_add_cstring_property
grpc_auth_context_set_peer_identity_property_name
grpc_channel_credentials_release
grpc_google_default_credentials_create
grpc_set_ssl_roots_override_callback
grpc_ssl_credentials_create
grpc_call_credentials_release
grpc_composite_channel_credentials_create
grpc_composite_call_credentials_create
grpc_google_compute_engine_credentials_create
grpc_max_auth_token_lifetime
grpc_service_account_jwt_access_credentials_create
grpc_google_refresh_token_credentials_create
grpc_access_token_credentials_create
grpc_google_iam_credentials_create
grpc_metadata_credentials_create_from_plugin
grpc_secure_channel_create
grpc_server_credentials_release
grpc_ssl_server_credentials_create
grpc_ssl_server_credentials_create_ex
grpc_server_add_secure_http2_port
grpc_call_set_credentials
grpc_server_credentials_set_auth_metadata_processor
grpc_slice_ref
grpc_slice_unref
grpc_slice_new
grpc_slice_new_with_user_data
grpc_slice_new_with_len
grpc_slice_malloc
grpc_slice_intern
grpc_slice_from_copied_string
grpc_slice_from_copied_buffer
grpc_slice_from_static_string
grpc_slice_from_static_buffer
grpc_slice_sub
grpc_slice_sub_no_ref
grpc_slice_split_tail
grpc_slice_split_head
grpc_empty_slice
grpc_slice_default_hash_impl
grpc_slice_default_eq_impl
grpc_slice_eq
grpc_slice_cmp
grpc_slice_str_cmp
grpc_slice_buf_start_eq
grpc_slice_rchr
grpc_slice_chr
grpc_slice_slice
grpc_slice_hash
grpc_slice_is_equivalent
grpc_slice_dup
grpc_slice_to_c_string
grpc_slice_buffer_init
grpc_slice_buffer_destroy
grpc_slice_buffer_add
grpc_slice_buffer_add_indexed
grpc_slice_buffer_addn
grpc_slice_buffer_tiny_add
grpc_slice_buffer_pop
grpc_slice_buffer_reset_and_unref
grpc_slice_buffer_swap
grpc_slice_buffer_move_into
grpc_slice_buffer_trim_end
grpc_slice_buffer_move_first
grpc_slice_buffer_move_first_into_buffer
grpc_slice_buffer_take_first
grpc_slice_buffer_undo_take_first
gpr_malloc
gpr_zalloc
gpr_free
gpr_realloc
gpr_malloc_aligned
gpr_free_aligned
gpr_set_allocation_functions
gpr_get_allocation_functions
gpr_avl_create
gpr_avl_ref
gpr_avl_unref
gpr_avl_add
gpr_avl_remove
gpr_avl_get
gpr_avl_maybe_get
gpr_avl_is_empty
gpr_cmdline_create
gpr_cmdline_add_int
gpr_cmdline_add_flag
gpr_cmdline_add_string
gpr_cmdline_on_extra_arg
gpr_cmdline_set_survive_failure
gpr_cmdline_parse
gpr_cmdline_destroy
gpr_cmdline_usage_string
gpr_cpu_num_cores
gpr_cpu_current_cpu
gpr_histogram_create
gpr_histogram_destroy
gpr_histogram_add
gpr_histogram_merge
gpr_histogram_percentile
gpr_histogram_mean
gpr_histogram_stddev
gpr_histogram_variance
gpr_histogram_maximum
gpr_histogram_minimum
gpr_histogram_count
gpr_histogram_sum
gpr_histogram_sum_of_squares
gpr_histogram_get_contents
gpr_histogram_merge_contents
gpr_join_host_port
gpr_split_host_port
gpr_log
gpr_log_message
gpr_set_log_verbosity
gpr_log_verbosity_init
gpr_set_log_function
gpr_format_message
gpr_strdup
gpr_asprintf
gpr_subprocess_binary_extension
gpr_subprocess_create
gpr_subprocess_destroy
gpr_subprocess_join
gpr_subprocess_interrupt
gpr_mu_init
gpr_mu_destroy
gpr_mu_lock
gpr_mu_unlock
gpr_mu_trylock
gpr_cv_init
gpr_cv_destroy
gpr_cv_wait
gpr_cv_signal
gpr_cv_broadcast
gpr_once_init
gpr_event_init
gpr_event_set
gpr_event_get
gpr_event_wait
gpr_ref_init
gpr_ref
gpr_ref_non_zero
gpr_refn
gpr_unref
gpr_stats_init
gpr_stats_inc
gpr_stats_read
gpr_thd_new
gpr_thd_options_default
gpr_thd_options_set_detached
gpr_thd_options_set_joinable
gpr_thd_options_is_detached
gpr_thd_options_is_joinable
gpr_thd_currentid
gpr_thd_join
gpr_time_0
gpr_inf_future
gpr_inf_past
gpr_time_init
gpr_now
gpr_convert_clock_type
gpr_time_cmp
gpr_time_max
gpr_time_min
gpr_time_add
gpr_time_sub
gpr_time_from_micros
gpr_time_from_nanos
gpr_time_from_millis
gpr_time_from_seconds
gpr_time_from_minutes
gpr_time_from_hours
gpr_time_to_millis
gpr_time_similar
gpr_sleep_until
gpr_timespec_to_micros
gpr_mpscq_init
gpr_mpscq_destroy
gpr_mpscq_push
gpr_mpscq_pop

さあ、これでとりあえずビルドまではできるはず。お疲れ様です。

今後は、protocの使い方や、grpc-gatewayの使い方などを投稿していきたいと思います。


補足 java用プラグインのビルド

grpcプロジェクトにはgoとjava以外のプラグインが含まれています。goはgo getで持ってこれますが、java用のプラグインは自力でビルドする必要があります。公式の手順ではgradlewのビルド設定を使用するみたいです。以下は公式にアナウンスされている方法ではないですが、手っ取り早くjava用のプラグインをビルドする方法です。


  • ソースをコピーしてくる

まずは、GitHubからgrpc-javaをダウンロードし、適当なディレクトリに展開します。展開されたgrpc-java-1.2.0\compiler\src\java_plugin\cpp配下のファイル全てを、grpcのビルド時に使用していたディレクトリのgrpc-1.2.3\src\compilerへコピーします。


  • プラグイン用プロジェクトをコピーしてソリューションに追加する

インクルード設定やリンカ設定がめんどくさいので、既存のプラグインプロジェクトをコピーして流用します。grpc-1.2.3\vsprojects\vcxproj\grpc_cpp_pluginをコピーし、ディレクトリ名とプロジェクト名をgrpc_cpp_pluginからprotoc-gen-grpc-javaへリネームします。

grpc-1.2.3\vsprojects\grpc_protoc_plugins.slnを開き、上記でリネームしたprotoc-gen-grpc-java.vcxprojをソリューションに追加します。protoc-gen-grpc-java.vcxprojに含まれるcpp_plugin.ccを削除(ファイルごと削除しないでくださいね)し、代わりにgrpc-1.2.3\src\compiler\java_plugin.cppを追加します。

プロジェクトのプロパティから、構成→全般→ターゲット名をgrpc_cpp_pluginからprotoc-gen-grpc-javaへ変更します。(名前に一貫性がありませんが、grpcでは今に始まったことではありません。公式の通りの実行ファイル名です)また、C/C++→全般→警告をエラーとして扱うをはい(/WX)から いいえ(/WX-)へ変更します。


  • プラグイン用ライブラリプロジェクトにソースを追加

grpc_plugin_supportプロジェクトに、grpc-1.2.3\src\compiler\java_generator.cpp / java_generator.hを追加します。ついでに、java_generator.cpp ソースに#include <iterator>を追加しましょう。参照エラーを吐きます。

プロジェクトのプロパティから、C/C++→全般→警告をエラーとして扱うをはい(/WX)から いいえ(/WX-)へ変更します。

以上でjava用プラグインがビルドできるようになります。書き出してみると全然手っ取り早く無いですね。java環境を作る際にはgradlew設定するでしょうし、普通はそちらを使った方が良いでしょう。