使用 MSBuild (VS2012),如何在生成程序集文件之前拦截它的路径?

With MSBuild (VS2012), how do I intercept the path of an assembly file before it's generated?

我想在我所有的 .vcxproj (Visual C++ 2012) 项目中注入一个自定义 MSBuild 任务,该任务将文件路径作为输入:

该任务然后使用这些路径执行一些操作,然后让构建继续实际生成程序集并将其复制到输出文件夹。我该怎么做?

我在问这个问题之前做了很多研究,但是 MSBuild 示例非常少,通常涉及一个或两个半硬编码路径,而不是通过管道传递所有即将 created/copied 的文件的名称自定义任务。

(如果你很好奇,这些操作可能包括检查文件是否已经存在并且是只读的,是否被程序锁定,是否未在 Perforce 中签出,然后采取适当的措施。)

没有包含 所有 个文件的单个项目最终进入输出目录。这基本上是一项不可能完成的任务,因为添加的任何自定义目标都可以将文件复制到输出目录。如果你想要所有这些,你将不得不手动弄清楚。为此,您 运行 将详细程度设置为 Detailed 的构建(选项->项目和解决方案->构建和 运行 在 VS 中,或在命令行上传递 /v:d)和查找包含您要查找的输出文件的行。例如在某些时候你会看到

Target "Link" in file "C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppCommon.targets"
...
Outputs for ....
  my.EXE
  my.PDB

然后您知道名为 Link 的目标最终会输出 exe 和 pdb,并且您可以通过在文本编辑器中打开该 .targets 文件来找出哪些属性包含这些路径。您将看到 Link 目标调用 Link 任务,该任务具有恰当命名的 OutputFile 和 ProgramDatabaseFile 输入。这就是您要查找的内容,它们被设置为 %(Link.OutputFile) 和 %(Link.ProgramDatabaseFile)。 % 符号表示元数据被引用,因此 Link 实际上不是 属性 而是 Item ,它是数组的 MSBuild 等价物,因此理论上可能存在例如多个 .pdb 文件。然后编写一个目标以将所有这些文件收集到一个项目中并将它们传递给这样的自定义任务:

<UsingTask TaskName="MyCustomTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll" >
  <ParameterGroup>
    <AllOutputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="True"/>
  </ParameterGroup>
  <Task>
    <Code Type="Fragment" Language="cs">
    //code here
    </Code>
  </Task>
</UsingTask>

<Target Name="CustomTarget" BeforeTargets="Link">
  <ItemGroup>
    <AllOutputFiles Include="@(Link->MetaData('ProgramDatabaseFile')->FullPath()->Distinct());@(Link->MetaData('OutputFile')->FullPath()->Distinct())"/>
  </ItemGroup>
  <Message Text="AllOutputFiles = @(AllOutputFiles)" />
  <MyCustomTask AllOutputFiles="@(AllOutputFiles)" />
</Target>

BeforeTargets="Link" 部分确保这个 运行s 在链接器之前。如果由于某种原因为时已晚,您可以使用许多其他目标:再次查看详细输出并选择一个。