本文を読み飛ばす

CMakeとProtocol BuffersとVisual Studio 2015

Google Protocol Buffers を Visual Studio 2015 でビルドする手順と、Visual Studio 2015 プロジェクトからそれらを参照する方法を備忘録。なお以下 4 種類のビルドを行えるようにする:

  • Win32 Debug
  • Win32 Release
  • x64 Debug
  • x64 Release

Protocol Buffers をビルドする際の要点

  • Protocol Buffers を Visual Studio 2015 でビルドするには CMake を使う
  • 定義されている CMake のターゲット名として、自動単体テスト用の check、インストール用の install がある
  • ビルド結果の単体テストには Google Mock, Google Test が使われている
    • 自分で取ってきて配置するのは面倒なので、それらが同梱された protobuf-cpp-x.x.x.zip という名前のアーカイブを使うと楽で良い
  • ヘッダ・ライブラリのインストール先は CMAKE_INSTALL_PREFIX で指定
    • リリースビルドとデバッグビルドは、ライブラリファイル名が異なるため同一ディレクトリに配置可能
    • Win32 版と x64 版は、ライブラリファイル名が同じなので異なるディレクトリに配置する必要あり
  • 日本語環境では、単体テスト用ソースの一部で文字コード関連のエラーが発生する(少なくとも 3.3.0 の場合は)
    • ビルドに先立って _CL_ 環境変数に /utf-8 を付けるか、問題のソースを修正してからビルドする
  • よく分からないが Visual Studio プロジェクトファイルを生成した場合 msbuildcmake --build から単体テストを実行すると失敗するので、単体テストだけはテストプログラムを直接起動する必要がある(少なくとも 3.3.0 の場合は)
  • Protocol Buffers を使うプログラムの方では、find_package(Protobuf) で検索すればよい

配置例

以下のように C:\Libraries というディレクトリに Win32x64 という、各プラットフォーム向けのライブラリ一式を格納するサブディレクトリを作り、その下に Protocol Buffers のファイルを配置することにする:

C:\Libraries\Win32\bin
C:\Libraries\Win32\include
C:\Libraries\Win32\lib
C:\Libraries\x64\bin
C:\Libraries\x64\include
C:\Libraries\x64\lib

ビルド・インストールの手順例

以下、protobuf-cpp-3.3.0.zip に含まれる protobuf-3.3.0 を、C:\Source に展開した前提で記す。

「Visual Studio 2015 開発者コマンドプロンプト」を開いてソースを展開したディレクトリに入り、次のように Visual Studio のプロジェクトファイルを生成する(コピペしたり、バッチファイルに転記できるようプロンプト部分は省略):

set _CL_=/utf-8
cd "C:\Source\protobuf-3.3.0\cmake"

REM --- Visual Studio のプロジェクトファイルを生成 ---
mkdir build\x64
cd build\x64
cmake ..\.. -A x64 -DCMAKE_INSTALL_PREFIX=C:\Libraries\x64

REM --- ビルドと単体テストの実行 ---
cmake --build . --config Debug
Debug\tests.exe

最初に _CL_ という環境変数を設定している点について補足。Protocol Buffers 3.3.0 に付属する単体テストのソースの一部には UTF-8 としても不正なデータ列が含まれている(そういうデータを正しく拒否できるかテストしている)。しかし、日本語環境では Visual Studio はソースファイルを Shift JIS だと思って解釈し始め、その不正なバイト列に出くわしたところで何かがオカシくなるらしく、その後のソースを正しく解釈できなくなってビルドに失敗してしまう。そこで、C/C++ 用コンパイルオプションに /utf-8 を強制的に追加することで対処すべく環境変数 _CL_ に設定を行っている。

また、日本語環境でビルドすると CommandLineInterfaceTest.Win32ErrorMessage というテストが失敗すると思う。このテストの実装を読めば分かるけれど、日本語環境では確実に失敗する内容なので無視して良いと思う。

それと、ターゲット "check" つまり check.vcxproj をビルドすると単体テストができるのだけれど、Visual Studio の IDE で開いてビルドすればテスト成功するのに、msbuild コマンド(したがって cmake --build も)では、なぜか大半のテストが失敗してしまった。ただし上記のようにテストプログラムである tests.exe を直接実行してやると成功するので、少なくとも生成されたライブラリが壊れていないことは確認できたと判断し、良しとした。

続いて、次のコマンドでインストールを行う:

cmake --build . --config Debug --target install

なおインストール先はプロジェクト生成時に指定した CMAKE_INSTALL_PREFIX に従い、 C:\Libraries\x64 下に includelib 等が出力される。

以上で x64 版のデバッグビルドについては作業完了。続いて x64 版のリリースビルドを作成する。手順は同様。

cd "C:\Source\protobuf-3.3.0\cmake\build\x64"

cmake --build . --config Release
Release\tests.exe
cmake --build . --config Release --target install

そして Win32 版のデバッグビルド、Win32 版のリリースビルドも同様に作成する:

cd "C:\Source\protobuf-3.3.0\cmake\build\Win32"

cmake --build . --config Debug
Debug\tests.exe
cmake --build . --config Debug --target install

cmake --build . --config Release
Release\tests.exe
cmake --build . --config Release --target install

Protocol Buffers を使うプログラム側

環境変数 CMAKE_PREFIX_PATH または cmake コマンド実行時のコマンドラインオプション -DCMAKE_PREFIX_PATH で、C:\Libraries\Win32 または C:\Libraries\x64 のうち適切な方を設定しつつ Visual Studio プロジェクトを生成すれば良い。

次のような 3 ファイルしか無い単純なソースツリーのプロジェクトを前提にすると:

src/CMakeLists.txt
src/example.cc   # プロトコルバッファを使うプログラムのソース
src/person.proto # プロトコルバッファ定義ファイル

CMakeLists.txt は次のような感じになった:

cmake_minimum_required(VERSION 2.8)

project(CMakeExample_Protobuf)

# Visual Studio には CRT ライブラリを静的リンクさせる(プロトコルバッファに合わせる)
if(MSVC)
    string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_DEBUG            ${CMAKE_C_FLAGS_DEBUG})
    string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_MINSIZEREL       ${CMAKE_C_FLAGS_MINSIZEREL})
    string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE          ${CMAKE_C_FLAGS_RELEASE})
    string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO   ${CMAKE_C_FLAGS_RELWITHDEBINFO})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG          ${CMAKE_CXX_FLAGS_DEBUG})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL     ${CMAKE_CXX_FLAGS_MINSIZEREL})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE        ${CMAKE_CXX_FLAGS_RELEASE})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
endif(MSVC)

# プロトコルバッファのライブラリを検索する
find_package(Protobuf REQUIRED)
message(STATUS "PROTOBUF_INCLUDE_DIRS: ${PROTOBUF_INCLUDE_DIRS}") # デバッグ用
message(STATUS "PROTOBUF_LIBRARIES: ${PROTOBUF_LIBRARIES}")       # デバッグ用

# "person.proto" から .pb.cc と .pb.h を生成し、生成されたソースとヘッダのファイル名を変数で受ける
protobuf_generate_cpp(MY_PROTOBUF_SOURCES MY_PROTOBUF_HEADERS
    person.proto)
message(STATUS "MY_PROTOBUF_SOURCES: ${MY_PROTOBUF_SOURCES}")     # デバッグ用
message(STATUS "MY_PROTOBUF_HEADERS: ${MY_PROTOBUF_HEADERS}")     # デバッグ用

# プロトコルバッファが生成した .pb.h のあるディレクトリを変数に入れておく
set(MY_PROTOBUF_HEADER_DIR ${CMAKE_CURRENT_BINARY_DIR})

# 後は普通にプログラムをビルドするための定義をしていく
add_executable(example
    example.cc
    ${MY_PROTOBUF_SOURCES}
    ${MY_PROTOBUF_HEADERS})

include_directories(
    ${PROJECT_SOURCE_DIR}
    ${PROTOBUF_INCLUDE_DIRS}
    ${MY_PROTOBUF_HEADER_DIR})

target_link_libraries(example
    ${PROTOBUF_LIBRARIES})