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

  • 1
    いいね
  • 0
    コメント

いつの間にかバージョン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設定するでしょうし、普通はそちらを使った方が良いでしょう。