是否可以在声明后修改ItemGroup的元数据。
例如:
<ItemGroup>
<SolutionToBuild Include="$(BuildProjectFolderPath)\MySolution.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>
</ItemGroup>
<Target Name="BuildNumberOverrideTarget">
<!--Code to get the version number from a file (removed)-->
<!--Begin Pseudo Code-->
<CodeToChangeItemGroupMetaData
ItemToChange="%(SolutionToBuild.Properties)"
Condition ="'%(SolutionToBuild.Identity)' ==
'$(BuildProjectFolderPath)\MySolution.sln'"
NewValue="Version=$(Version)" />
<!--End Pseudo Code-->
</Target>
我希望有一种方法不需要我删除该项目然后重新声明它。
感谢您的回答。 Vaccano
答案 0 :(得分:40)
是的,可以修改或添加<ItemGroup>
元数据后的数据(MSBuild 3.5)
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Define ItemGroup -->
<ItemGroup>
<TestItemGroup Include="filename.txt">
<MyMetaData>Test meta data</MyMetaData>
</TestItemGroup>
<TestItemGroup Include="filename2.txt">
<MyMetaData>Untouched</MyMetaData>
</TestItemGroup>
</ItemGroup>
<Target Name="ModifyTestItemGroup" BeforeTargets="Build">
<!-- Show me-->
<Message Text="PRE: %(TestItemGroup.Identity) MyMetaData:%(TestItemGroup.MyMetaData) OtherMetaData:%(TestItemGroup.OtherMetaData)" Importance="high" />
<!-- Now change it - can only do it inside a target -->
<ItemGroup>
<TestItemGroup Condition="'%(TestItemGroup.MyMetaData)'=='Test meta data' AND 'AnotherCondition'=='AnotherCondition'">
<MyMetaData>Well adjusted</MyMetaData>
<OtherMetaData>New meta data</OtherMetaData>
</TestItemGroup>
</ItemGroup>
<!-- Show me the changes -->
<Message Text="POST: %(TestItemGroup.Identity) MyMetaData:%(TestItemGroup.MyMetaData) OtherMetaData:%(TestItemGroup.OtherMetaData)" Importance="high" />
</Target>
<Target Name="Build" />
</Project>
参考:MSDN Library: New Methods for Manipulating Items and Properties (MSBuild)
答案 1 :(得分:2)
我必须编写一个自定义任务来执行此操作:
以下是它的工作原理
<ItemGroup>
<ItemsToChange Include="@(SolutionToBuild)">
<Properties>ChangedValue</Properties>
</ItemsToChange>
<MetaDataToChange Include="Properties"/>
</ItemGroup>
<UpdateMetadata SourceList="@(SolutionToBuild)" ItemsToModify="@(ItemsToChange)" MetadataToModify="@(MetaDataToChange)">
<Output TaskParameter="NewList" ItemName="SolutionToBuildTemp" />
</UpdateMetadata>
<ItemGroup>
<SolutionToBuild Remove="@(SolutionToBuild)"/>
<SolutionToBuild Include ="@(SolutionToBuildTemp)"/>
</ItemGroup>
它使用更改的值填充一个名为SolutionToBuildTemp的新项。然后我删除SolutionToBuild项目中的所有内容并使用SolutionToBuildTemp项填充它。
如果有人感兴趣的话,这是任务的代码(我确实将它提交给了MSBuildExtenstionPack)。
// By Stephen Schaff (Vaccano).
// Free to use for your code. Need my Permission to Sell it.
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace UpdateMetadata
{
///<summary>
/// Used to update the metadata in a ItemGroup (Note: Requires an MSBuild Call After using this task to complete the update. See Usage.)
/// Usage:
/// <?xml version="1.0" encoding="utf-8"?>
///<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Testing" ToolsVersion="3.5">
///
/// <!-- Do not edit this -->
/// <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
/// <UsingTask AssemblyFile="C:\Base\Junk\UpdateMetadata\UpdateMetadata\bin\Debug\UpdateMetadata.dll" TaskName="UpdateMetadata"/>
///
///
/// <!--Re-setup the solutions to build definition-->
/// <ItemGroup>
/// <SolutionToBuild Include="$(BuildProjectFolderPath)\ChangeThisOne.sln">
/// <Properties>Change</Properties>
/// </SolutionToBuild>
/// <SolutionToBuild Include="$(BuildProjectFolderPath)\ChangeThisToo.sln">
/// <Properties>Change</Properties>
/// </SolutionToBuild>
/// <SolutionToBuild Include="$(BuildProjectFolderPath)\DontChangeThisOne.sln">
/// <Properties>Don'tChange</Properties>
/// </SolutionToBuild>
/// </ItemGroup>
///
/// <Target Name="Testing">
/// <Message Text="Before = %(SolutionToBuild.Identity) %(SolutionToBuild.Properties)" />
///
/// <ItemGroup>
/// <ItemsToChange Include="@(SolutionToBuild)">
/// <Properties>ChangedValue</Properties>
/// </ItemsToChange>
///
/// <ItemsToChange Remove="%(ItemsToChange.rootdir)%(ItemsToChange.directory)DontChangeThisOne%(ItemsToChange.extension)"/>
/// </ItemGroup>
///
/// <ItemGroup>
/// <MetaDataToChange Include="Properties"/>
/// </ItemGroup>
///
/// <UpdateMetadata SourceList="@(SolutionToBuild)" ItemsToModify="@(ItemsToChange)" MetadataToModify="@(MetaDataToChange)">
/// <Output TaskParameter="NewList" ItemName="SolutionToBuildTemp" />
/// </UpdateMetadata>
///
/// <ItemGroup>
/// <SolutionToBuild Remove="@(SolutionToBuild)"/>
/// <SolutionToBuild Include ="@(SolutionToBuildTemp)"/>
/// </ItemGroup>
///
/// <Message Text="After = %(SolutionToBuild.Identity) %(SolutionToBuild.Properties)"/>
/// </Target>
///</Project>
///</summary>
public class UpdateMetadata : Task
{
///<summary>
/// The list to modify.
///</summary>
[Required]
public ITaskItem[] SourceList { get; set; }
///<summary>
/// Items in <see cref="SourceList"/> to change the Metadata for.
/// It should have the valid metadata set.
///</summary>
[Required]
public ITaskItem[] ItemsToModify { get; set; }
///<summary>
/// List of metadata to modify. This is an item group, but any metadata in it is ignored.
///</summary>
public ITaskItem[] MetadataToModify { get; set; }
///<summary>
/// If true then info about the update is output
///</summary>
public Boolean OutputMessages { get; set; }
///<summary>
/// Changed List. If you call the following it can replace the <see cref="SourceList"/>:
///</summary>
[Output]
public ITaskItem[] NewList { get; set; }
///<summary>
/// Runs the task to output the updated version of the property
///</summary>
///<returns></returns>
public override bool Execute()
{
// If we got empty params then we are done.
if ((SourceList == null) || (ItemsToModify == null) || (MetadataToModify == null))
{
Log.LogMessage("One of the inputs to ModifyMetadata is Null!!!", null);
return false;
}
if (OutputMessages)
Log.LogMessage(MessageImportance.Low, "Beginning Metadata Changeover", null);
int sourceIndex = 0;
foreach (ITaskItem sourceItem in SourceList)
{
// Fill the new list with the source one
NewList = SourceList;
foreach (ITaskItem itemToModify in ItemsToModify)
{
// See if this is a match. If it is then change the metadat in the new list
if (sourceItem.ToString() == itemToModify.ToString())
{
foreach (ITaskItem metadataToModify in MetadataToModify)
{
try
{
if (OutputMessages)
Log.LogMessage(MessageImportance.Low, "Changing {0}.{1}",
NewList[sourceIndex].ToString(), metadataToModify.ToString());
// Try to change the metadata in the new list.
NewList[sourceIndex].SetMetadata(metadataToModify.ToString(),
itemToModify.GetMetadata(metadataToModify.ToString()));
}
catch (System.ArgumentException exception)
{
// We got some bad metadata (like a ":" or something).
Log.LogErrorFromException(exception);
return false;
}
}
}
}
sourceIndex += 1;
}
return true;
}
}
}
我希望这对某些人有用,但代码显然是“使用风险自负”。
Vaccano
答案 2 :(得分:2)
现在,目标中的本地操作有效......
如何覆盖全局定义,例如
<Project ...>
<ItemGroup>
<TestFiles Include="a.test" />
<TestFiles Include="b.test" />
</ItemGroup>
<Target Name="DefaultTarget">
<Message Text="Files befor change ItemGroup:" />
<Message Text="%(TestFiles.Identity)" />
<CallTarget Targets="PreProcess" />
<Message Text="Files after change ItemGroup:" />
<Message Text="%(TestFiles.Identity)" />
</Target>
<Target Name="PreProcess">
<ItemGroup>
<TestFiles Remove="b.test" />
</ItemGroup>
<CreateItem Include="c.test">
<Output TaskParameter="Include" ItemName="TestFiles" />
</CreateItem>
<Message Text="Files after change ItemGroup (local in target):" />
<Message Text="%(TestFiles.Identity)" />
</Target>
</Project>
当我们在这里检查%(TestFiles)的内容时,我们将得到:
1)最初: 一个测试 b.test
2)在目标“PreProcess”中,我们得到: 一个测试 C.TEST
3)离开目标“PreProcess”后我们再次: 一个测试 b.test
那么有没有一种方法可以产生与2)相同的输出?
这会真正简化许多事情,例如:从编译等中排除特定目录中的代码。
干杯, 游牧
答案 3 :(得分:1)
无法修改现有项目,但您可以创建新列表。
<CreateItem Include="@(SolutionToBuild)"
AdditionalMetadata="Version=$(Version)" >
<Output ItemName="SolToBuildMods" TaskParameter="Include" />
</CreateItem>
<Message Text="%(SlnToBuildMods.Identity) %(SlnToBuildMods.Version)" />
答案 4 :(得分:1)
作为对Nomad的回应,当调用目标时,目标似乎获得属性和项目的当前值的副本。在您的示例中,您可以通过在PreProcess目标完成后调用DefaultTarget来解决问题。一种方法是指定DefaultTarget依赖于PreProcess目标:
<Target Name="DefaultTarget" DependsOnTargets="PreProcess">
...
</Target>
答案 5 :(得分:1)
我尝试使用4.0扩展包中的MSBuildHelper任务的UpdateMetaData TaskAction,但它没有按照我的预期执行,所以我使用了remove / replace方法。在这个例子中,我试图更新FilesForPackagingFromProject项目组中的DestinationRelativePath元数据属性。
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
$(CopyAllFilesToSingleFolderForPackageDependsOn);
SetFilePathsToRoot;
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="SetFilePathsToRoot">
<Message Text="Stripping \bin directory from package file paths" />
<!-- Tweak the package files' DestinationRelativePath property such that binaries don't go into a \bin directory -->
<ItemGroup>
<ModifiedFilesForPackagingFromProject Include="@(FilesForPackagingFromProject)">
<DestinationRelativePath>%(FileName)%(Extension)</DestinationRelativePath>
</ModifiedFilesForPackagingFromProject>
</ItemGroup>
<ItemGroup>
<FilesForPackagingFromProject Remove="@(FilesForPackagingFromProject)" />
<FilesForPackagingFromProject Include="@(ModifiedFilesForPackagingFromProject)" />
</ItemGroup>
</Target>
</Project>
答案 6 :(得分:0)
有一个新的使用 Update 属性修改元数据的方法 例如
<ItemGroup>
<Compile Update="somefile.cs"> // or Update="*.designer.cs"
<MetadataKey>MetadataValue</MetadataKey>
</Compile>
</ItemGroup>
中的更多内容