2009-05-08 11 views
20

Es posible modificar los metadatos de un grupo de elementos después de su declaración.Modificar metadatos del grupo de elementos de MSBuild

Por ejemplo:

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

estoy esperando que hay una manera que no me requiere para quitar el elemento continuación, volver a declararlo.

Gracias por cualquier respuesta. Vaccano

Respuesta

2

que tenía que escribir una tarea personalizada para hacer esto:

Aquí es cómo funciona

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

Se llena un nuevo elemento llamado SolutionToBuildTemp con el valor cambiado. Luego elimino todo en el ítem SolutionToBuild y lo llené con el ítem SolutionToBuildTemp.

Aquí está el código para la tarea si alguien está interesado (también lo envié a 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; 
     } 
    } 
} 

Espero que esto sea útil para alguien, pero el código es obviamente "Uso bajo su propio riesgo".

Vaccano

+0

Esta tarea se ha agregado a las tareas de extensión de MSBuild. – Vaccano

1

No es posible modificar un elemento existente, pero puede crear una lista nueva.

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

Sí se puede modificar o añadir a un <ItemGroup> 's meta datos después de que se define (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> 

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

+1

Tenga en cuenta que debe estar dentro de un objetivo para hacer esto –

+0

Zain tiene razón, código fijo para mostrar eso. – Jonathan

2

Ahora, la manipulación a nivel local en un objetivo funciona ...

¿Qué tal sobrescribir global de finalizaciones, p.

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

Cuando comprobamos el contenido de% (TestFiles) aquí obtendremos:

1) Inicialmente: a.test b.test

2) dentro de la meta "preproceso" nosotros obtener: a.test c.test

3) después de salir objetivo "de preproceso" tenemos de nuevo: a.test b.test

Entonces, ¿hay alguna manera de que 3) genere el mismo resultado que 2)?

Esto realmente simplificaría muchas cosas, p. excluir código en directorios específicos de compilación, etc.

Saludos, Nomad

+0

Esto debería haber sido una nueva pregunta. Escribí una tarea que se agregó al MSBuild Extension Pack (http://www.codeplex.com/MSBuildExtensionPack) para hacer una actualización. Hace demasiado tiempo que recordé si funcionaría en el escenario que describiste anteriormente, pero te recomendaría que lo probaras. (Se llama UpdateMetadata en la clase MSBuild.ExtensionPack.Framework.MSBuildHelper. – Vaccano

1

En respuesta a Nómada, parece que un objetivo obtiene una copia del valor actual de las propiedades y los elementos cuando el objetivo se llama. En su ejemplo, puede solucionar el problema llamando al DefaultTarget después de que se haya completado el objetivo de PreProcess. Una forma de hacer esto sería para especificar que el DefaultTarget depende del destino de preproceso:

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

1

He intentado utilizar el UpdateMetaData TaskAction de la tarea MSBuildHelper en el paquete de las extensiones 4.0 pero no hizo lo que yo esperado, así que fui con el método de quitar/reemplazar. En este ejemplo, intento actualizar la propiedad de metadatos DestinationRelativePath en el grupo de elementos 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> 
Cuestiones relacionadas