MSBuild 复制动态生成的文件作为项目依赖项的一部分
Posted
技术标签:
【中文标题】MSBuild 复制动态生成的文件作为项目依赖项的一部分【英文标题】:MSBuild to copy dynamically generated files as part of project dependency 【发布时间】:2012-12-28 15:30:02 【问题描述】:我有一个自定义 msbuild 任务,它正在生成一些输出文件到 ProjectA 的输出目录 ($(TargetDir))。当前代码是这样的:
<MyCustomTask ...>
<Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>
A ProjectB 正在引用 ProjectA 但问题是在构建 ProjectB 时,MyCustomTask 生成的文件不会复制到 ProjectB 的输出目录。
我们如何获取动态生成的附加文件以作为 MSBuild 项目依赖项的一部分进行复制?
【问题讨论】:
这里有一个类似的问题,导致 ***.com/q/44752139/165500 的方法略有不同,这对于不涉及自定义任务的情况可能很方便。 【参考方案1】:我终于设法自动执行 Project B 的副本,而无需对其进行修改。 IIya 离解决方案不远,但事实是我无法静态生成,因为使用 MyCustomTask 从 Project A 生成的文件列表是动态的。在深入了解Microsoft.Common.targets
之后,我发现ProjectB 将通过调用目标GetCopyToOutputDirectoryItems
来获取Project A 的输出列表。这个目标依赖于AssignTargetPaths
,它本身依赖于目标列表属性AssignTargetPathsDependsOn
。
因此,为了动态生成内容并通过标准项目依赖项自动复制此内容,我们需要在两个不同的地方挂钩 Project A:
在AssignTargetPathsDependsOn
中,Project B 通过 GetCopyToOutputDirectoryItems 在 Project A 上间接调用它。当PrepareResource
被调用时,它也被Project A 间接调用。在这里,我们只是输出将生成(由Project A)或由Project B 使用的文件列表。 AssignTargetPathsDependsOn 将调用自定义任务MyCustomTaskList
,该任务仅负责输出文件列表(但不生成文件),此文件列表将使用CopyOutputDirectory
创建动态“内容”。
在BuildDependsOn
中以实际生成Project A 中的内容。这将调用将生成内容的MyCustomTask
。
所有这些都是在 ProjectA 中这样设置的:
<!-- 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.Targets 的 C# 项目(因此它适用于纯桌面、WinRT XAML 应用程序或 Windows Phone 8 项目)。
【讨论】:
请注意AssignTargetPathsDependsOn
技巧已被 .NET SDK 破解,请参阅 this issue 寻求帮助。【参考方案2】:
这样的事情似乎可行,要么将其手动包含到 ProjectA 的 .csproj 中(请记住,VS 有一个坏习惯,即偶尔将通配符解析为绝对路径并覆盖 .csproj)或由自定义任务本身动态注入。此外,VS 在打开时缓存项目组,因此如果文件存在但被删除,它可能不会复制文件或构建失败。在这种情况下,需要重新加载项目或重新启动 VS 才能重新评估项目组。 MSBuild、TFS 等应该始终有效。
<ItemGroup>
<Content Include="$(TargetDir)\*.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
【讨论】:
到目前为止还没有,但我无法像您那样静态生成此列表。看我的回答。【参考方案3】:如果您已经使用 MSBuild 自己进行此构建,是否可以添加 Copy Task 以将文件推送到自己周围?
【讨论】:
我想问题是如何配置任务,以便它实际将文件复制到 ProjectB 的目标中。或者更有可能以某种方式标记 ProjectA 文件夹中的文件,以便 ProjectB 复制类似于引用的文件和其他具有“复制到输出目录 = 始终”的文件。 @Wolfwyrd,正如 Alexei 所说,您的解决方案意味着我必须修改 ProjectB 才能执行复制,但我想避免这种情况。 “FileWrites”似乎仅用于在使用“Clean”目标时跟踪文件,但不用于注册输出。 Microsoft.Common.targets 文件似乎执行了一些特定的操作来复制依赖项,并且不清楚它是否可以扩展来处理。 @xoofx - 所以 ProjectB 在某些时候包含您对 MyCustomTask 的调用?为什么不能在 MyCustomTask 之后将对于它的价值,如果我在目标中放置了 <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>
【讨论】:
【参考方案5】:据我了解,您想通过在 ProjectB.msbuild 中仅编写这一行来添加额外的步骤:
<Import Project="ProjectA.msbuild" />
要实现它,您可以在 ProjectA 中编写类似的内容:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>
这会将您的任务添加到构建任务的依赖项列表中。
有关详细信息,请参阅此问题: StyleCop MS Build magic? Who is calling the StyleCop target?
【讨论】:
并非如此。我想在项目 A 中动态生成一些内容,这些内容将被项目 B 复制,而不必修改项目 B。项目 B 只有一个 ProjectReference 依赖项,但绝不是它必须知道项目 A 正在动态生成一些内容.以上是关于MSBuild 复制动态生成的文件作为项目依赖项的一部分的主要内容,如果未能解决你的问题,请参考以下文章
在 Gradle 中,如何生成具有解析为实际使用版本的动态依赖项的 POM 文件?
是否可以创建一个包含项目类和项目依赖项的“uber”jar 作为带有自定义清单文件的 jar?
Visual Studio/MSBuild 将引用的类库的 app.config 作为 *.dll.config 复制到当前项目的 bin 文件夹