タイトルが少しややこしいので最初に整理します。
- このメモは: C++ のパッケージマネージャーの選択のはなし
- ただし:
- アプリはクロスプラットフォーム ( このメモでの具体例は Windows-10 & Ubuntu-19.04 )
- アプリ全体(=このメモでは「ソリューション」とします)はいくつかの構成部品(=このメモでは「プロジェクト」とします)に分けて作られる
- プロジェクトの1つ以上に C++ を採用したい
- そのプロジェクト単位で C++ のライブラリーを管理できるパッケージマネージャーを導入したい → どうするのが楽そうかな
のメモです。
選択肢と大雑把な検討
- conan https://conan.io/
- クロスプラットフォーム対応の C++ のパッケージマネージャーが欲しいの悩みに答えてくれる定番。 CMake でごにょごにょする
- vcpkg https://github.com/microsoft/vcpkg
- Microsoft が Windows 向けの Visual Studio のために作ったパッケージマネージャー「だった」。今では GNU/Linux と OSX も対象プラットフォームという事になっている
- Hunter https://github.com/cpp-pm/hunter
- conan よりキレイに CMake でごにょごにょしてくれるらしいクロスプラットフォーム対応の C++ パッケージマネージャー
- (最終手段) ライブラリーごとに手書きの CMake 魔術を作りプロジェクトの CMakeLists.txt へ仕込む https://cmake.org/cmake/help/latest/
- これを選択すると保守の手間が増えるので 1, 2, 3 の何れかにしたい
対応パッケージの手広さ対決
登録パッケージ数を比べてもあんまり意味が無いので、実際に私がソフトウェア開発プロジェクトで触れる機会、可能性のあるライブラリーやフレームワークを適当に列挙して、実際にコマンドで search を叩く、最新のリストから grep しつつ、ついでにライセンスとオリジナルの入手情報も整理しました。パッケージ名に気を付けると良さそうな場合は有無の〇×の〇代わりにパッケージ名を記載しました。
要注意 この表は 2019年12月29日 の調査結果です。
この調査結果からは、私の中では意外な事に vcpkg がとても有用そうな事がわかりました。少なくとも Windows で cl.exe 系のツールチェインを使う場合には vcpkg が期待動作しない事も少ないでしょうから、クロスプラットフォーム対応ではなく Windows だけを対象にする場合は vcpkg 一択で良さそうです。
vcpkg の懸念は Windows 以外でも期待動作してくれるのか…です。これは実際に試してみないと安心できません。
お試し
このメモでは...
- Eigen http://eigen.tuxfamily.org/ と
- // header only
- Message Pack (msgpack-c) https://github.com/msgpack/msgpack-c を
- // build required
conan, vcpkg, Hunter で取り込んでみます。
翻訳するソース my_something.cpp は...
#include <Eigen/Core> #include <msgpack.hpp> #include <fstream> extern "C" double my_norm( double x, double y, double z ) { return ::Eigen::Vector3d{ x, y, z }; } extern "C" void my_serialize( const std::string& path, double value ) { ::msgpack::sbuffer b; ::msgpack::pack( b, value ); ::std::oftream( path, ::std::ios::binary ).write( b.data(), b.size() ); }
無事に build.cmake/lib/libmy_something.so を作れた場合は、 python と od (Ubuntuなどの場合) または Format-Hex (Windows/PowerShellの場合) で簡易動作確認します:
>>> from ctypes import * >>> l = cdll.LoadLibrary( 'lib/libmy_something.so' ) >>> l.my_norm.restype = c_double >>> l.my_norm( c_double( 1 ), c_double( 2 ), c_double( 3 ) ) 3.7416573867739413 >>> l.my_serialize( c_char_p( 'out'.encode( 'utf-8' ) ), c_double( l.my_norm( c_double(1), c_double(2), c_double(3) ) ) ) 0 (CTRL+D)
od -tx1z -Ax out または Format-Hex -Path out で out を確認
000000 cb 40 0d ee ea 11 68 3f 49 >.@....h?I< 000009
Notes:
- 3次元ベクター
{ 1, 2, 3 }のノルムは3.74165738677...くらいのスカラーです。 https://www.google.com/search?q=sqrt(1*1%2B2*2%2B3*3) - msgpack の double 値のシリアライズ結果は
0xcbで始まり IEEE754/Binary64 の Big-endian 形式になります。 https://github.com/msgpack/msgpack/blob/master/spec.md#float-format-family
conan
導入方法
- python と pip を使える状態にする
pip install conan
プロジェクトへのパッケージ追加方法
- パッケージを探す:
conan search "*eigen*" --remote=conan-centerconan search "*msgpack*" --remote=conan-center- コツ: 検索キーワードは小文字にし、ありそうな名称の前後に
*を付けてワイルドカード付きで検索すると見つけやすいです - 動作はわりともっさりですが、数秒待っていれば動きます
- パッケージを確認:
conan inspect eigen/3.3.7conan inspect msgpack/3.2.0@bincrafters/stable
- パッケージを使用可能な状態にする:
- プロジェクトに CMake プロジェクトファイルを用意して conan のパッケージを使用してビルド:
CMakeLists.txtを用意して conan 用の追記...include(${PROJECT_SOURCE_DIR}/conan.build/conanbuildinfo.cmake)conan_basic_setup()target_link_libraries( my_project ${CONAN_LIBS} )
mkdir build.cmake && pushd build.cmakecmake ..cmake --build .
(†1) ↓ conanfile.txt; パッケージ名は大文字小文字に敏感です
[requires] eigen/3.3.7 msgpack/3.2.0@bincrafters/stable [generators] cmake
パッケージの実体
~/.conanディレクトリー内にパッケージごとに実体がconan install ..で必要になる度に導入が確認され格納される- プロジェクト内にはパッケージはソース、バイナリー何れも配置されず、 cmake から↑が参照される
- パッケージの管理単位にバージョン情報なども内包しているので、複数プロジェクトでパッケージが混乱する事は無さそう(たぶん)
vcpkg
導入方法
git clone https://github.com/Microsoft/vcpkg.git- または
git subtree,git submoduleで取り込む
- または
pushd vcpkg- vcpkg の実行ファイルをビルド; 数分程度かかるかもしれません
- ビルドツールへの仕込み; しなくてもいいけど、すると便利かもしれないし、厄介事の原因になるかもしれないのでプロジェクトにあわせてどうぞ
- CMake:
cmake叩く段階で-Dすればいいのでこの段階では何もしなくてOK - Visual Studio:
./vcpkg integrate projectまたは./vcpkg integrate installhttps://docs.microsoft.com/en-us/cpp/build/vcpkg?view=vs-2019#integrate-with-visual-studio-windows - その他: 必要に応じて
vcpkg導入先のinstalled,packagesあたりを眺めれば手インテグレートできそう
- CMake:
popd
プロジェクトへのパッケージ追加方法
Note: vcpkg をどこに置くかによらずパスを通したり alias を張ったりする場合は vcpkg/vcpkg とパス付きで呼ぶ必要はないのですが、今回はプロジェクト単位でライブラリー管理時にしか使わないので、パス付きで呼んで済ませます。
- パッケージを探す:
vcpkg/vcpkg search eigen->eigen3 3.3.7-3 C++ template library for linear algebra: matrices, vectors, numerical solvers,...vcpkg/vcpkg search msgpack->msgpack 3.2.0-1 MessagePack is an efficient binary serialization format, which lets you exchan...
- バージョンの固定が必要なら
portsディレクトリーの中身を適当に編集して対処します- 不要な場合は何もせず↓へ
- パッケージを導入:
vcpkg/vcpkg install eigen3終了時に CMake 用の情報を教えてくれます(†1)vcpkg/vcpkg install msgpack終了時に CMake 用の情報を教えてくれます(†2)
CMakeLists.txtにライブラリーを追加; ↑で教えてくれる例をもとにプロジェクト名を main から実際の my_something へ変えて追加find_package( Eigen3 CONFIG REQUIRED )find_package( msgpack CONFIG REQUIRED )target_link_libraries( my_something PRIVATE Eigen3::Eigen msgpackc msgpackc-cxx msgpackc-static)
- ビルド:
mkdir buildcd buildcmake .. -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmakeCMake Error..."msgpackc-static" is imported but not globally visibleとか出たらvcpkg/installed/{x64-linuxなどのプラットフォーム名 }/share/msgpack/msgpack-targets.cmakeのadd_library(msgpackc-static... にGLOBALを追記する修正が一時的に必要かもしれません- https://github.com/microsoft/vcpkg/issues/7205
cmake --build .
(†1): ↓ ./vcpkg install eigen3 の終わりに表示される CMake ではこうしてね情報:
find_package(Eigen3 CONFIG REQUIRED) target_link_libraries(main PRIVATE Eigen3::Eigen)
(†2): ↓ ./vcpkg install msgpack の終わりに表示される CMake ではこうしてね情報:
find_package(msgpack CONFIG REQUIRED) target_link_libraries(main PRIVATE msgpackc msgpackc-cxx msgpackc-static)
パッケージの実体
vcpkg導入先のbuildtrees,installed,packagesにそれぞれソースやビルドされたライブラリーが放り込まれます- 良くも悪くも強引ですが、場所を変更する手段はいまのところは実質的に存在しないので、必要な場合は
vcpkgの導入自体を単位として扱います https://docs.microsoft.com/en-us/cpp/build/vcpkg?view=vs-2019#per-project
- 良くも悪くも強引ですが、場所を変更する手段はいまのところは実質的に存在しないので、必要な場合は
Hunter
導入方法
- Hunter をプロジェクト単位で使うための HUNTER_ROOT 用のディレクトリーを作成:
mkdir hunter_root(†2)
- HunterGate をプロジェクトへ仕込む https://cpp-pm-hunter.readthedocs.io/en/latest/quick-start.html
mkdir cmake- HunterGate.cmake をダウンロードして配置
- wget を使う場合:
wget https://raw.githubusercontent.com/hunter-packages/gate/master/cmake/HunterGate.cmake -O cmake/HunterGate.cmake - PowerShell を使う場合:
Invoke-WebRequest -Uri https://raw.githubusercontent.com/hunter-packages/gate/master/cmake/HunterGate.cmake -OutFile cmake/HunterGate.cmake
- wget を使う場合:
CMakeLists.txtのprojectよりも前に Hunter を仕込む... (†1)set( ENV{HUNTER_ROOT} "${CMAKE_CURRENT_SOURCE_DIR}/hunter_root" )(†2)include( "cmake/HunterGate.cmake" )HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.23.240.tar.gz" SHA1 "ca19f3769e6c80cfdd19d8b12ba5102c27b074e0" )- 基本的に最新リリースを確認して設定するといいはず: https://github.com/cpp-pm/hunter/releases
(†1): この仕様のため、 Hunter は add_subdirectory されるサブプロジェクト単位で使用したい場合に少し厄介です。サブディレクトリー単位で独立した CMake を呼ぶトリックを仕込むか、諦めてルートになるプロジェクトの project よりも前に Hunter を仕込むか、状況によっては悩ましい対応が必要になるかもしれません。
(†2): ~/.hunter_root や %HOMEPATH%/.hunter_root を用意して、 .bashrc や Windows の環境変数設定で HUNTER_ROOT をその場所へ設定しておくと conan のように合理的なパッケージ実体の管理方法になります。このメモの方法はプロジェクト単位の中へ Hunter のパッケージ管理と本体も押し込めたい場合の簡単なトリックの例です。
プロジェクトへのパッケージ追加方法
- パッケージを探す... https://cpp-pm-hunter.readthedocs.io/en/latest/packages.html
CMakeLists.txtにライブラリーを追加hunter_add_package( Eigen )hunter_add_package( msgpack )find_package( Eigen3 CONFIG REQUIRED )find_package( msgpack CONFIG REQUIRED )
- ビルド
mkdir build.cmake && pushd build.cmakecmake ..- Hunter のパッケージビルドがここで行われるので容赦なくかなりながい時間がここでかかります
- 妙に長いので🍵ブレイク後に見たら Eigen, msgpack のほか OpenSSL, ZLIB もフルビルドされていました...
- Hunter のパッケージビルドがここで行われるので容赦なくかなりながい時間がここでかかります
cmake --build .
パッケージの実体
- CMake から HunterGate が実行された際の
HUNTER_ROOT環境変数のディレクトリー内へ、ダウンロード、ビルドされる- メタ情報の管理も細かくされていて、パッケージバージョンが混乱したりする心配は無さそう
conanのようにパッケージの実体はプロジェクトを超えて合理的に共有したい場合は適当な共有用のディレクトリーを設け、HUNTER_ROOT環境変数をプロジェクトの外で設定すればOK
現時点での感想
- Windows と Ubuntu 程度の軽いクロスプラットフォーム対応性
- 現時点では
conan,vcpkg,Hunterどれでも問題になる事の方が少なさそうvcpkgのmsgpackでは手修正が必要な問題が発生しましたが、エラーメッセージを読めば対応できる程度なのでまあ…どうだろう…プロジェクトの開発者が初心者だらけだったら…厳しいかもしれませんね…
- 現時点では
- ライブラリーの対応の幅広さ、抜け目の無さ
- 現時点での一般論としては
vcpkgが他に比べて少し有能そう; OSGeo/PROJ, OpenMVG, curppp など conan,Hunterも一般性の高いパッケージに困る事はおおよそない; zlib, libjpeg, boost など
- 現時点での一般論としては
- パッケージ検索のし易さ
- プロジェクト単位での使いやすさ
conanがとても合理的; 実体はバージョンごと共有管理するけれど使用はプロジェクト単位で特定バージョンを引っ張れる- しばしば
conanは CMake の使い方が邪悪と言われるけれど、そもそも CMake のパッケージ管理のゆるふわ感を考えるとどうでもいいかな、むしろこういうまとめ方もユーザーには便利でいいかもーくらいにゆるっと思う程度です
- しばしば
vcpkgはプロジェクト単位での使用方法が極めて強引ですが、プロジェクトで採用する補助ツールをgit subtree/git submoduleなどで取り込みやすい場合には寧ろ好都合かもしれません- 強引なものの、結果的には、パッケージの実体と vcpkg そのものを完全にプロジェクト単位の中だけで固有に管理しやすいので、ポータビリティーを考えると悪くないかもしれません
Hunterは CMake 的に複雑なソリューションの中のサブプロジェクトとしては工夫しないと採用できない点が地味にめんどくさいかも;add_subdirectoryの先に配置したい場合に少し厄介- しばしば
conanとの比較で CMake が綺麗と言われているように、実際、キレイです。キレイです…が、conanを使った後だと CMake の記述量が少しですが多い点をだるく感じてしまうかもしれません - このメモに残したちょっとしたトリック程度で比較的簡単にパッケージの実体と
Hunterの本体をまるごとプロジェクト単位の中に埋められるので、ポータビリティー的にはvcpkgと同等の強さがあります
- しばしば
- 時間
conanはビルド済みのバイナリーも提供してくれるので一般的な用途では便利vcpkg,Hunterは基本的にソースからビルドなので重いライブラリーを引っ張るとビルドに時間がかかります