22

我有一个自定义 msbuild 任务,它正在生成一些输出文件到 ProjectA 的输出目录 ($(TargetDir))。当前代码是这样的:

<MyCustomTask ...>
   <Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>

A ProjectB 正在引用ProjectA,但问题是在构建 ProjectB 时,MyCustomTask 生成的文件不会复制到 ProjectB 的输出目录中。

我们如何获取动态生成的附加文件以作为 MSBuild 项目依赖项的一部分进行复制?

4

5 回答 5

15

我终于设法自动执行来自项目 B的副本,而无需修改它。IIya 离解决方案不远,但事实是我无法静态生成,因为使用 MyCustomTask从项目 A生成的文件列表是动态的。在深入挖掘之后Microsoft.Common.targets,我发现 ProjectB 将通过调用 target来获取Project AGetCopyToOutputDirectoryItems的输出列表。此目标依赖于其AssignTargetPaths本身依赖于目标列表属性AssignTargetPathsDependsOn

因此,为了动态生成内容并通过标准项目依赖项自动复制此内容,我们需要在两个不同的地方挂钩项目 A :

  • AssignTargetPathsDependsOn因为它是由项目 B通过 GetCopyToOutputDirectoryItems在项目 A上间接调用的。并且它在被调用时被项目 APrepareResource间接调用。在这里,我们只是输出将生成(由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 项目)。

于 2013-01-25T13:45:28.867 回答
4

像这样的东西似乎有效,要么将其手动包含到 ProjectA 的 .csproj 中(请记住,VS 有一个坏习惯,即偶尔将通配符解析为绝对路径并覆盖 .csproj)或由自定义任务本身动态注入。此外,VS 在打开时缓存项目组,因此如果文件在那里但被删除,它可能不会复制文件或构建失败。在这种情况下,需要重新加载项目或重新启动 VS 才能重新评估项目组。MSBuild、TFS 等应该始终有效。

<ItemGroup>
  <Content Include="$(TargetDir)\*.txt">
    <Link>%(Filename)%(Extension)</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>
于 2013-01-21T00:45:22.517 回答
1

如果您已经使用 MSBuild 自己进行了此构建,您是否可以添加一个复制任务来将文件推送到自己周围?

于 2013-01-14T16:30:10.497 回答
1

对于它的价值,如果我将 a<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>
于 2019-10-21T11:00:40.120 回答
0

据我了解,您想通过在 ProjectB.msbuild 中仅编写此行来添加额外的步骤:

<Import Project="ProjectA.msbuild" />

要实现它,您可以在 ProjectA 中编写类似的内容:

<PropertyGroup>
  <BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>

这会将您的任务添加到构建任务的依赖项列表中。

有关详细信息,请参阅此问题: StyleCop MS Build 魔术?谁在调用 StyleCop 目标?

于 2013-01-25T07:10:58.560 回答