2

如此摆弄 MSBuild 任务,我发现 Regex 元数据属性被评估一次,而不是每个项目。

例如

<!-- 
  actual items, we use standard project reference items and extend via 
  ItemDefinitionGroup. add project references through IDE to extend 
  coverage
-->
<ItemGroup>
  <ProjectReference Include="..\Example.UnitTests-x86\Example.UnitTests-x86.csproj">
    <Project>{7e854803-007c-4800-80f9-be908655229d}</Project>
    <Name>Example.UnitTests-x86</Name>
  </ProjectReference>
  <ProjectReference Include="..\Example.UnitTests\Example.UnitTests.csproj">
    <Project>{eaac5f22-bfb8-4df7-a711-126907831a0f}</Project>
    <Name>Example.UnitTests</Name>
  </ProjectReference>
</ItemGroup>

<!-- additional item properties, defined with respect to item declaring it -->
<ItemDefinitionGroup>
  <ProjectReference>
    <Isx86>
      $([System.Text.RegularExpressions.Regex]::IsMatch(%(Filename), '.*x86*'))
    </Isx86>
  </ProjectReference>
</ItemDefinitionGroup>

<!-- additional task target, invoke both x64 and x86 tasks here -->
<Target Name="AdditionalTasks">
  <Message 
    Text="%(ProjectReference.Filename) Isx86 '%(Isx86)' Inline 
    '$([System.Text.RegularExpressions.Regex]::IsMatch(%(Filename), '.*x86*'))'" 
    Importance="high" />
</Target>

产生这个输出

Example.UnitTests-x86 Isx86 'False' Inline 'True'
Example.UnitTests Isx86 'False' Inline 'False'
4

1 回答 1

7

问题

文档ItemDefinitionGroup Element (MSBuild)指的是Item Definitions,其中有一条说明:

ItemGroup 中的项元数据在 ItemDefinitionGroup 元数据声明中没有用,因为 ItemDefinitionGroup 元素在 ItemGroup 元素之前处理。

这意味着您的%(Filename)元数据引用<ItemDefinitionGroup/>无法扩展。您可以通过以下代码片段自己查看。在代码段中,.ToString()调用将结果转换为字符串对象,从而阻止 MSBuild 进一步扩展它。(如果我遗漏.ToString()了,MSBuild 会留下一个System.RegularExpressions.Match对象。将元数据定义保留为Match对象似乎会延迟扩展为字符串,直到评估<Message/>'s Text,导致 MSBuild 对其进行字符串扩展传递,从而%(Identity)导致在您可能没有预料到的时候展开。下面的代码片段也演示了这种延迟的展开。)

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <MyItem Include="MyItem’s value" />
    <MyItem Include="MyItem’s second value" />
  </ItemGroup>
  <ItemDefinitionGroup>
    <MyItem>
      <ItemDefinitionMatchedText>$([System.Text.RegularExpressions.Regex]::Match(%(Identity), ".*").get_Groups().get_Item(0).ToString())</ItemDefinitionMatchedText>
      <ItemDefinitionMatchedTextDelayed>$([System.Text.RegularExpressions.Regex]::Match(%(Identity), ".*").get_Groups().get_Item(0))</ItemDefinitionMatchedTextDelayed>
    </MyItem>
  </ItemDefinitionGroup>
  <Target Name="Build" Outputs="%(MyItem.Identity)">
    <Message Text="Data being matched against for item “%(MyItem.Identity)” is “%(ItemDefinitionMatchedText)”"/>
    <Message Text="Delayed string conversion causes delayed expansion: “%(MyItem.ItemDefinitionMatchedTextDelayed)”"/>
  </Target>
</Project>

输出:

Build:
  Data being matched against for item “MyItem’s value” is “%(Identity)”
  Delayed string conversion causes delayed expansion: “MyItem’s value”
Build:
  Data being matched against for item “MyItem’s second value” is “%(Identity)”
  Delayed string conversion causes delayed expansion: “MyItem’s second value”

MSBuild 文档中的注释表明 Item 元数据在<ItemDefinitionGroup/>. 从 using 看来,Regex.Match()属性函数扩展正在处理%(Identity),或者在您的情况下,%(Filename)将其视为未引用的自由格式字符串。因此,由于您Regex.IsMatch()使用与我在上面示例中调用的相同语法进行调用Regex.Match(),因此您Regex.IsMatch()正在尝试检查文字字符串是否%(Filename)包含x8(可选地后跟任意数量的 6,其存在或不存在都不会影响匹配)。

解决方案

我所知道的根据现有元数据动态计算项目元数据的唯一方法是创建一个从原始项目派生的新项目。例如,要<ProjectReference/>使用您需要的元数据创建一个 s 列表,您可以使用以下项目定义来生成ProjectReferenceWithArch项目。我选择在使用后使用属性函数语法[MSBuild]::ValueOrDefault()将其转换为属性扩展上下文中的字符串,以便我可以使用String.Contains()(对于您的情况,Regex 有点矫枉过正,但如果需要,您可以轻松修改它以匹配正则表达式)。我更新了您<Message/>以打印出Project元数据,以证明此元数据在新项目的定义中仍然存在。

<ItemGroup>
  <ProjectReferenceWithArch Include="@(ProjectReference)">
    <Isx86>$([MSBuild]::ValueOrDefault('%(Filename)', '').Contains('x86'))</Isx86>
  </ProjectReferenceWithArch>
</ItemGroup>
<Target Name="AdditionalTasks">
  <Message 
    Text="%(ProjectReferenceWithArch.Filename) Isx86 '%(Isx86)' Inline '$([System.Text.RegularExpressions.Regex]::IsMatch(%(Filename), '.*x86*'))' Project '%(Project)'" 
    Importance="high" />
</Target>

输出:

AdditionalTasks:
  Example.UnitTests-x86 Isx86 'True' Inline 'True' Project '{7e854803-007c-4800-80f9-be908655229d}'
  Example.UnitTests Isx86 'False' Inline 'False' Project '{eaac5f22-bfb8-4df7-a711-126907831a0f}'

替代解决方案(编辑)

我刚刚注意到,如果您在<Target/>. 语法如下所示:

<Target Name="AdditionalTasks">
  <ItemGroup>
    <ProjectReference>
      <Isx86>$([MSBuild]::ValueOrDefault('%(Filename)', '').Contains('x86'))</Isx86>
    </ProjectReference>
  </ItemGroup>
</Target>

只需确保此目标在您需要检查Isx86元数据的目标之前运行,或者<ItemGroup/>在您的<Target/>.

于 2013-06-19T20:52:23.327 回答