2

そのため、MSBuild タスクをいじっていると、Regex メタデータ プロパティがアイテムごとではなく 1 回評価されることがわかりました。

例えば

<!-- 
  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 要素 (MSBuild)は、次のようなメモを持つ項目定義を参照しています。

ItemDefinitionGroup 要素は ItemGroup 要素の前に処理されるため、ItemGroup からのアイテム メタデータは ItemDefinitionGroup メタデータ宣言では役に立ちません。

これは、 の%(Filename)メタデータ参照を<ItemDefinitionGroup/>展開できないことを意味します。これは、次のスニペットで確認できます。スニペットでは、.ToString()呼び出しによって結果が文字列オブジェクトに変換され、MSBuild がそれをさらに展開できなくなります。(私が省略していた場合.ToString()、MSBuild にはオブジェクトが残っていたでしょうSystem.RegularExpressions.Match。メタデータ定義をオブジェクトとして残すと、が評価されるまで文字列へのMatch展開が遅れるように見え、MSBuild が文字列展開パスを実行し、その結果、予期しないときに展開されます。この遅れた展開は、次のスニペットでも示されています。)<Message/>Text%(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/>。を使用するRegex.Match()と、プロパティ関数の展開が処理されているように見え%(Identity)ます。または、あなたの場合は%(Filename)、引用符で囲まれていない自由形式の文字列として扱われます。したがって、上記の例でRegex.IsMatch()呼び出したのと同じ構文で呼び出すため、リテラル文字列に含まれているかどうかを確認しようとしているということになります(必要に応じて、存在または不在が一致に影響しない任意の数の 6 が続きます)。Regex.Match()Regex.IsMatch()%(Filename)x8

解決

既存のメタデータに基づいてアイテムのメタデータを動的に計算する唯一の方法は、元のアイテムから派生した新しいアイテムを作成することです。たとえば、<ProjectReference/>必要なメタデータを含む のリストを作成するには、次のアイテム定義を使用してアイテムを生成できますProjectReferenceWithArch使用できるように、使用後にプロパティ関数構文を使用[MSBuild]::ValueOrDefault()して、プロパティ展開コンテキストで文字列に変換することを選択しましたString.Contains()(正規表現はあなたのケースでは少しやり過ぎですが、必要に応じて正規表現と一致するように簡単に変更できます)。このメタデータが新しいアイテムの定義に残ることを示す<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 に答える