Copilot CLIでC++プロジェクトを生成すると、vcpkg.jsonに必要なライブラリは追加されても、依存関係の基準版が固定されていないことがあります。そのままでは、別のPCや数か月後のCI環境で復元した際に、異なるバージョンやポート定義が選ばれ、コンパイルエラーやABI不整合が発生する可能性があります。
対策の中心は、vcpkgマニフェストモードのvcpkg.jsonにbuiltin-baselineを設定し、使用するvcpkgリポジトリのコミットを固定することです。さらに、更新時はベースラインを自動追従させず、専用ブランチで明示的に進め、クリーンビルドとテストで影響を確認します。
MicrosoftのC++ Team Blogでも、Copilot CLIで生成したC++プロジェクトにbuiltin-baselineを追加し、依存関係を再現可能にしたうえで、将来の更新を管理する方法が案内されています。(Microsoft for Developers)
Copilot生成プロジェクトではbuiltin-baselineを必ず確認する
Copilot CLIが次のようなvcpkg.jsonを生成したとします。
{
"name": "quotecli",
"version-string": "0.1.0",
"dependencies": [
"nlohmann-json",
"fmt",
"rang"
]
}
この状態でもvcpkgによるインストールとビルドは可能です。しかし、どの時点のvcpkgレジストリを基準にバージョンを解決するかが、マニフェスト内に記録されていません。
レジストリ設定もbuiltin-baselineもない場合、vcpkgはマニフェストのバージョン管理情報を使わず、クラシックモード相当の方法で依存関係を処理します。そのため、PCごとに異なるvcpkgのチェックアウトを使用していると、同じvcpkg.jsonでも異なるパッケージが選ばれる可能性があります。(Microsoft Learn)
builtin-baselineを加えると、次のようになります。
{
"name": "quotecli",
"version-string": "0.1.0",
"builtin-baseline": "cd61e1e26a038e82d6550a3ebbe0fbbfe7da78e3",
"dependencies": [
"nlohmann-json",
"fmt",
"rang"
]
}
上記のコミット値はMicrosoftのデモで使用された例です。新しいプロジェクトへそのままコピーするのではなく、実際にチームが採用するvcpkgのリリースタグまたはレビュー済みコミットから取得してください。(Microsoft for Developers)
builtin-baselineが固定するもの
builtin-baselineには、Microsoftのvcpkgリポジトリに存在するコミットSHAを指定します。そのコミットに記録されたversions/baseline.jsonが、依存関係全体のバージョン解決に使われます。
ただし、builtin-baselineは単純な「全パッケージの完全固定リスト」ではありません。正確には、依存グラフ全体に対するバージョンの基準、つまり最低ラインを設定します。ほかの制約がなければベースラインのバージョンが選ばれますが、直接依存または推移的依存にversion>=がある場合は、それを満たすバージョンまで引き上げられることがあります。vcpkgは、適用されるすべての条件を満たす最小バージョンを選択します。(Microsoft Learn)
| 設定 | 動作 | 主な用途 |
|---|---|---|
builtin-baseline | レジストリ全体の基準版を設定 | プロジェクト全体の再現性確保 |
version>= | 指定パッケージの最低バージョンを設定 | 必要なAPIや不具合修正版の要求 |
overrides | 他の制約を無視して特定バージョンを強制 | 互換性問題や一時的な競合回避 |
| 設定なし | 使用中のvcpkg状態に依存しやすい | 再現性が不要な一時検証 |
通常のアプリケーション開発では、まずbuiltin-baselineを設定します。個別のライブラリに最低バージョン要件がある場合だけversion>=を追加し、overridesは例外的な回避策として扱うのが安全です。
最初にbuiltin-baselineを固定する手順
vcpkgの取得方法を確認する
最初に、プロジェクトがどのvcpkgを利用しているか確認します。
代表的な構成は次の2つです。
- プロジェクト内の
vcpkgフォルダーまたはGitサブモジュールを使用する - PCやCIに共通インストールされた
VCPKG_ROOTを使用する
Copilotがリポジトリ内にvcpkgを配置した場合は、次のような構成になっていることがあります。
project-root
├─ src
├─ vcpkg
├─ CMakeLists.txt
├─ CMakePresets.json
└─ vcpkg.json
再現性を重視するなら、vcpkg本体もGitサブモジュールなどでコミット固定する方法が分かりやすいでしょう。builtin-baselineだけでなく、vcpkg実行ファイルやツールチェーンの世代もチーム内でそろえやすくなります。
採用するvcpkgコミットを決める
リポジトリ内のvcpkgを使う場合は、最新のmasterへ無条件で追従させるのではなく、採用するリリースタグまたはコミットを明示します。
Windowsでは、たとえば次のように操作します。
git -C vcpkg fetch --tags
git -C vcpkg checkout <採用するリリースタグまたはコミット>
git -C vcpkg rev-parse HEAD
.\vcpkg\bootstrap-vcpkg.bat
git rev-parse HEADで表示されたコミットが、現在使用しているvcpkgリポジトリの位置です。
ここで重要なのは、「最新だから採用する」のではなく、チームが検証対象として選んだ状態を採用することです。
dry-runで追加内容を確認する
初回のbuiltin-baseline追加には、次のコマンドを使用できます。
.\vcpkg\vcpkg.exe x-update-baseline --add-initial-baseline --dry-run
--dry-runは変更予定を表示するだけで、ファイルを書き換えません。問題がなければ、実際に追加します。
.\vcpkg\vcpkg.exe x-update-baseline --add-initial-baseline
共通インストールされたvcpkgを使っている場合は、プロジェクトルートで次のように実行します。
vcpkg x-update-baseline --add-initial-baseline
x-update-baselineは、使用中のvcpkgインスタンスの現在のGitコミットをbuiltin-baselineへ設定します。したがって、コマンド実行前にvcpkgを意図したタグまたはコミットへ切り替えておく必要があります。(Microsoft Learn)
なお、公式ドキュメントではx-update-baselineは実験的機能として扱われています。将来、コマンド名や動作が変わる可能性があるため、利用中のvcpkgでvcpkg help x-update-baselineも確認してください。(Microsoft Learn)
vcpkg.jsonの差分を確認する
コマンド実行後は、必ずGit差分を確認します。
git diff -- vcpkg.json
想定される変更は、基本的に次の1行です。
+ "builtin-baseline": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
Gitサブモジュールとしてvcpkgを管理している場合は、サブモジュールの参照先も確認します。
git diff --submodule
次の2つは同じコミットまたはプルリクエストで管理するのが安全です。
vcpkg.jsonのbuiltin-baseline- vcpkgサブモジュールまたは取得スクリプトのバージョン
片方だけ更新すると、「マニフェストが指すレジストリ」と「実際に動かしているvcpkgツール」がずれ、トラブル時の原因調査が難しくなります。
固定後はクリーン環境で復元を確認する
builtin-baselineを追加しても、既存のbuildやvcpkg_installedが残っている状態で増分ビルドしただけでは、再現性を十分に確認できません。
最初の検証では、既存の生成物を削除します。
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force vcpkg_installed -ErrorAction SilentlyContinue
続いて、インストール予定を確認します。
.\vcpkg\vcpkg.exe install --dry-run
マニフェストモードのvcpkg installは、vcpkg.jsonに記載された状態へインストールディレクトリを合わせます。--dry-runを付けると、インストール、削除、再ビルドされるパッケージと機能を、実際に変更せず確認できます。(Microsoft Learn)
CMakeプロジェクトでは、ツールチェーンファイルを指定して構成します。
cmake -S . -B build `
-DCMAKE_TOOLCHAIN_FILE="$PWD\vcpkg\scripts\buildsystems\vcpkg.cmake"
cmake --build build --config Debug
ctest --test-dir build -C Debug --output-on-failure
CMakePresets.jsonが用意されている場合は、プロジェクトで定義されたプリセットを使います。
cmake --preset msvc-debug
cmake --build --preset build-debug
vcpkgのCMakeツールチェーンを利用すると、CMakeの構成処理中にマニフェストの依存関係が自動的にインストールされます。マニフェストモードではプロジェクトごとに独立したvcpkg_installedを持てるため、別プロジェクトのパッケージと混在しにくい点も利点です。(Microsoft Learn)
別PCとCIでそろえるべき項目
builtin-baselineは依存パッケージのバージョン解決を安定させますが、C++ビルド環境全体を完全に固定するものではありません。
少なくとも、次の項目も管理してください。
| 項目 | 固定または記録する内容 |
|---|---|
| vcpkg | Gitコミット、リリースタグ、サブモジュール参照 |
| マニフェスト | vcpkg.jsonとvcpkg-configuration.json |
| トリプレット | x64-windows、x64-windows-staticなど |
| コンパイラ | MSVC、Clang、GCCのバージョン |
| SDK | Windows SDKやプラットフォームSDK |
| CMake | 最低バージョンとCIで使用する実バージョン |
| ビルド設定 | Debug、Release、静的リンク、動的リンク |
| 機能 | vcpkgのfeaturesとdefault-features |
| オーバーレイ | overlay ports、overlay tripletsの内容 |
同じライブラリの同じバージョンでも、トリプレット、コンパイラ、CRTリンク方式、ビルドオプションが異なれば、生成されるバイナリは変わります。
そのため、現実的な再現性の目標は次のように分けて考えると分かりやすくなります。
- 依存解決の再現性:
builtin-baselineとマニフェストで管理 - ビルド設定の再現性:CMake PresetsやCI設定で管理
- 生成バイナリの再現性:コンパイラ、SDK、環境変数まで含めて管理
builtin-baselineを安全に更新する手順
ベースライン更新は、単なる保守作業ではなく、依存関係全体の変更として扱います。
更新専用ブランチを作る
git switch -c chore/update-vcpkg-baseline
アプリケーション機能の変更とベースライン更新を同じプルリクエストへ混ぜると、障害発生時に原因を切り分けにくくなります。
原則として、ベースライン更新だけを独立させます。
更新先のコミットを明示する
プロジェクト内のvcpkgを更新する場合は、採用するタグまたはコミットへ移動します。
git -C vcpkg fetch --tags
git -C vcpkg checkout <更新先のタグまたはコミット>
git -C vcpkg rev-parse HEAD
.\vcpkg\bootstrap-vcpkg.bat
x-update-baselineは現在のvcpkgコミットへベースラインを進めます。vcpkg側が意図せず最新コミットへ移動している状態で実行すると、想定以上の更新を取り込む可能性があります。
dry-runで変更予定を確認する
.\vcpkg\vcpkg.exe x-update-baseline --dry-run
--dry-runでは、ファイルを変更せずに予定されるベースライン更新を確認できます。カスタムレジストリを構成している場合、このコマンドはvcpkg.jsonとvcpkg-configuration.jsonに設定されたレジストリも対象にします。(Microsoft Learn)
問題がなければ更新します。
.\vcpkg\vcpkg.exe x-update-baseline
続いて差分を確認します。
git diff -- vcpkg.json vcpkg-configuration.json
git diff --submodule
依存パッケージの変更内容を確認する
既存のインストール状態を残したまま、変更予定を確認できます。
.\vcpkg\vcpkg.exe install --dry-run
この出力では、次の点を重点的に確認します。
- バージョンが変わる直接依存
- 推移的依存として新しく追加されるパッケージ
- 削除されるパッケージ
- 有効化または無効化されるfeatures
- 再ビルド対象
- ホスト用パッケージとターゲット用パッケージ
同じ上流ライブラリのバージョン名でも、vcpkg側のパッケージ定義だけが修正され、1.2.3#1のようにport-versionが上がることがあります。ソース本体が同じでも、ビルド手順、パッチ、依存関係、CMake設定が変わっている可能性があるため、port-versionの変更も確認対象です。(Microsoft Learn)
完全なクリーンビルドを行う
ベースライン更新後は、少なくとも一度、インストールツリーとビルドディレクトリを削除します。
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force vcpkg_installed -ErrorAction SilentlyContinue
cmake --preset msvc-debug
cmake --build --preset build-debug
Release構成も確認します。
cmake --preset msvc-release
cmake --build --preset build-release
増分ビルドだけでは、古いヘッダー、オブジェクトファイル、静的ライブラリ、DLLが残り、更新後の問題を見逃すことがあります。
テストとABI影響を確認する
ベースライン更新では、単に「コンパイルが通るか」だけでなく、実行時の互換性も確認します。
特に確認すべきなのは次の項目です。
- 単体テストと結合テスト
- ファイルやネットワークを使う主要機能
- JSON、XML、画像などの入出力
- 例外処理とエラー時の挙動
- DLLの読み込み
- プラグインや外部モジュールとの境界
- シリアライズ形式の互換性
- DebugとReleaseの両構成
- サポートする全トリプレット
- MSVC、Clang、GCCなど対象コンパイラ
外部公開するDLLやSDKを作っている場合は、構造体レイアウト、エクスポート関数、C++標準ライブラリ型の受け渡し、ランタイムライブラリの違いにも注意が必要です。
ベースライン更新によって依存DLLが変わった場合は、アプリケーション本体だけでなく、関連するDLLやプラグインも同じ依存セットで再ビルドしてください。
更新を承認する判断基準
| 更新理由 | 推奨対応 |
|---|---|
| セキュリティ修正 | 優先度を上げて更新し、全テストを実施 |
| 使用中ライブラリの重大な不具合 | 修正内容と影響範囲を確認して更新 |
| 必要な新API | version>=かベースライン更新を比較 |
| コンパイラやSDK更新への対応 | ツールチェーン更新と同時に検証 |
| 定期メンテナンス | 月次や四半期など決めた周期で更新 |
| 単に新しい版が出た | 必要性がなければ急いで更新しない |
builtin-baselineの目的は、常に最新へ追従することではありません。動作確認済みの依存関係を維持し、更新するタイミングを開発チーム側で決められるようにすることが重要です。
個別パッケージだけ更新したい場合
version>=を使う
特定ライブラリで必要な最低バージョンが決まっている場合は、依存関係をオブジェクト形式で記述できます。
{
"name": "sample-app",
"version-string": "1.0.0",
"builtin-baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc",
"dependencies": [
{
"name": "fmt",
"version>=": "10.1.1"
},
"zlib"
]
}
この場合、fmtはベースラインの条件と10.1.1以上という条件の両方を満たす最小バージョンへ解決されます。version>=は完全固定ではなく、最低バージョン指定である点に注意してください。(Microsoft Learn)
overridesは例外的に使う
特定バージョンを強制する場合はoverridesを使用できます。
{
"name": "sample-app",
"version-string": "1.0.0",
"builtin-baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc",
"dependencies": [
"fmt",
"zlib"
],
"overrides": [
{
"name": "zlib",
"version": "1.2.8"
}
]
}
overridesに指定されたパッケージは、ほかのバージョン制約を無視して指定版が使われます。依存先がより新しいAPIを必要としていても強制できてしまうため、長期的な標準運用ではなく、競合解消や一時的な後退措置として使用します。(Microsoft Learn)
カスタムレジストリを使っている場合の注意点
builtin-baselineが対象にするのは、vcpkgのデフォルト組み込みレジストリです。
社内ライブラリなどをvcpkg-configuration.jsonのGitレジストリから取得している場合は、各レジストリにもbaselineが必要です。
{
"default-registry": {
"kind": "builtin",
"baseline": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"registries": [
{
"kind": "git",
"repository": "社内レジストリのURL",
"baseline": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"packages": [
"company-*"
]
}
]
}
この構成でx-update-baselineを実行すると、設定された複数のレジストリが更新対象になります。組み込みレジストリだけを更新するつもりで実行しないよう、必ず--dry-runとGit差分を確認してください。(Microsoft Learn)
Copilot CLIへ渡す指示の例
Copilotに単に「依存関係を最新化して」と依頼すると、vcpkg本体、ベースライン、ライブラリの最低バージョン、ビルド設定が一度に変更される可能性があります。
初回固定では、次のように指示します。
このC++プロジェクトはvcpkgのマニフェストモードを使用しています。
現在のvcpkg.jsonを監査し、依存関係を再現可能にするため、
現在チェックアウトされているレビュー済みvcpkgコミットを使って
builtin-baselineを追加してください。
変更前にx-update-baselineのdry-run結果を示し、
vcpkg.json以外のファイルは勝手に変更しないでください。
変更後はクリーンなvcpkg_installedとbuildで構成、ビルド、
テストを実行してください。
更新時は、次のように変更範囲を制限します。
vcpkgのbuiltin-baseline更新専用の作業として進めてください。
採用するvcpkgコミットを先に提示し、承認済みのコミット以外へ
移動しないでください。
x-update-baseline --dry-runを実行し、
更新される直接依存と推移的依存、features、port-versionを整理してください。
overridesは追加しないでください。
更新後はDebugとReleaseでクリーンビルドし、
既存テストをすべて実行してください。
失敗した場合は依存バージョンを勝手に変更せず、原因を報告してください。
Copilotには作業を実行させつつも、次の判断は人間側に残します。
- 採用するvcpkgコミット
- ベースラインを更新する理由
version>=やoverridesを追加するか- ABI変更を許容できるか
- テスト結果を更新承認の根拠にできるか
よくある失敗
Microsoftのサンプル値をそのままコピーする
サンプルのコミットSHAは説明時点の例です。自社プロジェクトで採用するvcpkgコミットと一致するとは限りません。
使用するvcpkgを先に決め、その状態からbuiltin-baselineを生成します。
vcpkg本体だけ更新する
サブモジュールやVCPKG_ROOTだけを更新し、vcpkg.jsonを変更しないと、ツールと依存基準の管理状態が分かりにくくなります。
逆に、builtin-baselineだけ新しくして、古すぎるvcpkgツールを使い続ける構成も避けます。
増分ビルドだけで更新を承認する
古い静的ライブラリやオブジェクトファイルが残っていると、本来発生するはずのコンパイルエラーやリンクエラーが見えないことがあります。
更新時はbuildとvcpkg_installedを削除した検証を含めます。
overridesを通常の固定方法として使う
overridesは特定バージョンを強制できますが、ほかの依存関係が要求する条件まで無視します。
プロジェクト全体の標準的な固定にはbuiltin-baselineを使い、overridesには使用理由、削除条件、確認期限を残します。
baselineだけで完全な再現ビルドになると思う
builtin-baselineが固定するのは、主にvcpkgの依存バージョン解決です。
次の違いは別途管理しなければなりません。
- コンパイラ
- Windows SDK
- CMake
- トリプレット
- 静的リンクと動的リンク
- 環境変数
- オーバーレイポート
- ビルドオプション
- CPUアーキテクチャ
まず実施すべきチェックリスト
Copilot CLIで生成したC++プロジェクトを受け取ったら、次の順序で確認します。
vcpkg.jsonにbuiltin-baselineがあるか確認する- 使用中のvcpkgコミットを確認する
- 採用するリリースタグまたはコミットを固定する
x-update-baseline --add-initial-baseline --dry-runで変更予定を確認するbuiltin-baselineを追加する- vcpkg本体の参照と
vcpkg.jsonを同時にコミットする buildとvcpkg_installedを削除して復元する- DebugとReleaseでビルドする
- 単体テストと主要機能のスモークテストを行う
- 別PCまたはCIのクリーン環境でも復元する
- 以後のベースライン更新を専用プルリクエストに限定する
Copilotがライブラリの選定や設定を自動化しても、依存関係の更新タイミングまで自動化する必要はありません。builtin-baselineで動作確認済みの依存グラフを固定し、更新時だけ明示的にベースラインを進める運用にすれば、別PCや後日の復元で突然ビルドが壊れるリスクを大きく抑えられます。

コメント