为什么这些MSBuild输出项似乎是空的,当它们不是?

时间:2011-11-08 12:53:00

标签: msbuild msbuild-task

提出问题的奇怪方式,无论如何......基本上这是我对MSBuild的第一次真正尝试。

我设法创建了一个Target( RenameJs ),它生成了一个名为 JsMinifyOutput 的新输出项列表(另一个输入列表的重命名版本)。

然后,在依赖目标( ClosureCompile )中,我试图实际使用先前生成的输出列表。我有一个消息任务,如下所示,我可以看到控制台上打印的输出列表值:

    <Message Text="Outputs: %(JsMinifyOutput.FullPath)" />

但是,在同一个依赖目标中,当我尝试在实际执行任务中使用输出列表时,列表似乎是空的。以下任务:

<Exec Command="$(ClosureCompilerCmd) --js %(JsMinify.FullPath) --js_output_file %(JsMinifyOutput.FullPath)" />

在控制台上显示为:

java -jar Some\Path\To\SomeInputFile.debug.js --js_output_file

虽然我希望有类似的东西:

java -jar Some\Path\To\SomeInputFile.js --js_output_file Some\Path\To\SomeFileRenamed.js

如果我尝试使用这个简单的Exec Task,它会正确显示所有文件名:

<Exec Command="echo %(JsMinifyOutput.FullPath)" />

我在这里要实现的目标如下:从输入ItemGroup列表开始,通过对前者的一些转换(例如重命名,替换文本,...)创建另一个(输出)ItemGroup列表。然后,拥有这两个ItemGroup列表,循环遍历它们并行以便说明,并对它们执行Exec任务。在伪语言中,类似于:

 for(i from 0 to inList.length)
 {
   Exec Command="Path\To\Some\Command --in inList[i] --out outList[i]
 } 

我在做一些完全愚蠢的事情吗? PS:这是使用Build Engine 4.0版,.NET Framework 4.0版

2 个答案:

答案 0 :(得分:1)

嗯,似乎Exec Task不能同时迭代两个 parallel ItemGroups。 ......至少那是MSBuild大师回答的问题here

  

问题是你一次批量处理2件物品。

我不完全确定我在这里要做的是“一次批量处理2件物品”。

修改

清除了任务不起作用的实际原因之后,我的方法(我认为)是一个干净的解决方案:

  1. 将新元数据添加到现有输入ItemGroup。元数据 计算的输出路径并取代前一个输出ItemGroup。在元数据中,您可以找到以某种方式实现的文件路径转换(重命名,替换,...​​)(通过内联任务属性函数转换,你说的名字。)。
  2. 引用使用前一个输出ItemGroup的新元数据,例如:在Exec Command属性内,或在Target Outputs属性内。
  3. 举个例子:

    <Target Name="JsRename"> <!-- This adds the metadata -->
      <ItemGroup>
        <JsMinify>
          <OutputPath>[do something over %(FullPath)</OutputPath>
        </JsMinify>
      </ItemGroup>
    </Target>
    <Target Name="ClosureCompile" DependsOnTargets="JsRename"> <!-- Here is the actual task -->
      <Exec Command="$(ClosureCompilerCmd) --js %(JsMinify.FullPath) --js_output_file %(JsMinify.OutputPath)" />
    </Target>
    

答案 1 :(得分:0)

从我对msbuild的(有限的)经验来看,似乎你不能使用这样的两个列表。只需将它放在Message任务中,你就会看到它在做什么。

您可以使用一个包含元数据的列表,这只需要您从另外两个列表中手动生成交叉产品:

<ItemGroup>
  <JsMinify Include="a"/>
  <JsMinify Include="b"/>
  <JsMinifyOutput Include="d"/>
  <JsMinifyOutput Include="e"/>
</ItemGroup>

<Target Name="CrossProduct">
  <ItemGroup>
    <InPath Include="@(JsMinify->'%(Identity)')">
      <OutPath>%(JsMinifyOutput.Identity)</OutPath>
    </InPath>
  </ItemGroup>
</Target>

<Target Name="Show" DependsOnTargets="CrossProduct">
  <Message Text="%(InPath.Identity) and %(InPath.OutPath)"/>
</Target>

运行它将输出

  a and d
  b and d
  a and e
  b and e

<强>更新 可以通过多种方式进行批处理;这里有一点略有不同,你还必须以另一种方式添加元数据;实际上,遵循循环伪代码,我在下面使用的JsGroup对应于循环计数器。虽然这个样本看起来像你所追求的那样,但遗憾的是我不知道自动生成数字的方法所以它不是真正的解决方案,但我会发布它,因为它足够有趣。

<ItemGroup>
  <JsMinify Include="a">
    <JsGroup>1</JsGroup>
  </JsMinify>
  <JsMinify Include="b">
    <JsGroup>2</JsGroup>
  </JsMinify>

  <JsMinifyOutput Include="d">
    <JsGroup>1</JsGroup>
  </JsMinifyOutput>
  <JsMinifyOutput Include="e">
    <JsGroup>2</JsGroup>
  </JsMinifyOutput>
</ItemGroup>

<Message Text="%(JsGroup): @(JsMinify) and @(JsMinifyOutput)"/>

输出

a and d
b and e

我进一步观察,因为你正在使用msbuild 4,所以可能会开启一个全新的可能性世界。使用相同的ItemGroup及以上,这是一个内联任务,用实际代码调用你的循环:

<UsingTask TaskName="LoopBoth" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
  <ParameterGroup>
    <JsMinify ParameterType="System.String[]" Required="true" />
    <JsMinifyOutput ParameterType="System.String[]" Required="true" />
  </ParameterGroup>
  <Task>
    <Code Type="Fragment" Language="cs">
      <![CDATA[
          for( int i = 0 ; i < JsMinify.Length ; ++i )
          {
            Log.LogMessage( JsMinify[ i ] + " and " + JsMinifyOutput[ i ] );
            System.Diagnostics.Process.Start( "java",
              "-jar " + JsMinify[ i ] + " --js_output_file " + JsMinifyOutput[ i ] );
          }
      ]]>
    </Code>
  </Task>
</UsingTask>

<Target Name="DoIt">
  <LoopBoth JsMinify="@(JsMinify)" JsMinifyOutput="@(JsMinifyOutput)"/>
</Target>

你可以偏离此过程并仅传递JsMinify并在任务中进行转换。