C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

C++ のパッケージマネージャーの選択メモ( conan vs. vcpkg vs. Hunter on Windows and Ubuntu ): C++ 実装がサブプロジェクトとして内包されるクロスプラットフォームアプリのリポジトリーの場合

タイトルが少しややこしいので最初に整理します。

  • このメモは: C++ のパッケージマネージャーの選択のはなし
  • ただし:
    • アプリはクロスプラットフォーム ( このメモでの具体例は Windows-10 & Ubuntu-19.04 )
    • アプリ全体(=このメモでは「ソリューション」とします)はいくつかの構成部品(=このメモでは「プロジェクト」とします)に分けて作られる
    • プロジェクトの1つ以上に C++ を採用したい
      • そのプロジェクト単位で C++ のライブラリーを管理できるパッケージマネージャーを導入したい → どうするのが楽そうかな

のメモです。

選択肢と大雑把な検討

  1. conan https://conan.io/
  2. vcpkg https://github.com/microsoft/vcpkg
    • MicrosoftWindows 向けの Visual Studio のために作ったパッケージマネージャー「だった」。今では GNU/LinuxOSX も対象プラットフォームという事になっている
  3. Hunter https://github.com/cpp-pm/hunter
  4. (最終手段) ライブラリーごとに手書きの CMake 魔術を作りプロジェクトの CMakeLists.txt へ仕込む https://cmake.org/cmake/help/latest/
    • これを選択すると保守の手間が増えるので 1, 2, 3 の何れかにしたい

対応パッケージの手広さ対決

登録パッケージ数を比べてもあんまり意味が無いので、実際に私がソフトウェア開発プロジェクトで触れる機会、可能性のあるライブラリーやフレームワークを適当に列挙して、実際にコマンドで search を叩く、最新のリストから grep しつつ、ついでにライセンスとオリジナルの入手情報も整理しました。パッケージ名に気を付けると良さそうな場合は有無の〇×の〇代わりにパッケージ名を記載しました。

要注意 この表は 2019年12月29日 の調査結果です。

categorynameLICENSEconanvcpkgHunteroriginal
3D-data, FormatAssimpBSD-3-Clause×https://github.com/assimp/assimp
All-purposeBoost 1.0Boost 1.0https://github.com/Boost 1.0org/Boost 1.0
Civil-engineeringOSGeo/PROJMIT×proj4PROJ4https://github.com/OSGeo/PROJ
Civil-engineering, Image, FormatOSGeo/libgeotiffpublic domain××https://github.com/OSGeo/libgeotiff
Compressionzlibzlib/libpnghttps://www.zlib.net/
CompressionzstdBSD | GPLv2×https://facebook.github.io/zstd/
Compressionliblzmapublic domainlzmahttps://tukaani.org/xz/
CompressionSnappyApache 2.0https://google.github.io/snappy/
CompressionLZ4BSD-2-Clausehttps://github.com/lz4/lz4
ConcurrencyOpenCLKhronos OpenCLhttps://github.com/KhronosGroup/OpenCL-Headers
ConcurrencyOpenCL-cppKhronos OpenCLkhronos-opencl-clhpp×https://github.com/KhronosGroup/libclcxx
ConcurrencyTBBApache 2.0×https://github.com/intel/tbb
CryptoCryptoPPBoost 1.0https://github.com/weidai11/cryptopp
DatabaseSQLite3public domainhttps://sqlite.org/src/timeline
DatabasesqlitecppMIT×https://github.com/SRombauts/SQLiteCpp
Image, Computer-visioningOpenCVBSD-3-Clausehttps://github.com/opencv/opencv
Image, Computer-visioningOpenMVGMPL 2.0××https://github.com/openMVG/openMVG
Image, Computer-visioningOpenMVSAGPL××https://github.com/cdcseacave/openMVS
Image, Font, Format(*)stbpublic domainhttps://github.com/nothings/stb
Image, FormatlibtiffBSD-liketifftiffhttp://www.simplesystems.org/libtiff/
Image, Format, JPEGlibjpegBSD-likejpeghttp://libjpeg.sourceforge.net/
Image, Format, PNGlibpngzlib/libpngpnghttps://libpng.sourceforge.io/
Image, Format, WebPWebPBSDhttps://developers.google.com/speed/webp/
Linear-algebra, MathEigenMPL 2.0http://eigen.tuxfamily.org/
NetworkingcurlMIThttps://github.com/curl/curl
NetworkingcurlppMIT××https://github.com/jpbarrette/curlpp
NetworkingBoringSSLOpenSSL××https://boringssl.googlesource.com/boringssl/
NetworkingOpenSSLApache-stylehttps://www.openssl.org/
NetworkingCrowBSD-3-Clause××https://github.com/ipkn/crow
Networking, RPC, gRPCgRPCApache 2.0×https://github.com/grpc/grpc/tree/master/src/cpp
OpenGLOpenGL RegistryKhronos OpenGL×opengl-registry×https://github.com/KhronosGroup/OpenGL-Registry
OpenGLGLFWzlib/libpngglfw3https://github.com/glfw/glfw
OpenGL, GUIimguiMIThttps://github.com/ocornut/imgui
OpenGL, MathglmHappy Bunnyhttps://glm.g-truc.net/
OpenGL, WindowsglewBSD-3-Clausehttps://github.com/nigels-com/glew
OpenGL, Windows, Direct3DANGLEBSD-3-Clause××http://angleproject.org/
Script, JavascriptV8BSD-3-Clause××https://v8.dev/
Script, LuaLuaMIT×https://github.com/lua/lua
SerializationcerealBSD-3-Clausehttps://github.com/USCiLab/cereal
Serialization, CSVcsv-parser-plusplusGPLv3××https://github.com/headupinclouds/csv-parser-cplusplus
Serialization, CSVfast-cpp-csv-parserBSD-3-Clause××https://github.com/ben-strasser/fast-cpp-csv-parser
Serialization, JSONRapidJSONBSD-3-Clausehttps://github.com/Tencent/rapidjson/
Serialization, JSON, BSONjsonMITnlohmann_jsonnlohmann-jsonnlohmann_jsonhttps://github.com/nlohmann/json
Serialization, MessagePackmsgpackBoost 1.0https://github.com/msgpack/msgpack-c
Serialization, XMLRapidXMLBoost 1.0http://rapidxml.sourceforge.net/
Serialization, XMLXercesApache 2.0×http://xerces.apache.org/xerces-c/
Unit-testCatchBoost 1.0https://github.com/catchorg/Catch2
Image, Computer-visioningpclBSD-3-Clause××https://github.com/PointCloudLibrary/pcl
Image, Computer-visioningOpenNI2Apache 2.0××https://github.com/occipital/openni2
Concurrency, GPGPUCUDAGPLv2 and etc.××https://developer.nvidia.com/cuda-zone

この調査結果からは、私の中では意外な事に vcpkg がとても有用そうな事がわかりました。少なくとも Windows で cl.exe 系のツールチェインを使う場合には vcpkg が期待動作しない事も少ないでしょうから、クロスプラットフォーム対応ではなく Windows だけを対象にする場合は vcpkg 一択で良さそうです。

vcpkg の懸念は Windows 以外でも期待動作してくれるのか…です。これは実際に試してみないと安心できません。

お試し

このメモでは...

  1. Eigen http://eigen.tuxfamily.org/
    • // header only
  2. 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 を作れた場合は、 pythonod (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 outout を確認

000000 cb 40 0d ee ea 11 68 3f 49                       >.@....h?I<
000009

Notes:

conan

導入方法

  1. python と pip を使える状態にする
  2. pip install conan

プロジェクトへのパッケージ追加方法

  1. パッケージを探す:
    1. conan search "*eigen*" --remote=conan-center
    2. conan search "*msgpack*" --remote=conan-center
    3. コツ: 検索キーワードは小文字にし、ありそうな名称の前後に * を付けてワイルドカード付きで検索すると見つけやすいです
    4. 動作はわりともっさりですが、数秒待っていれば動きます
  2. パッケージを確認:
    1. conan inspect eigen/3.3.7
    2. conan inspect msgpack/3.2.0@bincrafters/stable
  3. パッケージを使用可能な状態にする:
    1. プロジェクト内のパッケージの定義ファイルを作る: conanfile.txt (†1)
    2. パッケージのビルドディレクトリーを作って入る: mkdir build.conan && pushd build.conan
    3. 導入: conan install .. && popd; ..conanfile.txt のある場所
      • conanfile.txt へライブラリーを追加する事があれば conan install の再実行も必要
      • Windows, Ubuntu あたりの一般的なツールチェインはバイナリーが降ってくるのですぐ終わります
  4. プロジェクトに CMake プロジェクトファイルを用意して conan のパッケージを使用してビルド:
    1. CMakeLists.txt を用意して conan 用の追記...
      1. include(${PROJECT_SOURCE_DIR}/conan.build/conanbuildinfo.cmake)
      2. conan_basic_setup()
      3. target_link_libraries( my_project ${CONAN_LIBS} )
    2. mkdir build.cmake && pushd build.cmake
    3. cmake ..
    4. cmake --build .

(†1) ↓ conanfile.txt; パッケージ名は大文字小文字に敏感です

[requires]
eigen/3.3.7
msgpack/3.2.0@bincrafters/stable

[generators]
cmake

パッケージの実体

  • ~/.conan ディレクトリー内にパッケージごとに実体が conan install .. で必要になる度に導入が確認され格納される
    • プロジェクト内にはパッケージはソース、バイナリー何れも配置されず、 cmake から↑が参照される
    • パッケージの管理単位にバージョン情報なども内包しているので、複数プロジェクトでパッケージが混乱する事は無さそう(たぶん)

vcpkg

導入方法

  1. git clone https://github.com/Microsoft/vcpkg.git
    • または git subtree, git submodule で取り込む
  2. pushd vcpkg
  3. vcpkg の実行ファイルをビルド; 数分程度かかるかもしれません
    • GNU/Linux っぽい環境: ./bootstrap-vcpkg.sh
    • Windows っぽい環境: ./bootstrap-vcpkg.bat
  4. ビルドツールへの仕込み; しなくてもいいけど、すると便利かもしれないし、厄介事の原因になるかもしれないのでプロジェクトにあわせてどうぞ
  5. popd

プロジェクトへのパッケージ追加方法

Note: vcpkg をどこに置くかによらずパスを通したり alias を張ったりする場合は vcpkg/vcpkg とパス付きで呼ぶ必要はないのですが、今回はプロジェクト単位でライブラリー管理時にしか使わないので、パス付きで呼んで済ませます。

  1. パッケージを探す:
    1. vcpkg/vcpkg search eigen -> eigen3 3.3.7-3 C++ template library for linear algebra: matrices, vectors, numerical solvers,...
    2. vcpkg/vcpkg search msgpack -> msgpack 3.2.0-1 MessagePack is an efficient binary serialization format, which lets you exchan...
  2. バージョンの固定が必要なら ports ディレクトリーの中身を適当に編集して対処します
    • 不要な場合は何もせず↓へ
  3. パッケージを導入:
    1. vcpkg/vcpkg install eigen3 終了時に CMake 用の情報を教えてくれます(†1)
    2. vcpkg/vcpkg install msgpack 終了時に CMake 用の情報を教えてくれます(†2)
  4. CMakeLists.txt にライブラリーを追加; ↑で教えてくれる例をもとにプロジェクト名を main から実際の my_something へ変えて追加
    1. find_package( Eigen3 CONFIG REQUIRED )
    2. find_package( msgpack CONFIG REQUIRED )
    3. target_link_libraries( my_something PRIVATE Eigen3::Eigen msgpackc msgpackc-cxx msgpackc-static)
  5. ビルド:
    1. mkdir build
    2. cd build
    3. cmake .. -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake
      • CMake 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
    4. 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 にそれぞれソースやビルドされたライブラリーが放り込まれます

Hunter

導入方法

  1. Hunter をプロジェクト単位で使うための HUNTER_ROOT 用のディレクトリーを作成:
    • mkdir hunter_root (†2)
  2. HunterGate をプロジェクトへ仕込む https://cpp-pm-hunter.readthedocs.io/en/latest/quick-start.html
    1. mkdir cmake
    2. 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
  3. CMakeLists.txtproject よりも前に Hunter を仕込む... (†1)
    1. set( ENV{HUNTER_ROOT} "${CMAKE_CURRENT_SOURCE_DIR}/hunter_root" ) (†2)
    2. include( "cmake/HunterGate.cmake" )
    3. HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.23.240.tar.gz" SHA1 "ca19f3769e6c80cfdd19d8b12ba5102c27b074e0" )

(†1): この仕様のため、 Hunter は add_subdirectory されるサブプロジェクト単位で使用したい場合に少し厄介です。サブディレクトリー単位で独立した CMake を呼ぶトリックを仕込むか、諦めてルートになるプロジェクトの project よりも前に Hunter を仕込むか、状況によっては悩ましい対応が必要になるかもしれません。

(†2): ~/.hunter_root%HOMEPATH%/.hunter_root を用意して、 .bashrcWindows環境変数設定で HUNTER_ROOT をその場所へ設定しておくと conan のように合理的なパッケージ実体の管理方法になります。このメモの方法はプロジェクト単位の中へ Hunter のパッケージ管理と本体も押し込めたい場合の簡単なトリックの例です。

プロジェクトへのパッケージ追加方法

  1. パッケージを探す... https://cpp-pm-hunter.readthedocs.io/en/latest/packages.html
    1. Eigen https://cpp-pm-hunter.readthedocs.io/en/latest/packages/pkg/Eigen.html
    2. msgpack https://cpp-pm-hunter.readthedocs.io/en/latest/packages/pkg/msgpack.html
  2. CMakeLists.txt にライブラリーを追加
    1. hunter_add_package( Eigen )
    2. hunter_add_package( msgpack )
    3. find_package( Eigen3 CONFIG REQUIRED )
    4. find_package( msgpack CONFIG REQUIRED )
  3. ビルド
    1. mkdir build.cmake && pushd build.cmake
    2. cmake ..
      • Hunter のパッケージビルドがここで行われるので容赦なくかなりながい時間がここでかかります
        • 妙に長いので🍵ブレイク後に見たら Eigen, msgpack のほか OpenSSL, ZLIB もフルビルドされていました...
    3. cmake --build .

パッケージの実体

  • CMake から HunterGate が実行された際の HUNTER_ROOT 環境変数ディレクトリー内へ、ダウンロード、ビルドされる
    • メタ情報の管理も細かくされていて、パッケージバージョンが混乱したりする心配は無さそう
  • conan のようにパッケージの実体はプロジェクトを超えて合理的に共有したい場合は適当な共有用のディレクトリーを設け、 HUNTER_ROOT 環境変数をプロジェクトの外で設定すればOK

現時点での感想

  1. WindowsUbuntu 程度の軽いクロスプラットフォーム対応性
    • 現時点では conan, vcpkg, Hunter どれでも問題になる事の方が少なさそう
      • vcpkgmsgpack では手修正が必要な問題が発生しましたが、エラーメッセージを読めば対応できる程度なのでまあ…どうだろう…プロジェクトの開発者が初心者だらけだったら…厳しいかもしれませんね…
  2. ライブラリーの対応の幅広さ、抜け目の無さ
    1. 現時点での一般論としては vcpkg が他に比べて少し有能そう; OSGeo/PROJ, OpenMVG, curppp など
    2. conan, Hunter も一般性の高いパッケージに困る事はおおよそない; zlib, libjpeg, boost など
  3. パッケージ検索のし易さ
    1. vcpkg が理想的かもしれません。
      • vcpkg search の動作がわりと軽快
      • 導入先の ports ディレクトリーを眺めるだけでも探せる
    2. conan searchpip のようなもっさり感はあるものの(中身はpythonだしね...)、実用十分には探せる&耐えられる
    3. Hunter はコマンド式のインターフェースは無く、"読み物"から探すのは地味にだるいです...
  4. プロジェクト単位での使いやすさ
    1. conan がとても合理的; 実体はバージョンごと共有管理するけれど使用はプロジェクト単位で特定バージョンを引っ張れる
      • しばしば conan は CMake の使い方が邪悪と言われるけれど、そもそも CMake のパッケージ管理のゆるふわ感を考えるとどうでもいいかな、むしろこういうまとめ方もユーザーには便利でいいかもーくらいにゆるっと思う程度です
    2. vcpkg はプロジェクト単位での使用方法が極めて強引ですが、プロジェクトで採用する補助ツールを git subtree / git submodule などで取り込みやすい場合には寧ろ好都合かもしれません
      • 強引なものの、結果的には、パッケージの実体と vcpkg そのものを完全にプロジェクト単位の中だけで固有に管理しやすいので、ポータビリティーを考えると悪くないかもしれません
    3. Hunter は CMake 的に複雑なソリューションの中のサブプロジェクトとしては工夫しないと採用できない点が地味にめんどくさいかも; add_subdirectory の先に配置したい場合に少し厄介
      • しばしば conan との比較で CMake が綺麗と言われているように、実際、キレイです。キレイです…が、 conan を使った後だと CMake の記述量が少しですが多い点をだるく感じてしまうかもしれません
      • このメモに残したちょっとしたトリック程度で比較的簡単にパッケージの実体と Hunter の本体をまるごとプロジェクト単位の中に埋められるので、ポータビリティー的には vcpkg と同等の強さがあります
  5. 時間
    1. conan はビルド済みのバイナリーも提供してくれるので一般的な用途では便利
    2. vcpkg, Hunter は基本的にソースからビルドなので重いライブラリーを引っ張るとビルドに時間がかかります