Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O MSBuild divide as listas de itens em diferentes categorias, ou lotes, com base nos metadados do item e executa um destino ou tarefa uma vez com cada lote.
Processamento em lote de tarefas
O processamento em lote de tarefas permite simplificar seus arquivos de projeto, fornecendo uma maneira de dividir listas de itens em lotes diferentes e passar cada um desses lotes para uma tarefa separadamente. Envio em lote significa que um arquivo de projeto só precisa ter a tarefa e seus atributos declarados uma vez, mesmo que possa ser executado várias vezes.
Especifica-se que deseja que o MSBuild execute o processamento em lote com uma tarefa ao usar a notação %(ItemMetaDataName) em um dos atributos da tarefa. O exemplo a seguir divide a Example lista de itens em lotes com base no valor de metadados do Color item e passa cada um dos lotes para a MyTask tarefa separadamente.
Observação
Se você não fizer referência à lista de itens em outro lugar nos atributos da tarefa, ou se o nome dos metadados puder ser ambíguo, poderá usar a notação %(<ItemCollection.ItemMetaDataName>) para qualificar totalmente o valor dos metadados do item a ser usado para processamento em lote.
<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 obter exemplos de lotes mais específicos, consulte Metadados de item em lotes de tarefas.
Agrupamento de destino
O MSBuild verifica se as entradas e saídas de um destino estão atualizadas antes de executar o destino. Se as entradas e saídas estiverem atualizadas, o alvo será ignorado. Se uma tarefa dentro de um destino usa lotes, o MSBuild precisa determinar se as entradas e saídas para cada lote de itens estão atualizadas. Caso contrário, o alvo é executado sempre que é atingido.
O exemplo a seguir mostra um Target elemento que contém um Outputs atributo com a %(ItemMetadataName) notação. MSBuild divide a Example lista de itens em lotes com base nos metadados do Color item e analisa os carimbos de data dos ficheiros de saída de cada lote. Se as saídas de um lote não estiverem atualizadas, o alvo será executado. Caso contrário, o alvo é ignorado.
<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 obter outro exemplo de processamento em lote de destino, consulte Metadados de item em lotes de destino.
Mutações de itens e propriedades
Esta seção descreve como compreender os efeitos da alteração das propriedades e/ou dos metadados dos itens ao utilizar o agrupamento de alvos ou o agrupamento de tarefas.
Como o lote de destino e o lote de tarefas são duas operações MSBuild diferentes, é importante entender exatamente qual forma de lote o MSBuild usa em cada caso. Quando a sintaxe %(ItemMetadataName) de agrupamento em lote aparece numa tarefa de um alvo, mas não num atributo no alvo, o MSBuild usa o agrupamento de tarefas. A única maneira de especificar o processamento em lote de alvos é usando a sintaxe de processamento em lote num atributo Target, geralmente o atributo Outputs.
Com o processamento em lote de destino e o processamento em lote de tarefas, os lotes podem ser considerados como executados independentemente. Todos os lotes começam com uma cópia do mesmo estado inicial dos valores de propriedade e dos metadados de itens. Quaisquer mutações de valores de propriedade durante a execução do lote não são visíveis para outros lotes. Considere o seguinte exemplo:
<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>
A saída é:
Target DemoIndependentBatches:
Things: 2 is red; needed change=true;1 is red; needed change=
O ItemGroup no destino é implicitamente uma tarefa e, com o %(Color) no Condition atributo, executa-se o processamento em lote de tarefas. Existem dois lotes: um para vermelho e outro para azul. A propriedade %(NeededColorChange) só é definida se os %(Color) metadados forem azuis e a configuração afeta apenas o item individual que correspondeu à condição quando o lote azul foi executado. O Message atributo da Text tarefa não aciona o envio em lote, apesar da %(ItemMetadataName) sintaxe, porque é usado dentro de uma transformação de item.
Os lotes são executados de forma independente, mas não em paralelo. Isso faz diferença quando você acessa valores de metadados que mudam na execução em lote. No caso em que você define uma propriedade com base em alguns metadados na execução em lote, a propriedade levaria o último conjunto de valores:
<PropertyGroup>
<SomeProperty>%(SomeItem.MetadataValue)</SomeProperty>
</PropertyGroup>
Após a execução em lote, a propriedade retém o valor final de %(MetadataValue).
Embora os lotes sejam executados de forma independente, é importante considerar a diferença entre o lote de destino e o lote de tarefas e saber qual tipo se aplica à sua situação. Considere o exemplo a seguir para entender melhor a importância dessa distinção.
As tarefas podem ser implícitas, em vez de explícitas, o que pode ser confuso quando o processamento em lote de tarefas ocorre com tarefas implícitas. Quando um PropertyGroup ou ItemGroup elemento aparece num Target, cada declaração de propriedade no grupo é implicitamente tratada como se fosse uma tarefa CreateProperty ou CreateItem separada. Esse comportamento significa que a execução da compilação é diferente quando o alvo é processado em lote, em comparação com quando o alvo não é processado em lote (ou seja, quando não tem a sintaxe %(ItemMetadataName) no atributo Outputs). Quando o destino é processado em lote, o ItemGroup é executado uma vez por destino, mas quando o destino não é processado em lote, os equivalentes implícitos das CreateItem ou CreateProperty tarefas são processados em lote usando o processamento em lote de tarefas, de modo que o destino só é executado uma vez e cada item ou propriedade no grupo é processado em lote separadamente usando o processamento em lote de tarefas.
O exemplo a seguir ilustra a agregação de destino versus a agregação de tarefas no caso em que os metadados são alterados. Considere uma situação em que você tem pastas A e B com alguns arquivos:
A\1.stub
B\2.stub
B\3.stub
Vejamos agora os resultados destes dois projetos semelhantes.
<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>
A saída é:
Test1:
>> A\ 'A\' 'A'
Test1:
>> B\ 'B\' 'B'
Agora, remova o atributo Outputs que especificava o processamento em lote 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>
A saída é:
Test1:
>> A\ 'B\' 'B'
>> B\ 'B\' 'B'
Observe que o título Test1 é impresso apenas uma vez, mas no exemplo anterior, ele foi impresso duas vezes. Isso significa que o alvo não é agrupado. E, como resultado, a saída é confusamente diferente.
O motivo é que, ao usar o processamento por lotes de destino, cada lote de destino executa tudo no alvo com sua própria cópia independente de todas as propriedades e itens, mas quando você omite o atributo Outputs, as linhas individuais no grupo de propriedades são tratadas como tarefas distintas, potencialmente submetidas a processamento por lote. Nesse caso, a tarefa é processada em lote ComponentDir (usa a sintaxe %(ItemMetadataName)), de modo que, no momento em que a linha ComponentName é executada, ambos os lotes da linha ComponentDir já foram concluídos, e o segundo que foi executado determinou o valor conforme visto na segunda linha.
Funções de propriedade usando metadados
O processamento em lote pode ser controlado por funções de propriedade que incluem metadados. Por exemplo
$([System.IO.Path]::Combine($(RootPath),%(Compile.Identity)))
usa Combine para combinar um caminho de pasta raiz com um caminho de item de compilação.
As funções de propriedade podem não aparecer dentro dos valores de metadados. Por exemplo
%(Compile.FullPath.Substring(0,3))
não é permitido.
Para obter mais informações sobre funções de propriedade, consulte Funções de propriedade.
Agrupamento de itens em metadados autorreferenciais
Considere o seguinte exemplo de referência a metadados de dentro de uma definição de item:
<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>
É importante notar que o comportamento difere quando definido fora de qualquer alvo e dentro do alvo.
Metadados de autorreferência de item fora de qualquer alvo
<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>
A referência de metadados é resolvida por instância de item (não afetada por nenhuma instância de item definida ou criada anteriormente) - levando à saída esperada:
i=[a/b.txt;c/d.txt;g/h.txt]
i->MyPath=[b.txt;d.txt;h.txt]
Metadados de autorreferência de item dentro de um destino
<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>
A referência de metadados, neste caso, leva ao processamento em lote, que produz resultados possivelmente inesperados e não intencionais:
i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
i->MyPath=[;b.txt;b.txt;d.txt]
Para cada instância de item, o mecanismo aplica metadados de todas as instâncias de item pré-existentes (é por isso que o MyPath está vazio para o primeiro item e contém b.txt para o segundo item). No caso de mais instâncias pré-existentes, esse comportamento leva à multiplicação da instância do item atual (é por isso que a g/h.txt instância do item ocorre duas vezes na lista resultante).
Para informar explicitamente sobre este comportamento possivelmente não intencional, versões posteriores do MSBuild emitem a mensagem 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]
Se a autorreferência for intencional, você tem poucas opções, dependendo do cenário real e das necessidades exatas:
- Mantenha o código e ignore a mensagem
- Definir o item fora do alvo
- Usar um item auxiliar e a operação de transformação
Usar um item auxiliar e a operação de transformação
Se quiser evitar o comportamento de envio em lote induzido pela referência de metadados, você pode conseguir isso definindo um item separado e, em seguida, usando a operação de transformação para criar instâncias de item com os metadados desejados:
<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>