including-generated-files について
MSBuild プロジェクトでビルド中に生成されるファイルを扱う開発スキルです。出力やコンパイルへの登録方法と、クリーン時に削除するための設定を説明します。
主な機能
- 01生成ファイルの MSBuild 登録
- 02コンパイル前のソース追加
- 03Clean ターゲット向けの追跡
- 04ファイル種別に応じた実行タイミング
- 05中間出力パスの使用
活用シーン
- 01ビルド中に生成した C# ファイルのコンパイル
- 02生成した設定ファイルやデータファイルのコピー
- 03ビルド間に残る古い生成ファイルの防止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill including-generated-filesインストール後、Claude Code で/including-generated-filesを入力して読み込まれたか確認します。
詳細
生成されたファイルをビルドに含める
概要
ビルド中に生成されたファイルは、通常、ビルドプロセスで無視されます。その結果、次のような分かりにくい問題が発生します。
- 生成されたファイルが出力ディレクトリに含まれない
- 生成されたソースファイルがコンパイルされない
- ビルド中に作成されたファイルがグロブで取得されない
これは、MSBuild のビルドフェーズの仕組みによって発生します。
要点
ビルド中に生成されるコードファイルについては、ファイルを生成するターゲット内で、それらを Compile および FileWrites の項目グループに追加する必要があります。
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>
ファイルを生成するターゲットは、CoreCompile および BeforeCompile ターゲットの前にフックする必要があります(BeforeTargets="CoreCompile;BeforeCompile")。
生成されたファイルが無視される理由
詳しい説明については、MSBuild によるプロジェクトのビルド方法を参照してください。
評価フェーズ
MSBuild はプロジェクトを読み込み、すべてをインポートし、プロパティを作成し、ターゲット外の項目のグロブを展開して、ビルドプロセスを設定します。
実行フェーズ
MSBuild は、指定されたプロパティと項目を使ってターゲットとタスクを実行し、ビルドを行います。
重要なポイント: 実行中に生成されたファイルは評価中には存在しないため、検出されません。これは、ソースファイル(.cs)など、既定でグロブされるファイルに特に影響します。
解決策:生成されたファイルを手動で追加する
ビルド中にファイルが生成される場合は、ビルドプロセスに手動で追加します。方法は、生成するファイルの種類によって異なります。
生成ファイルの場所には $(IntermediateOutputPath) を使用する
生成ファイルのベースディレクトリには、必ず $(IntermediateOutputPath) を使用してください。obj\ をハードコードしたり、中間パスを手動で組み立てたりしないでください(例:obj\$(Configuration)\$(TargetFramework)\)。ビルド構成によっては、中間出力パスが別の場所(共有出力ディレクトリや CI 環境など)にリダイレクトされることがあります。$(IntermediateOutputPath) を使用すれば、実際のパスにかかわらずターゲットが正しく動作します。
生成されたファイルは必ず FileWrites に追加する
生成されたファイルはすべて、FileWrites 項目グループに追加してください。これにより、MSBuild の Clean ターゲットが生成ファイルを適切に削除します。これを行わないと、ビルドを重ねるごとに古い生成物が蓄積します。
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
</ItemGroup>
基本パターン(コード以外のファイル)
出力先にコピーする必要がある生成ファイル(構成ファイル、データファイルなど)は、BeforeBuild の前に Content または None 項目へ追加します。
<Target Name="IncludeGeneratedFiles" BeforeTargets="BeforeBuild">
<ItemGroup>
<None Include="$(IntermediateOutputPath)my-generated-file.xyz" CopyToOutputDirectory="PreserveNewest"/>
<None Include="$(IntermediateOutputPath)generated\*.xyz" CopyToOutputDirectory="PreserveNewest"/>
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
<FileWrites Include="$(IntermediateOutputPath)generated\*.xyz" />
</ItemGroup>
</Target>
生成されたソースファイルの場合(コンパイルが必要なコード)
コンパイルが必要な .cs ファイルを生成する場合は、BeforeTargets="CoreCompile;BeforeCompile" を使用してください。これが Compile 項目を追加する適切なタイミングです。ファイル生成が完了した後、コンパイラーの実行前に処理されます。BeforeBuild を使用すると、シナリオによっては早すぎるため、すべての SDK 機能で確実に動作するとは限りません。
<Target Name="IncludeGeneratedSourceFiles" BeforeTargets="CoreCompile;BeforeCompile">
<PropertyGroup>
<GeneratedCodeDir>$(IntermediateOutputPath)Generated\</GeneratedCodeDir>
<GeneratedFilePath>$(GeneratedCodeDir)MyGeneratedFile.cs</GeneratedFilePath>
</PropertyGroup>
<MakeDir Directories="$(GeneratedCodeDir)" />
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>
</Target>
注:CoreCompile と BeforeCompile の両方を指定すると、先に実行される方のターゲットより前にターゲットが実行されるため、ビルドのカスタマイズにかかわらず、確実な順序を保てます。
ターゲットのタイミング
生成するファイルの種類に応じて、BeforeTargets の値を選択してください。
BeforeTargets="BeforeBuild"—NoneまたはContentに追加するコード以外のファイルに使用します。出力先へのコピーが必要なシナリオに適した早いタイミングで実行されます。BeforeTargets="CoreCompile;BeforeCompile"—Compileに追加する生成ソースファイルに使用します。コンパイラーの実行前にファイルが含まれるようにします。BeforeTargets="AssignTargetPaths"—NoneやContentなどの項目が新しい項目に変換される前の「最終地点」です。BeforeBuildでは早すぎる場合の代替手段として使用します。
グロブの動作
グロブの動作は、グロブを実行したタイミングによって異なります。
| グロブの場所 | キャプチャされるファイル |
|---|---|
| ターゲットの外部 | 評価フェーズ(ビルド開始前)に表示されているファイルのみ |
| ターゲットの内部 | ターゲットの実行時に表示されているファイル(タイミングが適切であれば、生成されたファイルもキャプチャできます) |
このため、この解決策では <ItemGroup> を <Target> の内部に配置しています。生成されたファイルが存在する実行フェーズ中にグロブが実行されるためです。