---
title: "CMakeのFetchContentとは｜Declare・MakeAvailableの書き方とオプション早見表"
url: "https://www.issoh.co.jp/tech/details/6353/"
published: 2025-04-17
updated: 2026-10-17
categories: ["開発"]
publisher: "株式会社一創"
---

# CMakeのFetchContentとは｜Declare・MakeAvailableの書き方とオプション早見表

FetchContentは、CMakeのconfigure時に外部ライブラリのソースを取得して、そのままビルド対象へ組み込むための標準モジュールです。ここではFetchContent\_DeclareとFetchContent\_MakeAvailableの最小構成から、主要オプションの対応バージョン、Gitとアーカイブのどちらで取るか、GoogleTestの組み込み例、CMake 3.30で書き方が変わった箇所、CMake 4系で依存側が止まるケースまでを扱います。コードと出力は、2026年10月時点の最新安定版CMake 4.4.4（10月2日公開）で実際に動かしたものです。

## まとめ

結論から言うと、いま新規に書くなら`FetchContent_Declare()`で宣言し、`FetchContent_MakeAvailable()`で取り込む、この2つだけを使います。`FetchContent_GetProperties()`と`FetchContent_Populate(<name>)`を組み合わせる古いパターンは、CMake 3.30で非推奨になり、`cmake_minimum_required(VERSION 3.30)`以上を宣言したプロジェクトでは構成エラーで停止します。

押さえておきたい要点は次のとおりです。

- モジュール自体はCMake 3.11から標準搭載ですが、`FetchContent_MakeAvailable()`は3.14で追加されたため、最小要求バージョンは3.14以上にします。
- 同じ名前で複数回宣言した場合は最初の宣言が採用されます。
- `GIT_TAG`はコミットハッシュで固定するのが原則ですが、`GIT_SHALLOW`と併用する場合はタグ名かブランチ名しか使えません。
- 配布用のアーカイブがある依存は、`URL`と`URL_HASH`で取得できます。本文のgoogletest比較では、展開後のソースディレクトリ容量は完全なクローンの約4分の1でした。信頼できる配布元のハッシュを指定すれば、配布物の変更も検出できます。
- CMake 4系では、自分のプロジェクトが新しくても、取得した依存が設定するポリシーバージョンが3.5未満だとそこで停止します。CMake公式ドキュメントの例にあるgoogletestの指定をそのまま写すと、ちょうどこれに当たります。最小要求バージョンが3.5未満でも、VERSIONの範囲指定などでポリシーバージョンを3.5以上にしていれば、この互換性エラーにはなりません。

次の節では、FetchContentとExternalProjectの取得タイミングを比較します。

## FetchContentが担う役割とExternalProject・add\_subdirectoryとの違い

FetchContentはCMake 3.11で追加されたモジュールで、`include(FetchContent)`の1行で使えます。追加のインストールは不要で、仕様の一次情報は[CMake公式のFetchContentモジュールのドキュメント](https://cmake.org/cmake/help/latest/module/FetchContent.html)です。特徴は取得のタイミングにあり、ExternalProjectがビルド時にダウンロードするのに対し、FetchContentはconfigure時に取得を完了させます。configureの段階でソースが手元にあるため、そのまま`add_subdirectory()`で本体のビルドに組み込め、依存ライブラリが定義するCMakeターゲットへ`target_link_libraries()`できます。

| 方式                   | 取得タイミング    | ターゲットの参照 | ソースの管理                   |
| -------------------- | ---------- | -------- | ------------------------ |
| add\_subdirectory    | 取得しない      | できる      | 自分でvendoringまたはsubmodule |
| ExternalProject\_Add | ビルド時       | できない     | 自動                       |
| FetchContent         | configure時 | できる      | 自動                       |

ExternalProjectでターゲットを直接参照できないのは、`ExternalProject_Add()`が外部プロジェクトのCMakeターゲットを親側へ自動では定義しないからです。取得もビルドも自分のビルド時に走るため、configureの時点で参照できる相手がいません。逆に言うと、依存ライブラリを独立した構成・ビルド・インストールのサイクルで回したい場合はExternalProjectが向きます。FetchContentは内部でExternalProjectのダウンロード・更新・パッチの各ステップを使うため、`GIT_REPOSITORY`や`URL_HASH`といった取得系オプションの書き方はExternalProject\_Addと共通です。ただしconfigure・build・install・testに関わるオプションは明示的に無効化されており、渡しても捨てられます。

### FetchContentを選ばない方がよい場面

FetchContentは依存をソースのまま取り込むので、新しいビルドディレクトリを作るたびに依存も一から取得してコンパイルします。googletest v1.18.0の場合、手元（Intel Core i9-9880H・`-j1`）でのビルドはgmock込みで17.75秒、gmockを切って12.72秒かかりました。依存のビルド時間はライブラリごとに異なります。依存のコンパイルがCIの所要時間を圧迫している場合や、ビルド済みバイナリを社内で配りたい組織では、パッケージマネージャーで事前ビルドしたものを`find_package()`で使う方が速く確実です。

両者は排他ではありません。[CMake公式の依存関係ガイド](https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html)は、サードパーティのパッケージマネージャーを「依存プロバイダ」として`find_package()`とFetchContentの要求に割り込ませる仕組みを用意しており、その設定ファイルを`CMAKE_PROJECT_TOP_LEVEL_INCLUDES`で読み込ませる方法をパッケージマネージャー向けの推奨としています。プロジェクト側はFetchContentで宣言しておき、取得方法は使う人が選ぶ、という分担ができます。

## 最小構成：DeclareとMakeAvailableの2段構え

argparse（コマンドライン引数の解析ライブラリ）を取り込む、実際に動く最小のCMakeLists.txtは次の形です。同じディレクトリに後掲の`main.cpp`を置きます。宣言と取り込みを分けるのがこのモジュールの基本設計で、すべての依存を先に宣言してから、まとめて`FetchContent_MakeAvailable()`に渡します。

```
cmake_minimum_required(VERSION 3.14)
project(app CXX)

include(FetchContent)
FetchContent_Declare(
  argparse
  GIT_REPOSITORY https://github.com/p-ranav/argparse.git
  GIT_TAG        3eda91b2e1ce7d569f84ba295507c4cd8fd96910 # v3.2
)
FetchContent_MakeAvailable(argparse)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE argparse::argparse)
```

```
#include <argparse/argparse.hpp>
#include <iostream>

int main(int argc, char* argv[]) {
  argparse::ArgumentParser p("app");
  p.add_argument("name");
  p.parse_args(argc, argv);
  std::cout << "hello " << p.get<std::string>("name") << "\n";
}
```

`cmake -S . -B b`と`cmake --build b`のあと`b/app world`を実行すると「hello world」と表示されます。

取得したソースは既定で`${CMAKE_BINARY_DIR}/_deps`の下に置かれます。CMake 4.4.4で構成すると`_deps/argparse-src`にソース、`_deps/argparse-build`にビルドツリーが作られ、`FetchContent_MakeAvailable()`が返った時点で`argparse_SOURCE_DIR`と`argparse_BINARY_DIR`が呼び出し側のスコープに定義されます。この配置先を変えたい場合は`FETCHCONTENT_BASE_DIR`で指定しますが、Windowsのパス長制限に当たりやすくなるため、変えるならビルドツリー直下の短いパスに留めます。

### 最小要求バージョンの下限は3.11ではなく3.14

FetchContentモジュール自体は3.11で入りましたが、`FetchContent_MakeAvailable()`が追加されたのは3.14です。`cmake_minimum_required(VERSION 3.11)`と書いたまま`FetchContent_MakeAvailable()`を呼ぶプロジェクトを、CMake 3.11から3.13の環境で構成すると、モジュールのincludeは通るのにコマンドが見つからずに失敗します。手元の新しいCMakeでは通ってしまうため見落としやすい箇所です。使うコマンドに合わせて3.14以上、後述のポリシー挙動まで固定するなら3.30以上を宣言します。

### 同じ名前を2回宣言したときに採用される側

依存の詳細は「最初に記録したものが勝つ」方式で管理されます。プロジェクト階層のどこで宣言されたかは関係なく、同じ`<name>`に対する2回目以降の`FetchContent_Declare()`は無視されます。手元で、42を返す版のコミットハッシュと99を返す版のコミットハッシュを続けて宣言し、CMake 4.4.4でビルドして実行したところ、出力は42、つまり1つ目の宣言のハッシュがチェックアウトされていました。

```
FetchContent_Declare(mydep GIT_REPOSITORY ... GIT_TAG f4f3701...)  # これが採用される
FetchContent_Declare(mydep GIT_REPOSITORY ... GIT_TAG b1910e5...)  # 無視される
FetchContent_MakeAvailable(mydep)
```

この仕様は、親プロジェクトが子プロジェクトの依存バージョンを上書きするための仕組みです。裏を返すと、依存ライブラリが内部でFetchContentを使って別の依存を引き込んでいる場合、自分より先にその依存を宣言されると自分の指定が効きません。共有される可能性のある依存は、`FetchContent_MakeAvailable()`を1回でも呼ぶ前に全部宣言しておくのが安全です。

## FetchContent\_Declareの主要オプションと対応バージョン

宣言時に渡せるオプションは、ExternalProject\_Add由来の取得系オプションと、FetchContent固有のオプションに分かれます。固有のものは追加されたバージョンがばらついているため、対応表で確認してから使います。

| オプション                   | 追加バージョン | 効果                                   |
| ----------------------- | ------- | ------------------------------------ |
| SOURCE\_SUBDIR          | 3.18    | add\_subdirectory対象を配下の別ディレクトリにする    |
| FIND\_PACKAGE\_ARGS     | 3.24    | find\_packageを先に試し、引数を追加する           |
| OVERRIDE\_FIND\_PACKAGE | 3.24    | find\_package呼び出しをFetchContentへ迂回させる |
| SYSTEM                  | 3.25    | 追加されるサブディレクトリをシステム扱いにする              |
| EXCLUDE\_FROM\_ALL      | 3.28    | 依存のターゲットをALLから外す                     |

`SYSTEM`と`EXCLUDE_FROM_ALL`は実務での効きが大きいオプションです。`SYSTEM`を付けると、依存が公開するインクルードディレクトリが利用側のコンパイル時にシステムインクルード扱いになります。GCCやClangはシステムヘッダ由来の警告を抑えるため、依存側のヘッダから出る警告で自分のビルドログが埋まらなくなります。`EXCLUDE_FROM_ALL`は依存のターゲットをALLターゲットから外すため、テスト用ライブラリのサンプルやベンチマークまで毎回ビルドされる状況を避けられます。どちらも古いCMakeでは単に無視されるわけではなく未知の引数として扱われる可能性があるため、最小要求バージョンと揃えて使います。

なお`CMAKE_TLS_VERIFY`・`CMAKE_TLS_CAINFO`・`CMAKE_NETRC`・`CMAKE_NETRC_FILE`がFetchContentの取得オプションの既定値として効くようになったのは3.22からです。それ以前のバージョンではこれらの変数はFetchContentから無視されるため、社内プロキシやカスタムCAを使う環境では宣言側に直接オプションを書く必要があります。

## GIT\_TAGの指定とGIT\_SHALLOWを併用したときの制限

`GIT_TAG`にはブランチ名・タグ名・コミットハッシュのいずれも書けますが、公式ドキュメントはコミットハッシュを推奨しています。理由は2つあり、ローカルのクローンに該当コミットが既にある場合は再構成のたびの`git fetch`が不要になること、そして自分のプロジェクトの履歴が依存の特定時点に確実に紐づくことです。ブランチ名やタグ名は指す先が動くため、過去のコミットをチェックアウトしてもビルドが再現しません。指定を省略した場合の既定値は`master`で、リポジトリの既定ブランチ名ではない点にも注意します。

気をつけたいのは`GIT_SHALLOW`との併用です。[ExternalProjectモジュールのドキュメント](https://cmake.org/cmake/help/latest/module/ExternalProject.html)のGIT\_TAGの項は「If GIT\_SHALLOW is enabled then GIT\_TAG works only with branch names and tags. A commit hash is not allowed.」と明記しており、仕様としてコミットハッシュは指定できません。やっかいなのは、この禁止が常にエラーとして現れるわけではない点です。CMakeが生成するクローン用スクリプトを読むと、実行されるコマンドは次の形でした。

```
git clone --no-checkout --depth 1 --no-single-branch <url> <dir>
git checkout <GIT_TAGの値> --
```

`--depth 1 --no-single-branch`は、全ブランチと全タグの先端コミットだけを取ってきます。そのため、指定したハッシュがどれかのブランチかタグの先端を指している間はチェックアウトが通ってしまい、仕様に反していることに気づけません。CMake 4.4.4でargparseを対象に確かめると、タグv3.2とv3.1が指すハッシュはいずれも構成が成功し、どのタグにも紐づかない中間コミット（2024年5月30日）のハッシュを指定した場合だけ次のエラーで停止しました。

```
fatal: unable to read tree (e462ab980c0852bd1df2ee1f4ec81826246b6f21)
CMake Error at b/CMakeFiles/fc-tmp/argparse/argparse-gitclone.cmake:66 (message):
  Failed to checkout tag: 'e462ab980c0852bd1df2ee1f4ec81826246b6f21'
```

通っているように見えても、それは浅いクローンにたまたまそのコミットが含まれていただけで、サポートされた指定ではありません。上流がタグを付け替えれば同じ記述が翌日には落ちます。`GIT_SHALLOW TRUE`を使うならドキュメントどおりタグ名かブランチ名で書き、コミットハッシュで固定したいなら`GIT_SHALLOW`を外して完全なクローンを取ります。浅いクローンで参照が届かなくなる同種のつまずきは、CIのチェックアウト設定でも起きます。GitHub Actionsでの取得深さの扱いは[actions/checkoutの既定変更と浅いクローンの壊れ方](/tech/details/16927/)で整理しています。

### CIでクローンが一時的に落ちるときの再試行回数

CMake 4.4で、`git clone`の再試行を制御する`CMAKE_EP_GIT_CLONE_RETRY_COUNT`と`CMAKE_EP_GIT_CLONE_RETRY_DELAY`が追加されました（[CMake 4.4のリリースノート](https://cmake.org/cmake/help/latest/release/4.4.html)）。既定は再試行2回・待ち時間0秒で、最初の1回は数えないため合計3回まで試します。存在しないリポジトリを指定し、回数2・待ち1秒で構成すると、出力は次のようになりました。

```
-- Retry #1, waiting 1 seconds before next attempt...
-- Retry #2, waiting 1 seconds before next attempt...
Had to git clone more than once: 3 times.
CMake Error at b/CMakeFiles/fc-tmp/nope/nope-gitclone.cmake:55 (message):
  Failed to clone repository:
```

この2つはCMakeLists.txtに書く変数ではありません。ドキュメントは「This variable should not be set by a project, it is intended for the user to set.」と明記しており、ホスティング側のレート制限に当たりやすいCIでだけ`cmake -DCMAKE_EP_GIT_CLONE_RETRY_COUNT=4 -DCMAKE_EP_GIT_CLONE_RETRY_DELAY=10`のようにコマンドラインから渡します。待ち時間を0のまま回数だけ増やしても、レート制限には同じ拒否が返るだけです。

## URLでアーカイブを取得する書き方とURL\_HASHの検証

依存がリリースごとにソースのアーカイブを配布しているなら、Gitを経由せず`URL`で取る方が軽く済みます。googletest v1.18.0を3通りで取得し、configureの所要時間と`_deps/googletest-src`の大きさを比べました（同じマシンで1回ずつ計測）。

| 取得方法                       | configure時間 | ソースの大きさ | うち.git |
| -------------------------- | ----------- | ------- | ------ |
| GIT\_TAG（コミットハッシュ）         | 7.17秒       | 19MB    | 15MB   |
| GIT\_TAG（タグ）＋GIT\_SHALLOW  | 5.06秒       | 7.3MB   | 2.8MB  |
| URL＋URL\_HASH（リリースのtar.gz） | 3.97秒       | 4.5MB   | なし     |

時間は回線に左右されますが、大きさの差は履歴の有無で決まります。ハッシュで固定した完全なクローンは全履歴を持つため、ソース本体の3倍以上を`.git`が占めました。宣言は次の形で、googletestのREADMEも`URL`指定の例を載せています。

```
FetchContent_Declare(
  googletest
  URL      https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz
  URL_HASH SHA256=6e3191c1455468b3fc35a417fb565c1c5071aee1b7e7f85e30cf48a98d37d8b5
)
```

`URL_HASH`は省略できますが、付けないと配布物が差し替えられても気づけません。値はダウンロードしたファイルを`shasum -a 256`（Linuxは`sha256sum`）にかけて得ます。GitHubのリリースに添付されたファイルなら、`gh api repos/google/googletest/releases/tags/v1.18.0`が返す`digest`欄にも同じ値が載っていました。

GitHubがタグやコミットから自動生成する`archive/…zip`を使う場合は、URLの書き方で値が変わる点に注意します。同じv1.18.0のコミットでも、タグ名のURLとコミットハッシュのURLで取ったzipはSHA256が一致しませんでした。展開したときの最上位ディレクトリ名がURLごとに違うためです。リリースに添付ファイルがあるならそちらを使い、無ければ一度ダウンロードして値を確かめてから書きます。

### DOWNLOAD\_EXTRACT\_TIMESTAMPの警告（CMP0135）

`cmake_minimum_required()`が3.24未満のプロジェクトで`URL`を使うと、configure時に次の作者向け警告が出ます。

```
CMake Warning (author) at .../Modules/FetchContent.cmake:1386 (message):
  The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
  not set.  The policy's OLD behavior will be used.
```

原因は[ポリシーCMP0135](https://cmake.org/cmake/help/latest/policy/CMP0135.html)です。OLDでは展開したファイルのタイムスタンプがアーカイブ内の日時のまま残るため、URLを新しい版に変えても依存側のファイルが「古い」と判定され、再ビルドされないことがあります。最小要求バージョンを3.24以上にすればNEW（展開した時刻を付ける）になり警告は消えます。

最小要求バージョンを上げられない場合は宣言に`DOWNLOAD_EXTRACT_TIMESTAMP FALSE`を足しますが、ここに罠があります。警告文の続きは「specify the DOWNLOAD\_EXTRACT\_TIMESTAMP option with a value of true」と読めるのに、[ExternalProjectのドキュメント](https://cmake.org/cmake/help/latest/module/ExternalProject.html)はTRUEを「アーカイブ内の日時を保つ」、FALSEを「展開した時刻を付ける」と定め、特別な理由が無ければFALSEを使うよう勧めています。CMake 4.4.4で同じアーカイブを両方の値で展開すると、TRUEでは`CMakeLists.txt`の更新日時がアーカイブ内の2026年8月11日のまま、FALSEでは展開した当日の日時になりました。警告はどちらの値でも消えるので、警告文の言い回しに従ってTRUEを書くと、警告が消えたまま再ビルド漏れの原因だけが残ります。なおこのオプション自体が3.24で追加されたものなので、実行するCMakeが3.23以前の環境まで支えるなら、宣言を`if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)`で分けます。

### ハッシュ不一致の原因を確認する詳細ログ

`URL_HASH`の値を誤ると、CMake 4.4.4の既定の出力は次のメッセージだけで、ハッシュの話だと分かりません。しかも止まるまでに同じURLを6回ダウンロードしていました。

```
CMake Error at b/CMakeFiles/fc-stamp/googletest/download-googletest.cmake:163 (message):
  Each download failed!
```

理由は詳細ログにだけ出ます。`cmake -S . -B b --log-level=VERBOSE`で構成し直すと、期待値と実際の値が並びます。

```
-- SHA256 hash of
    .../b/_deps/googletest-tmp/googletest-1.18.0.tar.gz
  does not match expected value
    expected: '0000000000000000000000000000000000000000000000000000000000000000'
      actual: '6e3191c1455468b3fc35a417fb565c1c5071aee1b7e7f85e30cf48a98d37d8b5'
-- Hash mismatch, removing...
```

同じ宣言を`cmake_minimum_required(VERSION 3.14)`の下で構成すると、サブビルドを使う旧実装（CMP0168がOLD）になり、この不一致の行は既定の出力にも表示されました。表示が消えるのは3.30以上を宣言したプロジェクトです。そのままではネットワーク障害と見分けがつかないので、URLで取る依存が落ちたらまず`--log-level=VERBOSE`を付けて構成し直します。

## FetchContent\_MakeAvailableが内部で行う処理の順序

`FetchContent_MakeAvailable()`は「取得して`add_subdirectory()`する」だけのコマンドではありません。依存ごとに次の順序で判定が入ります。

まず`FETCHCONTENT_SOURCE_DIR_<大文字名>`が設定されていれば、以降の取得判定はすべて飛ばしてそのディレクトリを使います。設定されていない場合は、依存プロバイダが登録されていればそちらへ処理を委譲し、次に許可されていれば`find_package()`を試します。ここまでで解決しなければ宣言された詳細に従って取得し、最後にソースの最上位に`CMakeLists.txt`があれば`add_subdirectory()`で本体のビルドへ加えます。

最上位に`CMakeLists.txt`が無くてもエラーにはならず、単にサブディレクトリの追加が省略されます。これはヘッダやアセットを既知の場所に配置するだけの依存を扱うための挙動です。逆に、リポジトリの最上位ではなく配下のディレクトリを取り込みたい場合は`SOURCE_SUBDIR`に相対パスを指定します。存在しないディレクトリを指してもよく、これは最上位のCMakeLists.txtを意図的に取り込ませない目的にも使えます。

取得済みかどうかを自分で調べたい場合は`FetchContent_GetProperties()`で`<小文字名>_POPULATED`・`<小文字名>_SOURCE_DIR`・`<小文字名>_BINARY_DIR`を取り出せますが、`FetchContent_MakeAvailable()`を使っている限り必要になる場面はほとんどありません。

### 依存のビルドオプションを確実に切るCACHE INTERNAL指定

依存ライブラリのテストやサンプルまで毎回ビルドされるのを止めたいときは、`FetchContent_MakeAvailable()`より前に変数を設定します。このとき`CACHE INTERNAL`を付けるかどうかで結果が変わります。公式ドキュメントのprotobufの例も`set(protobuf_BUILD_TESTS OFF CACHE INTERNAL "")`と書いており、[ポリシーCMP0077](https://cmake.org/cmake/help/latest/policy/CMP0077.html)との衝突を避けるためと理由が添えられています。

```
set(mydep_BUILD_TESTS OFF CACHE INTERNAL "")   # 効く
# set(mydep_BUILD_TESTS OFF)                   # 依存側の option() に戻されることがある
FetchContent_MakeAvailable(mydep)
```

CMP0077は3.13で追加されたポリシーで、NEWでは`option()`が同名の通常変数を尊重して何もしません。ところがこのポリシーの値を決めるのは、親ではなく依存側の`cmake_minimum_required()`です。CMake 4.4.4で、`cmake_minimum_required(VERSION 3.10)`と`option(MYDEP_BUILD_TESTS "build tests" ON)`を持つ依存に対し親から`set(MYDEP_BUILD_TESTS OFF)`を渡したところ、依存側が表示した値はONに戻っていました。同じ依存に`CACHE INTERNAL`付きで渡すとOFFのまま通ります。依存が古いCMakeを要求している限り、通常の`set()`は当てになりません。オプション名は依存ごとに違うので、対象リポジトリのCMakeLists.txtを`option(`で検索して確かめます。

## FetchContent\_Populateの単一引数形式はCMake 3.30で非推奨

ネット上のサンプルで今も多く見かけるのが、次の書き方です。`FetchContent_MakeAvailable()`が存在しなかった3.13以前に使われていたパターンで、CMake 3.30でこの形の`FetchContent_Populate()`は非推奨になりました（[ポリシーCMP0169](https://cmake.org/cmake/help/latest/policy/CMP0169.html)）。

```
# 非推奨。いま書くべきではない
FetchContent_GetProperties(mydep)
if(NOT mydep_POPULATED)
  FetchContent_Populate(mydep)
  add_subdirectory(${mydep_SOURCE_DIR} ${mydep_BINARY_DIR})
endif()
```

厄介なのは、警告で済むかエラーで止まるかが`cmake_minimum_required()`の値で変わる点です。CMake 4.4.4で、最小要求バージョンだけを変えた同一のコードを構成すると、`cmake_minimum_required(VERSION 3.14)`では作者向け警告が出るだけでconfigureは成功し、`cmake_minimum_required(VERSION 3.30)`では次のエラーで停止しました。

```
CMake Error at .../Modules/FetchContent.cmake:1966 (message):
  Calling FetchContent_Populate(mydep) is deprecated, call
  FetchContent_MakeAvailable(mydep) instead.  Policy CMP0169 can be set to
  OLD to allow FetchContent_Populate(mydep) to be called directly for now,
  but the ability to call it with declared details will be removed completely
  in a future version.
```

つまり最小要求バージョンを3.30以上へ引き上げた瞬間にビルドが壊れます。置き換えは`FetchContent_MakeAvailable(mydep)`の1行で済むので、警告の段階で直しておきます。この旧パターンが廃止された理由は、`SYSTEM`や`EXCLUDE_FROM_ALL`のように宣言側で指定したオプションを、プロジェクト側で書く`add_subdirectory()`へ伝える手段が無いためです。依存プロバイダの仕組みにも対応できません。

なお`FetchContent_Populate()`自体が消えるわけではなく、名前に続けて取得方法まで書き下す形は残ります。ただしこちらはCMakeスクリプトモードで独自の仕組みを実装するための用途で、通常のプロジェクトで使うものではありません。

もう1点、3.30では実装方式も変わりました。ポリシーCMP0168がNEWのとき、FetchContentは別プロジェクトのサブビルドを経由せず、メインプロジェクトのconfigureから直接ダウンロードと更新を行います。手元で確認すると、`cmake_minimum_required(VERSION 3.14)`のときは`_deps`配下に`<name>-subbuild`が作られ、3.30では作られませんでした。この違いに伴い、NEW側では`QUIET`オプションと`FETCHCONTENT_QUIET`変数、`LOG_*`系オプション、`PREFIX`・`TMP_DIR`・`STAMP_DIR`・`LOG_DIR`・`DOWNLOAD_DIR`が無視されます。出力の抑制は`CMAKE_MESSAGE_LOG_LEVEL`やコマンドラインの`--log-level`で制御します。

## find\_packageを先に試して取得へフォールバックする構成

CIでは毎回ソースから取得したいが、開発者のマシンにライブラリが入っていればそれを使いたい、という要求には3.24で入った2つのオプションが対応します。

`FIND_PACKAGE_ARGS`を付けると、`FetchContent_MakeAvailable()`はまず`find_package()`を試し、見つからなければ宣言どおりに取得します。キーワードの後ろに書いた内容がそのまま`find_package()`の引数に足されるため、パッケージ名がリポジトリ名と違う場合に対応できます。他のオプションはすべて`FIND_PACKAGE_ARGS`より前に書く必要があります。

```
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        063de7e9578f82b369302001269680b4b1553359 # v1.18.0
  FIND_PACKAGE_ARGS NAMES GTest
)
FetchContent_MakeAvailable(googletest)
```

ここで`REQUIRED`を渡さないようにします。`find_package()`が必ず成功しなければならなくなり、見つからなかったときの取得へフォールバックできなくなるためです。プロジェクト側で書かずに利用者側で切り替えたい場合は、変数`FETCHCONTENT_TRY_FIND_PACKAGE_MODE`を使います。既定は`OPT_IN`で、`FIND_PACKAGE_ARGS`を書いた依存についてだけ`find_package()`を試します。`ALWAYS`にすると全依存で試し、`NEVER`にすると`FIND_PACKAGE_ARGS`があっても試しません。この変数は`FetchContent_Declare()`を呼んだ時点の値が使われる点が独特で、`FetchContent_MakeAvailable()`の直前で書き換えても効きません。

逆方向が`OVERRIDE_FIND_PACKAGE`です。これを付けて宣言すると、以降の`find_package(<name>)`がFetchContentでの取得に迂回されます。プロジェクト内の既存コードが`find_package()`で依存を探している場合に、その呼び出しを書き換えずにソースからのビルドへ寄せられます。`FIND_PACKAGE_ARGS`とは同時に指定できません。

## GoogleTestを取り込んでctestでテストを動かす例

テストフレームワークの取り込みは、FetchContentの利用例の一つです。テスト対象は次の`test.cpp`で、テストを2件持ちます。

```
#include <gtest/gtest.h>

int add(int a, int b) { return a + b; }

TEST(Add, Basic)    { EXPECT_EQ(add(2, 3), 5); }
TEST(Add, Negative) { EXPECT_EQ(add(-2, -3), -5); }
```

同じディレクトリに置き、取得からテスト実行まで通したCMakeLists.txtが次のものです。

```
cmake_minimum_required(VERSION 3.30)
project(t CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
FetchContent_Declare(
  googletest
  URL      https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz
  URL_HASH SHA256=6e3191c1455468b3fc35a417fb565c1c5071aee1b7e7f85e30cf48a98d37d8b5
)
set(BUILD_GMOCK   OFF CACHE INTERNAL "")
set(INSTALL_GTEST OFF CACHE INTERNAL "")
# Windows（MSVC）でランタイムの不一致を避ける。googletestのREADMEの指定
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()
add_executable(t test.cpp)
target_link_libraries(t PRIVATE GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(t)
```

`cmake -S . -B b`、`cmake --build b`のあと`b`で`ctest`を実行すると、`gtest_discover_tests()`がテストケースを1件ずつ登録しているため「100% tests passed out of 2」と表示されました。

各行には理由があります。[googletestのREADME](https://github.com/google/googletest/blob/main/googletest/README.md)はビルドにC++17対応の環境を求めているため、`CMAKE_CXX_STANDARD`を17にします。`BUILD_GMOCK`は既定でONなので、gMockを使わないなら切ります。ビルド時間は17.75秒から12.72秒に縮みました。`gtest_force_shared_crt`は、Visual Studioの新規プロジェクトがCランタイムを動的にリンクするのに対しgoogletestは静的にリンクするため起きる`LNK2038`エラーを避ける指定です。macOSやLinuxでは影響しません。

### 古いサンプルのgoogletest指定がCMake 4系で止まる理由

見落としやすいのは、CMake公式のFetchContentドキュメント自体が、2026年10月時点の最新版（latest）でもgoogletestを`GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0`で宣言する例を載せている点です。このコミットは2019年のrelease-1.10.0で、最上位のCMakeLists.txtが`cmake_minimum_required(VERSION 2.8.8)`を宣言しています。CMake 4.4.4でそのまま構成すると、後半の「CMake 4系で依存側のconfigureが止まる場合の切り分け」で扱う互換性削除に当たって停止しました。

```
HEAD is now at 703bd9ca Googletest export
CMake Error at b/_deps/googletest-src/CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 3.5 has been removed from CMake.
```

googletestはv1.18.0（2026年8月10日公開）で`cmake_minimum_required(VERSION 3.16)`まで上がっているので、写してきたハッシュはリリースタグの値に置き換えます。

## 通信を止める3つの変数の使い分けとローカルソースへの差し替え

オフライン環境やCIのキャッシュ運用では、取得そのものを抑えたい場面があります。制御用の変数は用途が微妙に違うので、使い分けを整理します。

| 変数                                  | 既定  | 抑制する範囲             |
| ----------------------------------- | --- | ------------------ |
| FETCHCONTENT\_SOURCE\_DIR\_<大文字名>   | 未設定 | その依存の取得と更新をすべて省略   |
| FETCHCONTENT\_UPDATES\_DISCONNECTED | OFF | git・hgの更新ステップの通信のみ |
| FETCHCONTENT\_FULLY\_DISCONNECTED   | OFF | ダウンロードと更新の全体       |

開発中の依存を手元で書き換えながら試したいときは`FETCHCONTENT_SOURCE_DIR_<大文字名>`が使えます。名前部分は大文字にする点に注意してください。手元で、`GIT_REPOSITORY`に到達できないURLを書いた状態で`cmake -S . -B b -DFETCHCONTENT_SOURCE_DIR_MYDEP=/tmp/fctest/dep`を実行したところ、通信を一切試みずに構成が成功し、`mydep_SOURCE_DIR`は指定したディレクトリを指しました。取得先の既定位置にあるソースを直接編集すると、宣言内容が変わったタイミングで消えることがあるため、依存に手を入れるならこの変数を使うのが正解です。

`FETCHCONTENT_UPDATES_DISCONNECTED`はより穏やかな制御で、gitとhgの更新ステップだけリモートへ接続しなくなります。ダウンロードステップは対象外なので、未取得の依存は通常どおり取得されます。ローカルに既にあるタグやコミットへの切り替えは成功し、手元に無いハッシュへの切り替えは失敗します。依存ごとに切り分けたい場合は`FETCHCONTENT_UPDATES_DISCONNECTED_<大文字名>`を使い、めったに変わらない依存だけ更新を止める運用ができます。

`FETCHCONTENT_FULLY_DISCONNECTED`はダウンロードも更新も行いません。これは初回構成でネットワークを遮断するための変数ではなく、一度取得が済んだ後にconfigureを速くするためのものです。CMake 3.30で追加されたポリシーCMP0170がNEW（3.30以上を宣言したプロジェクト）の場合、この変数がTRUEなのにソースディレクトリが存在しなければ致命的エラーで停止します。CMP0170が未設定の場合は警告を出してOLDの動作を使います。以前は取得が黙って飛ばされて原因の追いにくいエラーになっていたため、挙動としては改善です。初回からネットワークを遮断したい場合は、依存プロバイダを設定してローカルの内容から解決させます。

### 宣言の段階でローカルディレクトリを依存にする書き方

社内の別リポジトリを決まった場所にチェックアウトしておき、それを常に使いたいなら、利用者ごとの上書きではなく宣言そのものにローカルのパスを書けます。取得方法を書かずに`SOURCE_DIR`だけを渡す形です。

```
FetchContent_Declare(mydep SOURCE_DIR ${CMAKE_SOURCE_DIR}/../mydep)
FetchContent_MakeAvailable(mydep)
```

CMake 4.4.4で試すと、通信は行われず指定したディレクトリがそのまま`add_subdirectory()`され、`_deps`には`mydep-build`と`mydep-tmp`だけが作られました。ExternalProjectのドキュメントも、取得方法が無い場合の`SOURCE_DIR`は展開済みの既存ディレクトリを指すものと定めています。

逆に、`GIT_REPOSITORY`や`URL`と`SOURCE_DIR`を一緒に書くのは危険です。同じドキュメントは、URL方式以外では「All other download methods silently discard any previous contents of the source directory.」としており、手元で編集中のディレクトリを`SOURCE_DIR`に指定すると、取得のたびに中身が警告なしで消されます。編集中のソースを差し込むなら、前述の`FETCHCONTENT_SOURCE_DIR_<大文字名>`を使います。

CMakeを組み込み開発のツールチェーンと組み合わせて使う場合の環境構築は、[STM32CubeIDE for VS Code拡張でのビルドとデバッグ](/tech/details/6859/)にまとめています。

## CMake 4系で依存側のconfigureが止まる場合の切り分け

2025年3月27日に公開されたCMake 4.0で、3.5より前との互換性が削除されました。[CMake 4.0のリリースノート](https://cmake.org/cmake/help/latest/release/4.0.html)の記述は「Compatibility with versions of CMake older than 3.5 has been removed. Calls to cmake\_minimum\_required() or cmake\_policy() that set the policy version to an older value now issue an error.」です。ここで効いてくるのが、自分のプロジェクトを新しく書いていても、FetchContentが取ってくる依存のCMakeLists.txtが古ければそちらで止まるという点です。FetchContentは依存のソースをそのまま`add_subdirectory()`するため、依存側の`cmake_minimum_required()`がそのまま自分のconfigureに影響します。

手元で`cmake_minimum_required(VERSION 3.0)`を宣言した依存をFetchContentで取り込み、CMake 4.4.4で構成すると次のエラーで停止しました。指されているのが`_deps/mydep-src/CMakeLists.txt`、つまり自分が書いていないファイルなので、原因の見当がつきにくいのが厄介なところです。

```
CMake Error at b/_deps/mydep-src/CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 has been removed from CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.

  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try configuring anyway.
```

逃げ道はエラーメッセージが示すとおり`CMAKE_POLICY_VERSION_MINIMUM`です。CMake 4.0で追加された変数で、対象プロジェクトのコードに触れずに最低ポリシーバージョンを外から与えます。`-DCMAKE_POLICY_VERSION_MINIMUM=3.5`でキャッシュに入れる方法、同名の環境変数で新しいビルドツリーに自動で効かせる方法、そしてプロジェクト側で`FetchContent_MakeAvailable()`の直前に通常変数として設定する方法があり、CMake 4.4.4ではいずれもconfigureが通りました。依存1つに絞って当てるなら3つ目です。前章のgoogletest release-1.10.0にこの指定を当てると、`cmake_minimum_required`は非推奨の警告に変わって通過しました。ただし1.10.0には`GTest::gtest_main`という名前のターゲットが無く（別名ターゲットは1.11.0で追加）、今度はリンク先が見つからずに止まります。古い依存を延命させると別の箇所で詰まるので、先に版を上げるのが筋です。

```
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)   # この依存のためだけに効かせる
FetchContent_MakeAvailable(mydep)
unset(CMAKE_POLICY_VERSION_MINIMUM)
```

ただしこれを恒久策にすべきではありません。[CMAKE\_POLICY\_VERSION\_MINIMUMの公式ドキュメント](https://cmake.org/cmake/help/latest/variable/CMAKE%5FPOLICY%5FVERSION%5FMINIMUM.html)はこの変数を「まだ更新されていないプロジェクトを外から構成してみるため」のものと位置づけ、プロジェクトが自分のポリシーバージョンを設定する目的で書くことは明確に否定しています。それには`cmake_minimum_required(VERSION)`を使えという指示です。実務での順番は、依存の`GIT_TAG`をCMake 4系に対応した版へ上げるのが先、上げられない場合だけ上のように依存ごとに限定して当て、あわせて上流へ報告します。環境変数でCI全体に効かせる運用は、別の依存が抱える同種の問題まで見えなくするため避けます。

## よくある質問

### FetchContentとExternalProjectはどちらを使えばよいですか？

依存ライブラリのCMakeターゲットに`target_link_libraries()`でリンクしたいならFetchContentです。ExternalProjectはビルド時にダウンロードするため、configure時点では依存のターゲットが存在せず参照できません。依存を独立して構成・ビルド・インストールしたい場合や、CMakeプロジェクトではないものをビルドさせたい場合はExternalProjectが向きます。

### FetchContent\_MakeAvailableとadd\_subdirectoryは何が違いますか？

`FetchContent_MakeAvailable()`は取得を済ませたうえで、内部的に`add_subdirectory()`を呼びます。`add_subdirectory()`単体はソースが既に手元にある前提で、取得は行いません。またソースの最上位に`CMakeLists.txt`が無い場合、`FetchContent_MakeAvailable()`はサブディレクトリの追加を省略するだけでエラーにはなりませんが、`add_subdirectory()`を直接呼ぶとエラーになります。

### FetchContent\_Populateはもう使えないのですか？

宣言済みの名前だけを渡す`FetchContent_Populate(<name>)`の形が非推奨です。CMake 3.30以降を`cmake_minimum_required()`で宣言しているとエラーで停止し、それより前のバージョンを宣言している場合は警告が出ます。名前に続けて`GIT_REPOSITORY`などの取得方法まで書き下す形は残っていますが、CMakeスクリプトモードでの利用が想定された用途です。

### 取得したソースはどこに置かれますか？

既定では`${CMAKE_BINARY_DIR}/_deps`の下に`<小文字名>-src`と`<小文字名>-build`が作られます。CMake 3.29以前の実装、またはポリシーCMP0168がOLDのときは`<小文字名>-subbuild`も作られます。配置先は`FETCHCONTENT_BASE_DIR`で変更できますが、Windowsのパス長制限に当たりやすいため短いパスにします。

### GIT\_TAGを省略するとどうなりますか？

既定値は`master`です。リポジトリの既定ブランチ名が使われるわけではないため、既定ブランチが`main`で、masterという参照が存在しないリポジトリでは取得に失敗します。再現性の面からも、リリースタグが指すコミットハッシュを明示するのが安全です。

## 関連記事

- [actions/checkoutとは｜v7の既定変更・主要入力と浅いクローンの壊れ方](/tech/details/16927/)
- [STM32をVS Codeで開発する｜STM32CubeIDE for VS Code 3.11の導入・ビルド・デバッグ【2026年10月】](/tech/details/6859/)
- [ROS 2の環境構築｜Ubuntu 22.04へのHumbleインストールとcolconビルド手順【2026年8月時点】](/tech/details/16826/)
- [Siv3Dとは？無料C++フレームワークのインストールと使い方【v0.6.16対応】](/tech/details/9701/)

---

出典: [CMakeのFetchContentとは｜Declare・MakeAvailableの書き方とオプション早見表](<https://www.issoh.co.jp/tech/details/6353/>)（株式会社一創）
