MSBuild 复制动态生成的文件作为项目依赖项的一部分
本文关键字:项目 依赖 一部分 文件 复制 动态 MSBuild | 更新日期: 2023-09-27 17:53:13
我有一个自定义的msbuild任务,该任务正在生成一些输出文件到项目A的输出目录($(TargetDir((。当前代码如下所示:
<MyCustomTask ...>
<Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>
项目 B 引用项目 A,但问题是在构建项目 B 时,MyCustomTask 生成的文件不会复制到项目 B 的输出目录中。
我们如何获取动态生成的其他文件,作为 MSBuild 项目依赖项的一部分进行复制?
我终于设法自动执行项目 B 的副本,而无需修改它。IIya 离解决方案并不远,但事实是我无法静态生成,因为要使用 MyCustomTask 从项目 A 生成的文件列表是动态的。在对Microsoft.Common.targets
进行深入研究后,我发现ProjectB会通过调用目标GetCopyToOutputDirectoryItems
来获取项目A的输出列表。此目标依赖于AssignTargetPaths
,而本身依赖于目标列表属性AssignTargetPathsDependsOn
。
因此,为了生成动态内容并通过标准项目依赖项自动复制此内容,我们需要在两个不同的地方挂钩项目 A:
- 在
AssignTargetPathsDependsOn
,因为它由项目 A 上的项目 B 通过 GetCopyToOutputDirectoryItems 间接调用。而且在调用 A 时,它也会被项目 A 间接调用PrepareResource
。在这里,我们只是输出将生成(由项目 A(或由项目 B 使用的文件列表。AssignTargetPathsDependsOn 将调用一个自定义任务MyCustomTaskList
该任务只负责输出文件列表(但不负责生成它们(,此文件列表将使用CopyOutputDirectory
创建动态"内容"。 - 为了实际生成项目 A 中的内容
BuildDependsOn
。这将调用将生成内容的MyCustomTask
。
所有这些都在 Project A 中是这样设置的:
<!-- In Project A -->
<!-- Task to generate the files -->
<UsingTask TaskName="MyCustomTask" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- Task to output the list of generated of files - It doesn't generate the file -->
<UsingTask TaskName="MyCustomTaskList" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- 1st PART : When Project A is built, It will generate effectively the files -->
<PropertyGroup>
<BuildDependsOn>
MyCustomTaskTarget;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskTarget">
<!-- Call MyCustomTask generate the files files that will be generated by MyCustomTask -->
<MyCustomTask
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
</MyCustomTask>
</Target>
<!-- 2nd PART : When Project B is built, It will call GetCopyToOutputDirectoryItems on ProjectA so we need to generate this list when it is called -->
<!-- For this we need to override AssignTargetPathsDependsOn in order to generate the list of files -->
<!-- as GetCopyToOutputDirectoryItems ultimately depends on AssignTargetPathsDependsOn -->
<!-- Content need to be generated before AssignTargets, because AssignTargets will prepare all files to be copied later by GetCopyToOutputDirectoryItems -->
<!-- This part is also called from ProjectA when target 'PrepareResources' is called -->
<PropertyGroup>
<AssignTargetPathsDependsOn>
$(AssignTargetPathsDependsOn);
MyCustomTaskListTarget;
</AssignTargetPathsDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskListTarget">
<!-- Call MyCustomTaskList generating the list of files that will be generated by MyCustomTask -->
<MyCustomTaskList
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
<Output TaskParameter="ContentFiles" ItemName="MyCustomContent"/>
</MyCustomTaskList>
<ItemGroup>
<!--Generate the lsit of content generated by MyCustomTask -->
<Content Include="@(MyCustomContent)" KeepMetadata="Link;CopyToOutputDirectory"/>
</ItemGroup>
</Target>
此方法适用于使用 Common.Target 的任何类型的 C# 项目(因此它适用于纯桌面、WinRT XAML App 或 Windows Phone 8 项目(。
这样的东西似乎有效,要么手动将其包含在ProjectA的.csproj中(请记住VS有一个坏习惯,偶尔将通配符解析为绝对路径并覆盖.csproj(,要么由自定义任务本身动态注入。此外,VS 在打开时缓存项组,因此如果文件存在但已删除,则可能不会复制文件或生成失败。在这种情况下,需要重新加载项目或重新启动 VS 才能重新评估项组。MSBuild,TFS等应该始终有效。
<ItemGroup>
<Content Include="$(TargetDir)'*.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
如果您已经在使用 MSBuild 自己构建了此内容,是否可以添加一个复制任务来推动自己周围的文件?
就其价值而言,如果我在目标中放置一个<None Link="..." />
,我就可以处理输出,而不会将其显示在基于 SDK 的项目中。此外,引用此项目的其他项目会将其作为输出的一部分。
例如
<ItemGroup>
<WebPackBuildOutput Include="..'..'WebPackOutput'dist'**'*" />
</ItemGroup>
<Target Name="WebPackOutputContentTarget" BeforeTargets="BeforeBuild">
<Message Text="Output dynamic content: @(WebPackBuildOutput)" Importance="high"/>
<ItemGroup>
<!-- Manually constructing Link metadata, works in classic projects as well -->
<None Include="@(WebPackBuildOutput)" Link="dist'%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
我了解,您希望通过在ProjectB.msbuild中仅编写以下行来添加其他步骤:
<Import Project="ProjectA.msbuild" />
要实现它,您可以在ProjectA中编写类似内容的内容:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>
这会将任务添加到生成任务的依赖项列表中。
有关详细信息,请参阅此问题:风格警察MS构建魔法?谁在称呼StyleCop为目标?