MSBuild:如何在常规构建过程后将自定义生成的文件视为内容构建输出



我正在使用vs2019的MSBuild SDK风格项目。我试图运行一个自定义文件生成工具,这取决于当前项目的构建输出。应该将这些文件视为为其设置了CopyToOutputDirectory的常规内容。在依赖的项目中,我希望这些文件也是输出目录的一部分。我现在的解决方案是有效的,但不是从干净的构建,这显然是不可接受的。

我目前在项目文件中有这个:

<Target Name="Generation" AfterTargets="AfterBuild">
<Exec Command="GeneratedFiles" />
<ItemGroup>
<Content Include="$(TargetDir)GeneratedFiles.*.xml">
<TargetPath>GeneratedFiles%(Filename)%(Extension)</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Target>

这可以工作,但只适用于非干净的构建。

Generation目标不起作用的原因是,基于CopyToOutputDirectory元数据的存在和值,执行复制的逻辑作为'CoreBuild'的一部分运行。Generation目标有AfterTargets="AfterBuild"。它在构建(和复制)已经完成后运行。

Generation目标不能用于第一次构建,也不能用于后续的增量构建。

问题描述说明文件是为增量构建复制的。虽然文件被复制可能是真的,但由于Generation目标,它们无法被复制。在没有看到完整的项目文件的情况下,我假设项目中还有另一个地方为这些文件设置了CopyToOutputDirectory元数据。

要在编译之后和文件复制之前运行Generation目标,可以使用以下命令:

<Target Name="Generation" AfterTargets="AfterCompile" BeforeTargets="GetCopyFilesToOutputDirectoryItems">

GetCopyFilesToOutputDirectoryItems是公开记录的,它运行在另一个公开记录的目标CopyFilesToOutputDirectory之前。CopyFilesToOutputDirectory是用DependsOnTargets属性定义的,该属性运行一组执行实际复制的目标。这意味着文件拷贝在CopyFilesToOutputDirectory目标完成之前完成。

为了保证正确的顺序,使用BeforeTargets="GetCopyFilesToOutputDirectoryItems"而不是BeforeTargets="CopyFilesToOutputDirectory"

如果场景不同,<Exec Command="GeneratedFiles" />不依赖于编译步骤,即GeneratedFiles命令不使用项目正在创建的程序集,那么Generation目标可能更早发生,例如

<Target Name="Generation" BeforeTargets="BeforeBuild">

更新-执行目标,如果ProjectReference有一个特定的项目

此更新是对评论中讨论的回应。

假设我们有一个FooApp项目和一个BarLib项目,FooApp依赖于BarLib,需要从BarLib项目目录复制任意文件。

FooApp有一个ProjectReference到BarLib,例如

<ItemGroup>
<ProjectReference Include="..BarLibBarLib.csproj" />
</ItemGroup>

ProjectReference是一个ItemGroup,我们可以检查它是否包含BarLib。

<Target Name="CopyFilesFromBar" BeforeTargets="BeforeBuild">
<PropertyGroup>
<!-- The name of the project to look for -->
<BarProjectName>BarLib</BarProjectName>
<!-- Use batching to check the ItemGroup -->
<!-- Save the project's directory because we will need it later -->
<BarProjectDirectory Condition="'%(ProjectReference.Filename)' == '$(BarProjectName)'">%(ProjectReference.Directory)</BarProjectDirectory>
<!-- Set up a boolean that indicates if the project was found or not -->
<HasProjectRefToBar>false</HasProjectRefToBar>
<HasProjectRefToBar Condition="$(FooProjectDirectory) != ''">true</HasProjectRefToBar>
</PropertyGroup>
<!-- Copy if the project was found in ProjectReference -->
<Copy SourceFiles="$(TargetDir)$(BarProjectDirectory)binGeneratedFiles*.*" DestinationFolder="$(OutputPath)GeneratedFiles" Condition="$(HasProjectRefToBar)" />
</Target>

这个目标可以在Directory.Build.targets文件中定义一次,并在整个解决方案中共享。

如果生成的文件(在示例场景中的BarLib中)不会根据配置和平台而更改,请考虑使用一个不更改的输出路径位置,如示例中- 'binGeneratedFiles'。这使得消费项目更加容易。否则,使用相同的配置和平台值以及相同的$(OutputPath),使所有项目保持同步。

最新更新