我有一个自定义的msbuild任务,它将一些输出文件生成到ProjectA的输出目录($(TargetDir))。目前的代码是这样的:
<MyCustomTask ...>
<Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>
ProjectB正在引用 ProjectA ,但问题是在构建ProjectB时,MyCustomTask生成的文件不会复制到ProjectB的输出目录中。
如何使用MSBuild将动态生成的附加文件作为项目依赖项的一部分进行复制?
答案 0 :(得分:12)
我终于设法自动执行 Project B 的副本而无需修改它。 IIya离解决方案不远,但事实是我无法静态生成,因为使用MyCustomTask从 Project A 生成的文件列表是动态的。在深入研究Microsoft.Common.targets
之后,我发现ProjectB将通过调用目标GetCopyToOutputDirectoryItems
来获取 Project A 的输出列表。此目标取决于AssignTargetPaths
,AssignTargetPathsDependsOn
本身依赖于目标列表属性AssignTargetPathsDependsOn
。
因此,为了生成动态内容并通过标准项目依赖项自动复制此内容,我们需要在两个不同的地方挂钩 Project A :
PrepareResource
中,项目A 通过GetCopyToOutputDirectoryItems在项目A 上间接调用它。当调用MyCustomTaskList
时, Project A 间接调用它。在这里,我们只输出将生成的文件列表(通过 Project A )或由 Project B 使用。 AssignTargetPathsDependsOn将调用自定义任务CopyOutputDirectory
,该任务仅负责输出文件列表(但不生成它们),此文件列表将使用BuildDependsOn
创建动态“内容”。MyCustomTask
中,以便在项目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>
。所有这些都在ProjectA中设置如下:
{{1}}
此方法适用于使用Common.Targets的任何C#项目(因此它与纯桌面,WinRT XAML应用程序或Windows Phone 8项目一起使用)。
答案 1 :(得分:2)
这样的东西似乎有效,要么手动将其包含在ProjectA的.csproj中(请记住VS有一个习惯,即偶尔将通配符解析为绝对路径并覆盖.csproj)或者通过自定义任务本身动态注入。此外,VS在打开时缓存项目组,因此它可能不会复制文件或者如果它们在那里但是被删除则使构建失败。在这种情况下,需要重新加载项目或重新启动VS以重新评估项目组。 MSBuild,TFS等应该始终有效。
<ItemGroup>
<Content Include="$(TargetDir)\*.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
答案 2 :(得分:1)
如果您已经使用MSBuild自行构建,可以添加Copy Task来推送自己的文件吗?
答案 3 :(得分:1)
据我了解,您希望通过在ProjectB.msbuild中仅编写此行来添加其他步骤:
<Import Project="ProjectA.msbuild" />
要实现它,您可以编写类似于ProjectA的内容:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>
这会将您的任务添加到Build任务的依赖项列表中。
详情请参阅此问题: StyleCop MS Build magic? Who is calling the StyleCop target?
答案 4 :(得分:0)
关于它的价值,如果我将<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>