Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
MSBuild divide las listas de elementos en diferentes categorías, o lotes, en función de los metadatos del elemento, y ejecuta un destino o una tarea una vez con cada lote.
Procesamiento por lotes de tareas
El procesamiento por lotes de tareas le permite simplificar los archivos del proyecto proporcionando una manera de dividir las listas de elementos en distintos lotes y pasar cada uno de esos lotes a una tarea por separado. El procesamiento por lotes significa que un archivo de proyecto solo necesita tener la tarea y sus atributos declarados una vez, aunque se pueda ejecutar varias veces.
Usted especifica que quiere que MSBuild realice el procesamiento por lotes con una tarea mediante la notación %(ItemMetaDataName) en uno de los atributos de tarea. En el ejemplo siguiente se divide la Example lista de elementos en lotes en función del Color valor de metadatos del elemento y se pasa cada uno de los lotes a la MyTask tarea por separado.
Nota:
Si no hace referencia a la lista de elementos en otra parte de los atributos de tarea, o el nombre de los metadatos puede ser ambiguo, puede usar la notación %(<ItemCollection.ItemMetaDataName>) para calificar completamente el valor de metadatos del elemento que se usará para el procesamiento por lotes.
<Project>
<ItemGroup>
<Example Include="Item1">
<Color>Blue</Color>
</Example>
<Example Include="Item2">
<Color>Red</Color>
</Example>
</ItemGroup>
<Target Name="RunMyTask">
<MyTask
Sources = "@(Example)"
Output = "%(Color)\MyFile.txt"/>
</Target>
</Project>
Para obtener ejemplos de procesamiento por lotes más específicos, consulte Metadatos de elementos en el procesamiento por lotes de tareas.
Procesamiento por lotes de destino
MSBuild comprueba si las entradas y salidas de un destino están actualizadas antes de ejecutar el destino. Si las entradas y salidas están actualizadas, se omite el objetivo. Si una tarea dentro de un destino usa el procesamiento por lotes, MSBuild debe determinar si las entradas y salidas de cada lote de elementos están actualizadas. De lo contrario, el objetivo se procesa cada vez que se alcanza.
El siguiente ejemplo muestra un elemento Target que contiene un atributo Outputs con la notación %(ItemMetadataName). MSBuild divide la Example lista de elementos en lotes en función de los metadatos del Color elemento y analiza las marcas de tiempo de los archivos de salida de cada lote. Si las salidas de un lote no están actualizadas, se ejecuta el destino. De lo contrario, se omite el objetivo.
<Project>
<ItemGroup>
<Example Include="Item1">
<Color>Blue</Color>
</Example>
<Example Include="Item2">
<Color>Red</Color>
</Example>
</ItemGroup>
<Target Name="RunMyTask"
Inputs="@(Example)"
Outputs="%(Color)\MyFile.txt">
<MyTask
Sources = "@(Example)"
Output = "%(Color)\MyFile.txt"/>
</Target>
</Project>
Para obtener otro ejemplo de procesamiento por lotes de destino, consulte Metadatos de elementos en el procesamiento por lotes de destino.
Mutaciones de elementos y propiedades
En esta sección se describe cómo comprender los efectos de cambiar las propiedades o metadatos de elementos, al usar el procesamiento por lotes de destino o el procesamiento por lotes de tareas.
Dado que el procesamiento por lotes de destino y el procesamiento por lotes de tareas son dos operaciones diferentes de MSBuild, es importante comprender exactamente qué forma de procesamiento por lotes usa MSBuild en cada caso. Cuando la sintaxis %(ItemMetadataName) de procesamiento por lotes aparece en una tarea de un destino, pero no en un atributo en target, MSBuild usa el procesamiento por lotes de tareas. La única manera de especificar el procesamiento por lotes de destino es mediante la sintaxis de procesamiento por lotes en un atributo Target, normalmente el Outputs atributo .
Con el procesamiento por lotes de destino y el procesamiento por lotes de tareas, los lotes se pueden considerar que se ejecuten de forma independiente. Todos los lotes comienzan con una copia del mismo estado inicial de los valores de metadatos de propiedades y elementos. Las mutaciones de los valores de propiedad durante la ejecución por lotes no son visibles para otros lotes. Considere el ejemplo siguiente:
<ItemGroup>
<Thing Include="2" Color="blue" />
<Thing Include="1" Color="red" />
</ItemGroup>
<Target Name="DemoIndependentBatches">
<ItemGroup>
<Thing Condition=" '%(Color)' == 'blue' ">
<Color>red</Color>
<NeededColorChange>true</NeededColorChange>
</Thing>
</ItemGroup>
<Message Importance="high"
Text="Things: @(Thing->'%(Identity) is %(Color); needed change=%(NeededColorChange)')"/>
</Target>
La salida es la siguiente:
Target DemoIndependentBatches:
Things: 2 is red; needed change=true;1 is red; needed change=
El ItemGroup en el target es implícitamente una tarea y, con el %(Color) en el atributo Condition, se realiza el procesamiento por lotes. Hay dos lotes: uno para rojo y otro para azul. La propiedad %(NeededColorChange) solo se establece si los %(Color) metadatos son azules y la configuración solo afecta al elemento individual que coincide con la condición cuando se ejecutó el lote azul. El atributo de la tarea Message no desencadena el procesamiento en lotes, a pesar de la sintaxis %(ItemMetadataName), porque se usa dentro de una transformación de elemento.
Los lotes se ejecutan de forma independiente, pero no en paralelo. Esto hace una diferencia cuando se accede a los valores de metadatos que cambian en la ejecución por lotes. En el caso de establecer una propiedad basada en algunos metadatos de la ejecución por lotes, la propiedad tomaría el último valor establecido:
<PropertyGroup>
<SomeProperty>%(SomeItem.MetadataValue)</SomeProperty>
</PropertyGroup>
Después de la ejecución por lotes, la propiedad conserva el valor final de %(MetadataValue).
Aunque los lotes se ejecutan de forma independiente, es importante tener en cuenta la diferencia entre el procesamiento por lotes de destino y el procesamiento por lotes de tareas y saber qué tipo se aplica a su situación. Considere el ejemplo siguiente para comprender mejor la importancia de esta distinción.
Las tareas pueden ser implícitas, en lugar de explícitas, que pueden resultar confusas cuando se produce el procesamiento por lotes de tareas con tareas implícitas. Cuando un elemento PropertyGroup o ItemGroup aparece en un Target, cada declaración de propiedad del grupo se trata implícitamente como una tarea CreateProperty o CreateItem independiente. Este comportamiento significa que la ejecución de la compilación es diferente cuando el destino se procesa por lotes, frente a cuando no se procesa por lotes (es decir, cuando carece de la sintaxis %(ItemMetadataName) en el atributo Outputs). Cuando el destino se realiza por lotes, el ItemGroup se ejecuta una vez por destino, pero cuando el destino no se realiza por lotes, los equivalentes implícitos de CreateItem o de las tareas CreateProperty se agrupan mediante el procesamiento por lotes de tareas, por lo que el destino solo se ejecuta una vez y cada elemento o propiedad del grupo se procesa por lotes independientemente mediante el procesamiento por lotes de tareas.
En el ejemplo siguiente se muestra el procesamiento por lotes de destino frente al procesamiento por lotes de tareas en el caso de que se mutan los metadatos. Considere una situación en la que tiene carpetas A y B con algunos archivos:
A\1.stub
B\2.stub
B\3.stub
Ahora vea el resultado de estos dos proyectos similares.
<ItemGroup>
<StubFiles Include="$(MSBuildThisFileDirectory)**\*.stub"/>
<StubDirs Include="@(StubFiles->'%(RecursiveDir)')"/>
</ItemGroup>
<Target Name="Test1" AfterTargets="Build" Outputs="%(StubDirs.Identity)">
<PropertyGroup>
<ComponentDir>%(StubDirs.Identity)</ComponentDir>
<ComponentName>$(ComponentDir.TrimEnd('\'))</ComponentName>
</PropertyGroup>
<Message Text=">> %(StubDirs.Identity) '$(ComponentDir)' '$(ComponentName)'"/>
</Target>
La salida es la siguiente:
Test1:
>> A\ 'A\' 'A'
Test1:
>> B\ 'B\' 'B'
Ahora quite el Outputs atributo que especificó el procesamiento por lotes de destino.
<ItemGroup>
<StubFiles Include="$(MSBuildThisFileDirectory)**\*.stub"/>
<StubDirs Include="@(StubFiles->'%(RecursiveDir)')"/>
</ItemGroup>
<Target Name="Test1" AfterTargets="Build">
<PropertyGroup>
<ComponentDir>%(StubDirs.Identity)</ComponentDir>
<ComponentName>$(ComponentDir.TrimEnd('\'))</ComponentName>
</PropertyGroup>
<Message Text=">> %(StubDirs.Identity) '$(ComponentDir)' '$(ComponentName)'"/>
</Target>
La salida es la siguiente:
Test1:
>> A\ 'B\' 'B'
>> B\ 'B\' 'B'
Observe que el encabezado Test1 solo se imprime una vez, pero en el ejemplo anterior, se imprimió dos veces. Esto significa que el destino no está procesado por lotes. Y como resultado, la salida es confusamente diferente.
El motivo es que al usar el procesamiento por lotes de destino, cada lote de destino ejecuta todo lo que hay en el destino con su propia copia independiente de todas las propiedades y elementos, pero cuando se omite el Outputs atributo, las líneas individuales del grupo de propiedades se tratan como tareas distintas y potencialmente por lotes. En este caso, la ComponentDir tarea se procesa por lotes (usa la %(ItemMetadataName) sintaxis), de modo que, para cuando se ejecuta la ComponentName línea, ambos lotes de la ComponentDir línea se han completado, y el segundo lote que se ejecutó determinó el valor, como se observa en la segunda línea.
Funciones de propiedad mediante metadatos
El procesamiento por lotes se puede controlar mediante funciones de propiedad que incluyen metadatos. Por ejemplo
$([System.IO.Path]::Combine($(RootPath),%(Compile.Identity)))
usa Combine para combinar la ruta de acceso de la carpeta raíz con la del elemento de compilación.
Es posible que las funciones de propiedad no aparezcan dentro de los valores de metadatos. Por ejemplo
%(Compile.FullPath.Substring(0,3))
no está permitido.
Para obtener más información sobre las funciones de propiedad, vea Funciones de propiedad.
Procesamiento por lotes de elementos en metadatos de referencia automática
Considere el ejemplo siguiente de hacer referencia a metadatos desde dentro de una definición de elemento:
<ItemGroup>
<i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
<i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
<i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
</ItemGroup>
Es importante tener en cuenta que el comportamiento difiere cuando se define fuera de cualquier destino y dentro del destino.
Metadatos de referencia automática de elementos fuera de cualquier destino
<Project>
<ItemGroup>
<i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
<i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
<i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
</ItemGroup>
<Target Name='ItemOutside'>
<Message Text="i=[@(i)]" Importance='High' />
<Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
</Target>
</Project>
Las referencias de metadatos se resuelven por instancia de elemento (no se ven afectadas por ninguna instancia de elemento definida o creada previamente), lo que conduce a la salida esperada:
i=[a/b.txt;c/d.txt;g/h.txt]
i->MyPath=[b.txt;d.txt;h.txt]
Metadatos de autorreferencia de elementos dentro de un objetivo
<Project>
<Target Name='ItemInside'>
<ItemGroup>
<i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
<i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
<i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
</ItemGroup>
<Message Text="i=[@(i)]" Importance='High' />
<Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
</Target>
</Project>
Los metadatos que hacen referencia en este caso conducen al procesamiento por lotes, lo que produce posiblemente resultados inesperados e imprevistos:
i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
i->MyPath=[;b.txt;b.txt;d.txt]
Para cada instancia de elemento, el motor aplica metadatos de todas las instancias de elemento preexistentes (por eso está MyPath vacío para el primer elemento y contiene b.txt para el segundo elemento). En el caso de instancias más preexistentes, este comportamiento conduce a la multiplicación de la instancia de elemento actual (por eso la g/h.txt instancia de elemento se produce dos veces en la lista resultante).
Para informar explícitamente sobre este comportamiento posiblemente no deseado, las versiones posteriores de MSBuild emiten el mensaje MSB4120:
proj.proj(4,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(4,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(5,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(5,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(6,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(6,11): message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
i->MyPath=[;b.txt;b.txt;d.txt]
Si la auto-referencia es intencionada, tiene algunas opciones en función del escenario real y las necesidades exactas:
- Mantener el código e ignorar el mensaje
- Definir el ítem fuera del objetivo
- Usar un elemento auxiliar y la operación de transformación
Usar un elemento auxiliar y la operación de transformación
Si desea evitar el comportamiento de procesamiento por lotes provocado por la referencia de metadatos, puede lograrlo definiendo un elemento independiente y usando la operación de transformación para crear instancias de elemento con los metadatos deseados:
<Project>
<Target Name='ItemOutside'>
<ItemGroup>
<j Include='a/b.txt' />
<j Include='c/*' />
<i Include='@(j)' MyPath="%(Filename)%(Extension)" />
</ItemGroup>
<Message Text="i=[@(i)]" Importance='High' />
<Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
</Target>
</Project>