为什么$([System.Text.RegularExpressions.Regex] :: IsMatch())在ItemGroupDefinition中评估一次? [英] Why is $([System.Text.RegularExpressions.Regex]::IsMatch()) evaluated once in ItemGroupDefinition?
问题描述
如此摆弄MSBuild任务,我发现一次对Regex元数据属性进行评估,而不是对每个项进行评估.
So fiddling with MSBuild tasks, and I am finding that a Regex metadata property is evaluated once rather than per item.
例如
<!--
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'
推荐答案
问题
文档 ItemDefinitionGroup元素(MSBuild)引用项目定义,其中有一条注释哪个状态:
Problem
The documentation ItemDefinitionGroup Element (MSBuild) refers to Item Definitions which has a note which states:
ItemGroup中的项目元数据在ItemDefinitionGroup元数据声明中没有用,因为ItemDefinitionGroup元素先于ItemGroup元素进行处理.
Item metadata from an ItemGroup is not useful in an ItemDefinitionGroup metadata declaration because ItemDefinitionGroup elements are processed before ItemGroup elements.
这意味着您无法扩展<ItemDefinitionGroup/>
中的%(Filename)
元数据引用.您可以通过以下代码片段自己查看.在代码段中,.ToString()
调用将结果转换为字符串对象,从而阻止MSBuild对其进行进一步扩展. (如果我遗漏了.ToString()
,则MSBuild将剩下一个System.RegularExpressions.Match
对象.将元数据定义保留为Match
对象似乎会延迟扩展到字符串,直到评估<Message/>
的Text
,导致MSBuild对它进行字符串扩展传递,从而导致%(Identity)
在您可能不希望的情况下被扩展.在以下代码段中也演示了这种延迟的扩展.)
This means that your %(Filename)
metadata reference in the <ItemDefinitionGroup/>
cannot be expanded. You can see this yourself with this following snippet. In the snippet, the .ToString()
call converts the result to a string object, preventing MSBuild from further expanding it. (If I had left .ToString()
out, MSBuild would have been left with a System.RegularExpressions.Match
object. Leaving the metadata definition as a Match
object seems to delay expansion to string until the <Message/>
’s Text
is evaluated, causing MSBuild to do a string expansion pass over it, resulting in %(Identity)
being expanded when you might not expect it to be. This delayed expansion is also demonstrated in the following snippet.)
<?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文档中的注释表明,<ItemDefinitionGroup/>
中不提供项目元数据.通过使用Regex.Match()
,似乎属性函数扩展会将%(Identity)
或您的情况下的%(Filename)
视为未引用的自由格式字符串.因此,由于您使用与上述示例中调用Regex.Match()
相同的语法来调用Regex.IsMatch()
,因此您的Regex.IsMatch()
试图检查文字字符串%(Filename)
是否包含x8
(可选地,后面跟任意存在或不影响比赛的6s数.
The note from MSBuild’s documentation indicates that Item metadata is not available in <ItemDefinitionGroup/>
. It appears, from using Regex.Match()
, that the property function expansion is treating %(Identity)
or, in your case, %(Filename)
as an unquoted free-form string. Thus, since you invoke Regex.IsMatch()
with the same syntax as I invoke Regex.Match()
in the above example, it follows that your Regex.IsMatch()
is trying to check if the literal string %(Filename)
contains x8
(optionally followed by any number of 6s whose presence or absence will not affect the match).
据我所知,根据现有元数据动态计算商品的元数据的唯一方法是创建一个从原始商品派生的新商品.例如,要创建包含所需元数据的<ProjectReference/>
列表,可以使用以下项目定义生成ProjectReferenceWithArch
项目.我选择使用属性函数语法使用 [MSBuild]::ValueOrDefault()
后在属性扩展上下文中将其转换为字符串,以便我可以使用String.Contains()
(对于您的情况,Regex有点过大,但是如果需要,您可以轻松地对其进行修改以与正则表达式匹配).我更新了您的<Message/>
以打印出Project
元数据,以证明该元数据可以保留到新商品的定义中.
The only way I know of to dynamically calculate an Item’s metadata based on existing metadata is to create a new Item derived from the original item. For example, to create a list of <ProjectReference/>
s with the metadata you need, you could use the following item definition to produce ProjectReferenceWithArch
Items. I chose to use property function syntax after using [MSBuild]::ValueOrDefault()
to turn it into a string in property expansion context so that I could use String.Contains()
(Regex is a bit overkill for your case, but you could easily modify this to match against a regular expression if needed). I updated your <Message/>
to print out the Project
metadata to demonstrate that this metadata survives into the new Item’s definition.
<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)
我刚刚注意到您可以在<Target/>
中动态更新商品的元数据.语法如下:
Alternative Solution (EDIT)
I just noticed that you can dynamically update an Item’s metadata if you do so in a <Target/>
. The syntax looks like this:
<Target Name="AdditionalTasks">
<ItemGroup>
<ProjectReference>
<Isx86>$([MSBuild]::ValueOrDefault('%(Filename)', '').Contains('x86'))</Isx86>
</ProjectReference>
</ItemGroup>
</Target>
只需确保此目标在您需要检查Isx86
元数据的目标之前运行,或者确保<ItemGroup/>
在<Target/>
中需要该元数据之前出现.
Just ensure that this target runs before the target in which you need to check the Isx86
metadata or that the <ItemGroup/>
appears before the metadata is needed in your <Target/>
.
这篇关于为什么$([System.Text.RegularExpressions.Regex] :: IsMatch())在ItemGroupDefinition中评估一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!