如何在msbuild项目文件内使用不同参数两次调用相同的msbuild目标

16

我有下面这段msbuild代码:

  <PropertyGroup>
    <DirA>C:\DirA\</DirA>
    <DirB>C:\DirB\</DirB>
  </PropertyGroup>

  <Target Name="CopyToDirA"
          Condition="Exists('$(DirA)') AND '@(FilesToCopy)' != ''"
          Inputs="@(FilesToCopy)"
          Outputs="@(FilesToCopy -> '$(DirA)%(Filename)%(Extension)')">
    <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(DirA)" />
  </Target>

  <Target Name="CopyToDirB"
          Condition="Exists('$(DirB)') AND '@(FilesToCopy)' != ''"
          Inputs="@(FilesToCopy)"
          Outputs="@(FilesToCopy -> '$(DirB)%(Filename)%(Extension)')">
    <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(DirB)" />
  </Target>

  <Target Name="CopyFiles" DependsOnTargets="CopyToDirA;CopyToDirB"/>

调用目标CopyFiles将相关文件复制到$(DirA)$(DirB),前提是它们不存在或不是最新的。

但是CopyToDirACopyToDirB目标看起来完全相同,除了一个将文件复制到$(DirA),另一个则是复制到$(DirB)。是否可能将它们统一到一个目标中,首先使用$(DirA)调用,然后使用$(DirB)调用?

谢谢。

3个回答

15

你应该能够生成一个包含Dirs的ItemGroup,然后在其上执行%操作。

<ItemGroup>
    <Dirs Include="C:\DirA\;C:\DirB\">
</ItemGroup>
<Target Name="CopyFiles"
    Condition="Exists('%(Dirs)') AND '@(FilesToCopy)' != ''"
    Inputs="@(FilesToCopy)"
    Outputs="@(FilesToCopy -> '%(Dirs)%(Filename)%(Extension)')">
    <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="%(Dirs)" />
</Target>

或者你可以进行两次显式调用:

<Target Name="CopyFiles">
    <MsBuild Projects="$(MSBuildProjectFullPath)" Targets="CopyASetOfFiles" Properties="FilesToCopy=@(FilesToCopy);DestDir=$(DirA)" />
    <MsBuild Projects="$(MSBuildProjectFullPath)" Targets="CopyASetOfFiles" Properties="FilesToCopy=@(FilesToCopy);DestDir=$(DirB)" />
</Target>

<Target Name="CopyASetOfFiles"
    Condition="Exists('$(DestDir)') AND '@(FilesToCopy)' != ''"
    Inputs="@(FilesToCopy)"
    Outputs="@(FilesToCopy -> '$(DestDir)%(Filename)%(Extension)')">
    <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(DestDir)" />
</Target>

我没有测试过这两种语法,但是对第二种语法相对更有信心。

(如果有答案的话,它在我桌子上的Sayed Hashimi书中 - 你必须等到以下三个事件中的第一个:

  1. 拿到这本书
  2. 我感到无聊
  3. Sayed发现了这篇帖子,并提供了一个经过测试的绝妙答案)

嗨,Ruben。你能在回复中详细解释一下...吗?我对参数化目标不是很熟悉,无法理解你的意思。谢谢。 - mark
@mark:完成。我之前没有做的原因有两个,一是手头没有好的编辑器,二是不确定第一个示例的语法。希望这可以帮到你。强烈推荐《Inside MSBuild》这本书。 - Ruben Bartelink
我认为解决方案2存在问题。如果您的构建脚本是通过命令行调用特定属性,则在使用<MsBuild>任务时不会传递这些属性。 - Pete
我已经将$(MSBuildProjectFile)替换为$(MSBuildProjectFullPath),以使复制文件目标清晰明确是为了另一个MSBuild分叉调用正下方的Target(除非有InitialTargets在播放中),因此将运行的唯一目标是CopyASetOfFiles,它仅需要传递的两个属性(即FilesToCopyDestDir。关于擦亮我的答案而言,最重要的事情是BuildInParallel="true"Dirs上的% 批处理操作,以使它们可以并行化。希望您同意我的推理,并且我没有漏掉任何东西! - Ruben Bartelink
@Pete:解决方案#2的主要问题在于,如果唯一关注的是复制文件和/或可以批处理的任何操作,则它是无意义的琐事 - 需要复制粘贴并稍微修改目标或分叉另一个MSBuild的情况比人们想象的要少得多 - 也就是说,#1更符合习惯 :D - Ruben Bartelink

9

4
是的,您需要的是在MSBuild中进行批处理。
;%(Dirs.Identity)

在“输出”中定义,将导致该任务针对Dirs ItemGroup中的每个项目执行。

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="CopyFiles"
   xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
   ToolsVersion="3.5">

<ItemGroup>
    <Dirs Include="C:\DirA" />
    <Dirs Include="C:\DirB" />
</ItemGroup>

<Target Name="CopyFiles"
    Inputs="@(FilesToCopy);@(Dirs)"
    Outputs="@(FilesToCopy -> '%(Dirs.Identity)%(Filename)%(Extension)');%(Dirs.Identity)" >
    <Message Text="%(Dirs.Identity)" />
</Target>

</Project>

输出:

Build started 8/19/2009 10:11:57 PM.
Project "D:\temp\test.proj" on node 0 (default targets).
  C:\DirA
CopyFiles:
  C:\DirB
Done Building Project "D:\temp\test.proj" (default targets).

将消息任务更改为复制任务,并使用以下条件即可完成:
Condition="Exists('%(Dirs.Identity)') AND '@(FilesToCopy)' != ''"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接