2009-05-08 14 views
20

Czy jest możliwe zmodyfikowanie metadanych ItemGroup po zadeklarowaniu.Modyfikuj MSBuild ItemGroup Metadata

Dla przykładu:

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

Mam nadzieję, że istnieje sposób, który nie wymaga mnie aby usunąć element ponownie zadeklarować.

Dzięki za wszelkie odpowiedzi. Wulkan

Odpowiedz

2

miałem napisać niestandardowe zadanie to zrobić:

Oto jak to działa

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

wypełnia nowy przedmiot o nazwie SolutionToBuildTemp o wartości zmieniło. Następnie usuwam wszystko z elementu SolutionToBuild i uzupełniam go w ItemToBuildTemp.

Oto kod zadania, jeśli ktoś jest zainteresowany (przesłałem go również do 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; 
     } 
    } 
} 

Mam nadzieję, że jest to przydatne dla kogoś, ale kod jest oczywiście "Użyj na własne ryzyko".

Vaccano

+0

To zadanie zostało dodane do zadań rozszerzenia MSBuild. – Vaccano

1

Nie można modyfikować istniejącej pozycji, ale można utworzyć nową listę.

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

Tak ty może zmodyfikować lub dodać do <ItemGroup> „s metadanych po to jest zdefiniowana (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> 

referencyjny: MSDN Library: New Methods for Manipulating Items and Properties (MSBuild)

+1

Uwaga że trzeba być w środku cel, aby to zrobić –

+0

Zain ma rację, stały kod, aby pokazać, że. – Jonathan

2

Teraz manipulacja lokalnie w celu działa ...

Jak o nadpisaniu globalnego de finycje, np.

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

Kiedy sprawdzić zawartość% (TestFiles) tutaj otrzymamy:

1) Początkowo: a.test b.test

2) w ramach celu "Preprocesuj" my otrzymujemy: a.test c.test

3) po opuszczeniu cel "Preprocesuj" mamy jeszcze: a.test b.test

Czy istnieje sposób, że 3) wygeneruje takie samo wyjście jak 2)?

To naprawdę uprościłoby wiele rzeczy, np. wykluczyć kod w określonych katalogach z kompilacji itp.

Cheers, Nomad

+0

To powinno być nowe pytanie. Napisałem zadanie, które zostało dodane do MSBuild Extension Pack (http://www.codeplex.com/MSBuildExtensionPack), aby wykonać aktualizację. Zbyt dawno temu pamiętałem, czy zadziała w scenariuszu, który opisałeś powyżej, ale polecam spróbować. (Nazywa się UpdateMetadata w klasie MSBuild.ExtensionPack.Framework.MSBuildHelper. – Vaccano

1

W odpowiedzi na Nomad, wydaje się, że cel dostaje kopię aktualnej wartości nieruchomości i przedmiotów, gdy cel jest tzw. W twoim przykładzie możesz rozwiązać problem, wywołując DefaultTarget po zakończeniu celu PreProcess. Jednym ze sposobów, aby to zrobić byłoby określić, że DefaultTarget zależy od docelowej Preprocesuj:

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

1

Próbowałem za pomocą UpdateMetaData TaskAction od zadania MSBuildHelper w rozszerzeniach 4,0 pakować ale nie robić to, co ja oczekiwano więc poszedłem z metodą usuwania/zamiany. W tym przykładzie próbuję zaktualizować właściwość metadata DestinationRelativePath w grupie elementów FilesForPackagingFromProject.

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