【问题标题】:Modify MSBuild ItemGroup Metadata修改 MSBuild ItemGroup 元数据
【发布时间】:2009-05-08 21:34:34
【问题描述】:

是否可以在声明后修改 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>

我希望有一种方法不需要我删除该项目然后重新声明它。

感谢您的任何回答。 瓦卡诺

【问题讨论】:

    标签: msbuild


    【解决方案1】:

    是的,您可以在定义 &lt;ItemGroup&gt; 的元数据后修改或添加它(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)

    【讨论】:

    • 请注意,您必须在目标内部才能执行此操作
    【解决方案2】:

    有一个新的使用 Update 属性来修改元数据 例如

    <ItemGroup>
        <Compile Update="somefile.cs">  // or Update="*.designer.cs"
            <MetadataKey>MetadataValue</MetadataKey>
        </Compile>
    </ItemGroup>  
    

    更多内容请关注MSBuild documentation

    【讨论】:

      【解决方案3】:

      我必须编写一个自定义任务来执行此操作:

      这是它的工作原理

      <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:
          /// &lt;?xml version="1.0" encoding="utf-8"?&gt;
          ///&lt;Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Testing" ToolsVersion="3.5"&gt;
          /// 
          ///  &lt;!-- Do not edit this --&gt;
          ///  &lt;Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" /&gt;
          ///  &lt;UsingTask AssemblyFile="C:\Base\Junk\UpdateMetadata\UpdateMetadata\bin\Debug\UpdateMetadata.dll" TaskName="UpdateMetadata"/&gt;
          /// 
          /// 
          ///  &lt;!--Re-setup the solutions to build definition--&gt;
          ///  &lt;ItemGroup&gt;
          ///    &lt;SolutionToBuild Include="$(BuildProjectFolderPath)\ChangeThisOne.sln"&gt;
          ///      &lt;Properties&gt;Change&lt;/Properties&gt;
          ///    &lt;/SolutionToBuild&gt;
          ///    &lt;SolutionToBuild Include="$(BuildProjectFolderPath)\ChangeThisToo.sln"&gt;
          ///      &lt;Properties&gt;Change&lt;/Properties&gt;
          ///    &lt;/SolutionToBuild&gt;
          ///    &lt;SolutionToBuild Include="$(BuildProjectFolderPath)\DontChangeThisOne.sln"&gt;
          ///      &lt;Properties&gt;Don'tChange&lt;/Properties&gt;
          ///    &lt;/SolutionToBuild&gt;
          ///  &lt;/ItemGroup&gt;
          /// 
          ///  &lt;Target Name="Testing"&gt;
          ///    &lt;Message Text="Before = %(SolutionToBuild.Identity) %(SolutionToBuild.Properties)" /&gt;
          /// 
          ///    &lt;ItemGroup&gt;
          ///      &lt;ItemsToChange Include="@(SolutionToBuild)"&gt;
          ///        &lt;Properties&gt;ChangedValue&lt;/Properties&gt;
          ///      &lt;/ItemsToChange&gt;
          ///   
          ///      &lt;ItemsToChange Remove="%(ItemsToChange.rootdir)%(ItemsToChange.directory)DontChangeThisOne%(ItemsToChange.extension)"/&gt;      
          ///    &lt;/ItemGroup&gt;
          /// 
          ///    &lt;ItemGroup&gt;
          ///      &lt;MetaDataToChange Include="Properties"/&gt;
          ///    &lt;/ItemGroup&gt;
          /// 
          ///    &lt;UpdateMetadata SourceList="@(SolutionToBuild)" ItemsToModify="@(ItemsToChange)" MetadataToModify="@(MetaDataToChange)"&gt;
          ///      &lt;Output TaskParameter="NewList" ItemName="SolutionToBuildTemp" /&gt;
          ///    &lt;/UpdateMetadata&gt;
          /// 
          ///    &lt;ItemGroup&gt;
          ///      &lt;SolutionToBuild Remove="@(SolutionToBuild)"/&gt;
          ///      &lt;SolutionToBuild Include ="@(SolutionToBuildTemp)"/&gt;
          ///    &lt;/ItemGroup&gt;
          ///         
          ///    &lt;Message Text="After  = %(SolutionToBuild.Identity) %(SolutionToBuild.Properties)"/&gt;
          ///  &lt;/Target&gt;
          ///&lt;/Project&gt;
          ///</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;
              }
          }
      }
      

      我希望这对某些人有用,但代码显然是“使用风险自负”。

      瓦卡诺

      【讨论】:

      • 此任务已添加到 MSBuild 扩展任务中。
      【解决方案4】:

      无法修改现有项目,但您可以创建新列表。

      <CreateItem Include="@(SolutionToBuild)"  
                  AdditionalMetadata="Version=$(Version)" >
            <Output ItemName="SolToBuildMods" TaskParameter="Include" />
      </CreateItem>
      <Message Text="%(SlnToBuildMods.Identity) %(SlnToBuildMods.Version)" />
      

      【讨论】:

        【解决方案5】:

        作为对 Nomad 的响应,似乎在调用目标时,目标会获取属性和项目的当前值的副本。在您的示例中,您可以通过在 PreProcess 目标完成后调用 DefaultTarget 来解决问题。一种方法是指定 DefaultTarget 取决于 PreProcess 目标:

        <Target Name="DefaultTarget" DependsOnTargets="PreProcess"> ... </Target>

        【讨论】:

          【解决方案6】:

          我尝试使用 4.0 扩展包中 MSBuildHelper 任务中的 UpdateMetaData TaskAction,但它没有达到我的预期,因此我使用了删除/替换方法。在此示例中,我尝试更新 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>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-10-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-08-24
            • 2011-10-07
            相关资源
            最近更新 更多