including-generated-files
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseIncluding Generated Files Into Your Build
将生成的文件纳入构建流程
Overview
概述
Files generated during the build are generally ignored by the build process. This leads to confusing results such as:
- Generated files not being included in the output directory
- Generated source files not being compiled
- Globs not capturing files created during the build
This happens because of how MSBuild's build phases work.
构建过程中生成的文件通常会被构建流程忽略,这会导致一些令人困惑的结果:
- 生成的文件未被复制到输出目录
- 生成的源文件未被编译
- 通配符无法捕获构建过程中创建的文件
这是由MSBuild的构建阶段工作方式导致的。
Quick Takeaway
核心要点
For code files generated during the build - we need to add those to and item groups within the target generating the file(s):
CompileFileWritesxml
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>The target generating the file(s) should be hooked before CoreCompile and BeforeCompile targets -
BeforeTargets="CoreCompile;BeforeCompile"对于构建过程中生成的代码文件,我们需要在生成文件的Target内将其添加到和项组中:
CompileFileWritesxml
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>生成文件的Target应设置为在CoreCompile和BeforeCompile目标之前执行 -
BeforeTargets="CoreCompile;BeforeCompile"Why Generated Files Are Ignored
为什么生成的文件会被忽略
For detailed explanation, see How MSBuild Builds Projects.
如需详细解释,请参阅MSBuild项目构建方式。
Evaluation Phase
评估阶段
MSBuild reads your project, imports everything, creates Properties, expands globs for Items outside of Targets, and sets up the build process.
MSBuild会读取项目、导入所有内容、创建Properties、展开Target外部的Items通配符,并设置构建流程。
Execution Phase
执行阶段
MSBuild runs Targets & Tasks with the provided Properties & Items to perform the build.
Key Takeaway: Files generated during execution don't exist during evaluation, therefore they aren't found. This particularly affects files that are globbed by default, such as source files ().
.csMSBuild使用提供的Properties和Items运行Targets与Tasks以执行构建操作。
核心结论: 执行阶段生成的文件在评估阶段并不存在,因此无法被检测到。这尤其会影响那些默认使用通配符匹配的文件,例如源文件()。
.csSolution: Manually Add Generated Files
解决方案:手动添加生成的文件
When files are generated during the build, manually add them into the build process. The approach depends on the type of file being generated.
当文件在构建过程中生成时,需要手动将其纳入构建流程。具体方法取决于生成文件的类型。
Use $(IntermediateOutputPath)
for Generated File Location
$(IntermediateOutputPath)使用$(IntermediateOutputPath)
作为生成文件的存储位置
$(IntermediateOutputPath)Always use as the base directory for generated files. Do not hardcode or construct the intermediary path manually (e.g., ). The intermediate output path can be redirected to a different location in some build configurations (e.g., shared output directories, CI environments). Using ensures your target works correctly regardless of the actual path.
$(IntermediateOutputPath)obj\obj\$(Configuration)\$(TargetFramework)\$(IntermediateOutputPath)始终使用作为生成文件的基础目录。请勿硬编码或手动构造中间路径(例如)。在某些构建配置中(例如共享输出目录、CI环境),中间输出路径可能会被重定向到其他位置。使用可确保你的Target在任何实际路径下都能正常工作。
$(IntermediateOutputPath)obj\obj\$(Configuration)\$(TargetFramework)\$(IntermediateOutputPath)Always Add Generated Files to FileWrites
FileWrites始终将生成的文件添加到FileWrites
FileWritesEvery generated file should be added to the item group. This ensures that MSBuild's target properly removes your generated files. Without this, generated files will accumulate as stale artifacts across builds.
FileWritesCleanxml
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
</ItemGroup>每个生成的文件都应添加到项组中。这可确保MSBuild的Target能正确删除生成的文件。如果不这样做,生成的文件会作为陈旧工件在多次构建中累积。
FileWritesCleanxml
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
</ItemGroup>Basic Pattern (Non-Code Files)
基础模式(非代码文件)
For generated files that need to be copied to output (config files, data files, etc.), add them to or items before :
ContentNoneBeforeBuildxml
<Target Name="IncludeGeneratedFiles" BeforeTargets="BeforeBuild">
<!-- Your logic that generates files goes here -->
<ItemGroup>
<None Include="$(IntermediateOutputPath)my-generated-file.xyz" CopyToOutputDirectory="PreserveNewest"/>
<!-- Capture all files of a certain type with a glob -->
<None Include="$(IntermediateOutputPath)generated\*.xyz" CopyToOutputDirectory="PreserveNewest"/>
<!-- Register generated files for proper cleanup -->
<FileWrites Include="$(IntermediateOutputPath)my-generated-file.xyz" />
<FileWrites Include="$(IntermediateOutputPath)generated\*.xyz" />
</ItemGroup>
</Target>对于需要复制到输出目录的生成文件(配置文件、数据文件等),请在之前将其添加到或项中:
BeforeBuildContentNonexml
<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>For Generated Source Files (Code That Needs Compilation)
针对生成的源文件(需要编译的代码)
If you're generating files that need to be compiled, use . This is the correct timing for adding items — it runs late enough that the file generation has occurred, but before the compiler runs. Using is too early for some scenarios and may not work reliably with all SDK features.
.csBeforeTargets="CoreCompile;BeforeCompile"CompileBeforeBuildxml
<Target Name="IncludeGeneratedSourceFiles" BeforeTargets="CoreCompile;BeforeCompile">
<PropertyGroup>
<GeneratedCodeDir>$(IntermediateOutputPath)Generated\</GeneratedCodeDir>
<GeneratedFilePath>$(GeneratedCodeDir)MyGeneratedFile.cs</GeneratedFilePath>
</PropertyGroup>
<MakeDir Directories="$(GeneratedCodeDir)" />
<!-- Your logic that generates the .cs file goes here -->
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>
</Target>Note: Specifying both and ensures the target runs before whichever target comes first, providing robust ordering regardless of customizations in the build.
CoreCompileBeforeCompile如果你生成的文件需要被编译,请使用****。这是添加项的正确时机——它的执行时间足够晚,确保文件已生成,但又在编译器运行之前。在某些场景下使用时机过早,可能无法与所有SDK功能可靠兼容。
.csBeforeTargets="CoreCompile;BeforeCompile"CompileBeforeBuildxml
<Target Name="IncludeGeneratedSourceFiles" BeforeTargets="CoreCompile;BeforeCompile">
<PropertyGroup>
<GeneratedCodeDir>$(IntermediateOutputPath)Generated\</GeneratedCodeDir>
<GeneratedFilePath>$(GeneratedCodeDir)MyGeneratedFile.cs</GeneratedFilePath>
</PropertyGroup>
<MakeDir Directories="$(GeneratedCodeDir)" />
<!-- 此处添加生成.cs文件的逻辑 -->
<ItemGroup>
<Compile Include="$(GeneratedFilePath)" />
<FileWrites Include="$(GeneratedFilePath)" />
</ItemGroup>
</Target>注意:同时指定和可确保Target在最先执行的那个目标之前运行,无论构建中的自定义配置如何,都能提供可靠的执行顺序。
CoreCompileBeforeCompileTarget Timing
Target时机选择
Choose the value based on the type of file being generated:
BeforeTargets- — For non-code files added to
BeforeTargets="BeforeBuild"orNone. Runs early enough for copy-to-output scenarios.Content - — For generated source files added to
BeforeTargets="CoreCompile;BeforeCompile". Ensures the file is included before the compiler runs.Compile - — The "final stop" before
BeforeTargets="AssignTargetPaths"andNoneitems (among others) are transformed into new items. Use as a fallback ifContentis too early.BeforeBuild
根据生成文件的类型选择的值:
BeforeTargets- — 适用于添加到
BeforeTargets="BeforeBuild"或None的非代码文件。执行时机足够早,可满足复制到输出目录的场景。Content - — 适用于添加到
BeforeTargets="CoreCompile;BeforeCompile"的生成源文件。确保文件在编译器运行之前被纳入构建。Compile - — 这是
BeforeTargets="AssignTargetPaths"和None等项被转换为新项之前的"最后时机"。如果Content时机过早,可将此作为备选。BeforeBuild
Globbing Behavior
通配符行为
Globs behave according to when the glob took place:
| Glob Location | Files Captured |
|---|---|
| Outside of a target | Only files visible during Evaluation phase (before build starts) |
| Inside of a target | Files visible when the target runs (can capture generated files if timed correctly) |
This is why the solution places the inside a - the glob runs during execution when the generated files exist.
<ItemGroup><Target>通配符的行为取决于通配符执行的时机:
| 通配符位置 | 捕获的文件 |
|---|---|
| Target外部 | 仅捕获评估阶段(构建开始前)可见的文件 |
| Target内部 | 捕获Target运行时可见的文件(如果时机正确,可捕获生成的文件) |
这就是为什么解决方案将放在内部的原因——通配符在执行阶段运行,此时生成的文件已经存在。
<ItemGroup><Target>