CMakeでFetchContentとfind_packageを使い分ける目安

CMakeで外部ライブラリを使うとき、find_package() で探すのか、FetchContent で取ってくるのか迷うことがあります。

どちらが常に正しいというより、依存関係をどこで管理したいかで分けるのが分かりやすいです。

  • すでに環境に入っているライブラリを使う -> find_package()
  • プロジェクト側で取得元やバージョンを固定したい -> FetchContent

公式ドキュメントでも、FetchContentfind_package() の関係は別々の仕組みとして扱われています。

https://cmake.org/cmake/help/latest/module/FetchContent.html

https://cmake.org/cmake/help/latest/command/find_package.html

find_packageを使う場合

find_package() は、インストール済みのパッケージを探すときに使います。

find_package(ZLIB REQUIRED)

add_executable(my_app
  src/main.cpp
)

target_link_libraries(my_app
  PRIVATE
    ZLIB::ZLIB
)

この形は、OSのパッケージマネージャやvcpkg、Conan、SDKなどでライブラリを用意する場合に向いています。

プロジェクト側は「ZLIBが必要」とだけ書き、どこから持ってくるかは環境に任せます。

FetchContentを使う場合

FetchContent は、configure時に外部プロジェクトを取得して、メインのビルドに組み込むための仕組みです。

公式ドキュメントでは、FetchContent_Declare() で取得内容を定義し、FetchContent_MakeAvailable() で使える状態にする流れになっています。

https://cmake.org/cmake/help/latest/module/FetchContent.html

include(FetchContent)

FetchContent_Declare(
  fmt
  GIT_REPOSITORY https://github.com/fmtlib/fmt.git
  GIT_TAG        10.2.1
)

FetchContent_MakeAvailable(fmt)

add_executable(my_app
  src/main.cpp
)

target_link_libraries(my_app
  PRIVATE
    fmt::fmt
)

この形は、依存ライブラリの取得元やバージョンをプロジェクト内で固定したい場合に向いています。

ただし、configure時にネットワークアクセスが必要になる点は見ておく必要があります。

使い分けの目安

状況 使うもの
OSやSDKに入っているライブラリを使う find_package()
CIや開発環境で同じバージョンを使いたい FetchContent
クロスコンパイルでsysroot内のライブラリを使う find_package()
小さな依存をプロジェクトに固定したい FetchContent
パッケージ管理ツールに任せたい find_package()

クロスコンパイルでは、基本的にターゲット用のライブラリをsysrootやSDKから探すことが多いため、find_package() の方が自然な場面が多いです。

一方で、ヘッダーオンリーに近いライブラリや、ターゲットごとの差が少ない依存なら FetchContent でも扱いやすいことがあります。

クロスコンパイル時に find_package() がホスト側を見に行く場合は、CMakeのクロスコンパイルでfind_packageがホストを見に行くときの設定 で探索先の設定を整理しています。

FetchContentで気をつけること

GIT_TAG には、ブランチ名よりもタグやコミットハッシュを指定します。

FetchContent_Declare(
  fmt
  GIT_REPOSITORY https://github.com/fmtlib/fmt.git
  GIT_TAG        10.2.1
)

公式ドキュメントでも、外部から取得する内容について、ブランチ名よりハッシュの方が安全で期待した内容か確認しやすいという説明があります。

https://cmake.org/cmake/help/latest/module/FetchContent.html

ブランチ名を指定すると、同じCMake設定でも日によって取得される内容が変わる可能性があります。

find_packageとFetchContentを混ぜる場合

CMake 3.24以降では、FetchContent_Declare()FIND_PACKAGE_ARGS などのオプションもあります。

FetchContent_Declare(
  fmt
  GIT_REPOSITORY https://github.com/fmtlib/fmt.git
  GIT_TAG        10.2.1
  FIND_PACKAGE_ARGS
)

ただ、最初からここまで使うと分かりにくくなります。

まずは次のどちらかに寄せる方が読みやすいです。

  • 環境にあるものを使うなら find_package()
  • プロジェクトで取ってくるなら FetchContent

依存関係が増えてきたら、vcpkgやConanなどのパッケージ管理も含めて見直す形になります。

まとめ

外部ライブラリの扱いは、管理したい場所で分けます。

  • 環境やSDKに任せるなら find_package()
  • プロジェクト内で取得元とバージョンを固定するなら FetchContent
  • クロスコンパイルではターゲット環境のライブラリを探すため、find_package() を使う場面が多い
  • FetchContent を使うなら、GIT_TAG は固定する

最初は混ぜすぎず、どちらで依存関係を管理するのかを明確にしておくのが扱いやすいです。

参考