2012-12-31 82 views
2

因此,與MSBuild任務擺弄,並且我發現,一個正則表達式的元數據屬性被計算一次,而不是每個項目。

例如

<!-- 
    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

問題

文檔ItemDefinitionGroup Element (MSBuild)Item Definitions具有音符其中指出:

來自ItemGroup的項目元數據在ItemDefinitionGroup元數據聲明中無用,因爲ItemDefinitionGroup元素是在ItemGroup元素之前處理的。

這意味着您的%(Filename)元數據在<ItemDefinitionGroup/>中的引用無法展開。你可以用下面的代碼片段自己看看。在代碼片段中,.ToString()調用將結果轉換爲字符串對象,阻止MSBuild進一步擴展它。 (如果我離開了.ToString(),MSBuild將留下一個System.RegularExpressions.Match對象。將元數據定義保留爲Match對象似乎將延遲擴展爲字符串,直到對<Message/>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」 

的音符從MS Build的文檔表明,項目元數據在<ItemDefinitionGroup/>中不可用。它的出現,使用Regex.Match(),該property function expansion被處理%(Identity)或者,你的情況,作爲%(Filename)不帶引號的自由形式的字符串。因此,由於您調用Regex.IsMatch()的語法與我在上例中調用Regex.Match()的語法相同,因此您的Regex.IsMatch()正試圖檢查文字字符串%(Filename)是否包含x8(可選地後跟任意數量的6,其存在或不存在不會影響比賽)。

解決方案

我知道的,動態計算基於現有的元數據項目的元數據的唯一方法是創建一個從原始項目派生的新項目。例如,要創建的<ProjectReference/> s的你所需要的元數據的列表,你可以使用以下項目的定義,產生ProjectReferenceWithArch項目。我選擇使用[MSBuild]::ValueOrDefault()把它變成財產擴張背景下的字符串,這樣我可以用String.Contains()後使用property function syntax(正則表達式是你的情況有點矯枉過正,但你可以很容易地修改,如果需要這個來匹配一個正則表達式)。我更新了<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}' 

替代解決方案(EDIT)

我只注意到that you can dynamically update an Item’s metadata if you do so in a <Target/>。語法如下:

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

只要確保這個目標,你需要檢查Isx86元的目標之前,或者在<ItemGroup/>需要在你的<Target/>元數據出現之前運行。

+0

哦,男人,對不起,我錯過了這個優秀的迴應!謝謝,對不起,延遲! –

+1

@johnnyg沒問題。我遲到了半年(因爲我只是在困惑的時候才發現你的問題),而且我對答案的前半部分甚至沒有很好的把握,因爲我沒有花太多時間使用MSBuild,並且發現它很令人困惑仍然。 – binki