Uruchamianie zadań lub obiektów docelowych w partiach na podstawie metadanych elementu
Program MSBuild dzieli listy elementów na różne kategorie lub partie na podstawie metadanych elementu i uruchamia element docelowy lub zadanie pojedynczo z każdą partią.
Przetwarzanie wsadowe zadań
Przetwarzanie wsadowe zadań pozwala uprościć pliki projektu, udostępniając sposób dzielenia list elementów na różne partie i oddzielne przekazywanie każdej z tych partii do zadania. Oznacza to, że plik projektu musi mieć zadeklarowane zadanie i jego atrybuty tylko raz, mimo że można go uruchomić kilka razy.
Należy określić, że program MSBuild ma wykonywać przetwarzanie wsadowe z zadaniem przy użyciu %(ItemMetaDataName)
notacji w jednym z atrybutów zadania. Poniższy przykład dzieli Example
listę elementów na partie na Color
podstawie wartości metadanych elementu i przekazuje poszczególne partie do MyTask
zadania oddzielnie.
Uwaga
Jeśli nie odwołujesz się do listy elementów w innym miejscu atrybutów zadania lub nazwa metadanych może być niejednoznaczna, możesz użyć notacji %(<ItemCollection.ItemMetaDataName>), aby w pełni zakwalifikować wartość metadanych elementu do użycia do dzielenia na partie.
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
Aby uzyskać bardziej szczegółowe przykłady dzielenia na partie, zobacz Metadane elementów w partiach zadań.
Przetwarzanie wsadowe docelowe
Program MSBuild sprawdza, czy dane wejściowe i wyjściowe obiektu docelowego są aktualne, zanim uruchomi obiekt docelowy. Jeśli zarówno dane wejściowe, jak i wyjściowe są aktualne, element docelowy zostanie pominięty. Jeśli zadanie wewnątrz obiektu docelowego używa przetwarzania wsadowego, program MSBuild musi określić, czy dane wejściowe i wyjściowe dla każdej partii elementów są aktualne. W przeciwnym razie element docelowy jest wykonywany za każdym razem, gdy zostanie trafiony.
W poniższym przykładzie pokazano Target
element zawierający Outputs
atrybut z %(ItemMetadataName)
notacją. Program MSBuild podzieli Example
listę elementów na partie na Color
podstawie metadanych elementu i przeanalizuje znaczniki czasu plików wyjściowych dla każdej partii. Jeśli dane wyjściowe z partii nie są aktualne, element docelowy zostanie uruchomiony. W przeciwnym razie obiekt docelowy zostanie pominięty.
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
Aby zapoznać się z innym przykładem przetwarzania wsadowego docelowego, zobacz Metadane elementu w partii docelowej.
Mutacje elementów i właściwości
W tej sekcji opisano, jak zrozumieć skutki zmiany właściwości i/lub metadanych elementu podczas przetwarzania wsadowego docelowego lub dzielenia zadań na partie.
Ponieważ przetwarzanie wsadowe obiektów docelowych i przetwarzanie wsadowe zadań to dwie różne operacje MSBuild, ważne jest, aby dokładnie zrozumieć, która forma przetwarzania wsadowego programu MSBuild jest używana w każdym przypadku. Gdy składnia %(ItemMetadataName)
dzielenia na partie pojawia się w zadaniu docelowym, ale nie w atrybucie docelowym, program MSBuild używa przetwarzania wsadowego zadań. Jedynym sposobem określania wsadowania docelowego jest użycie składni dzielenia na partie w atrybucie Target, zazwyczaj atrybutu Outputs
.
W przypadku przetwarzania wsadowego docelowego i przetwarzania wsadowego zadań można uznać partie za niezależne. Wszystkie partie zaczynają się od kopii tego samego początkowego stanu właściwości i wartości metadanych elementu. Wszelkie mutacje wartości właściwości podczas wykonywania wsadowego nie są widoczne dla innych partii. Rozważmy następujący przykład:
<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>
Dane wyjściowe to:
Target DemoIndependentBatches:
Things: 2 is red; needed change=true;1 is red; needed change=
Obiekt ItemGroup
docelowy jest niejawnie zadaniem, a w %(Color)
atrybucie Condition
wykonywane jest dzielenie zadań na partie. Istnieją dwie partie: jedna dla czerwonego i druga dla niebieskiego. Właściwość %(NeededColorChange)
jest ustawiana tylko wtedy, gdy %(Color)
metadane są niebieskie, a ustawienie wpływa tylko na pojedynczy element pasujący do warunku, gdy została uruchomiona niebieska partia. Atrybut Message
zadania nie wyzwala przetwarzania Text
wsadowego, pomimo %(ItemMetadataName)
składni, ponieważ jest używany wewnątrz przekształcenia elementu.
Partie są uruchamiane niezależnie, ale nie równolegle. Ma to wpływ na dostęp do wartości metadanych, które zmieniają się w ramach wykonywania wsadowego. W przypadku ustawienia właściwości na podstawie niektórych metadanych w wykonaniu wsadowym właściwość będzie miała ostatni zestaw wartości:
<PropertyGroup>
<SomeProperty>%(SomeItem.MetadataValue)</SomeProperty>
</PropertyGroup>
Po wykonaniu wsadowym właściwość zachowuje ostateczną wartość %(MetadataValue)
.
Mimo że partie są uruchamiane niezależnie, ważne jest, aby wziąć pod uwagę różnicę między wsadowaniem docelowym a dzieleniem zadań na partie i wiedzieć, który typ ma zastosowanie do danej sytuacji. Rozważmy poniższy przykład, aby lepiej zrozumieć znaczenie tego rozróżnienia.
Zadania mogą być niejawne, a nie jawne, co może być mylące w przypadku dzielenia zadań na partie zadań niejawnych. Gdy element PropertyGroup
lub ItemGroup
pojawi się w Target
obiekcie , każda deklaracja właściwości w grupie jest niejawnie traktowana jak oddzielne zadanie CreateProperty lub CreateItem . Oznacza to, że zachowanie jest inne, gdy obiekt docelowy jest wsadowy, a gdy obiekt docelowy nie jest wsadowy (czyli gdy brakuje %(ItemMetadataName)
składni w atrybucie Outputs
). Gdy obiekt docelowy jest wsadowy, wykonywane raz na obiekt docelowy, ItemGroup
ale gdy obiekt docelowy nie jest wsadowy, niejawne odpowiedniki CreateItem
zadań lub CreateProperty
zadań są wsadowe przy użyciu przetwarzania wsadowego zadań, więc obiekt docelowy jest wykonywany tylko raz, a każdy element lub właściwość w grupie jest wsadowywane oddzielnie przy użyciu przetwarzania wsadowego zadań.
W poniższym przykładzie pokazano przetwarzanie wsadowe obiektów docelowych i przetwarzanie wsadowe zadań w przypadku, gdy metadane są zmutowane. Rozważ sytuację, w której masz foldery A i B z niektórymi plikami:
A\1.stub
B\2.stub
B\3.stub
Teraz przyjrzyj się danych wyjściowych tych dwóch podobnych projektów.
<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>
Dane wyjściowe to:
Test1:
>> A\ 'A\' 'A'
Test1:
>> B\ 'B\' 'B'
Teraz usuń Outputs
atrybut określony docelowy wsadowy.
<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>
Dane wyjściowe to:
Test1:
>> A\ 'B\' 'B'
>> B\ 'B\' 'B'
Zwróć uwagę, że nagłówek Test1
jest drukowany tylko raz, ale w poprzednim przykładzie został wydrukowany dwa razy. Oznacza to, że element docelowy nie jest wsadowy. W rezultacie dane wyjściowe są mylące różne.
Przyczyną jest to, że w przypadku korzystania z przetwarzania wsadowego obiektów docelowych każda partia docelowa wykonuje wszystko z własną niezależną kopią wszystkich właściwości i elementów, ale po pominięcia Outputs
atrybutu poszczególne wiersze w grupie właściwości są traktowane jako odrębne, potencjalnie wsadowe zadania. W takim przypadku ComponentDir
zadanie jest wsadowe (używa %(ItemMetadataName)
składni), tak aby w czasie ComponentName
wykonywania wiersza obie partie ComponentDir
wiersza zostały zakończone, a druga, która została uruchomiona, określiła wartość, jak widać w drugim wierszu.
Funkcje właściwości korzystające z metadanych
Przetwarzanie wsadowe może być kontrolowane przez funkcje właściwości, które zawierają metadane. Przykład:
$([System.IO.Path]::Combine($(RootPath),%(Compile.Identity)))
używa Combine metody do łączenia ścieżki folderu głównego ze ścieżką elementu Kompiluj.
Funkcje właściwości mogą nie być wyświetlane w ramach wartości metadanych. Przykład:
%(Compile.FullPath.Substring(0,3))
jest niedozwolona.
Aby uzyskać więcej informacji na temat funkcji właściwości, zobacz Funkcje właściwości.
Przetwarzanie wsadowe elementów w metadanych odwołujące się do siebie
Rozważmy następujący przykład odwoływania się do metadanych z definicji elementu:
<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>
Należy pamiętać, że zachowanie różni się, gdy jest zdefiniowane poza dowolnym obiektem docelowym i w obrębie elementu docelowego.
Samodzielnie odwołujące się do elementu metadane poza dowolnym obiektem docelowym
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
Odwoływanie się do metadanych jest rozpoznawane dla wystąpienia elementu (bez wpływu na żadne wcześniej zdefiniowane lub utworzone wystąpienia elementów) — co prowadzi do oczekiwanych danych wyjściowych:
i=[a/b.txt;c/d.txt;g/h.txt]
i->MyPath=[b.txt;d.txt;h.txt]
Samodzielne odwoływanie się do metadanych elementu w obiekcie docelowym
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
Metadane odwołujące się w tym przypadku prowadzą do dzielenia na partie, co daje prawdopodobnie nieoczekiwane i niezamierzone dane wyjściowe:
i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
i->MyPath=[;b.txt;b.txt;d.txt]
Dla każdego wystąpienia elementu aparat stosuje metadane wszystkich wstępnie istniejących wystąpień elementów (dlatego element MyPath
jest pusty dla pierwszego elementu i zawiera b.txt
drugi element). W przypadku większej liczby wstępnie istniejących wystąpień prowadzi to do mnożenia bieżącego wystąpienia elementu (dlatego g/h.txt
wystąpienie elementu występuje dwa razy na wynikowej liście).
Aby jawnie poinformować o tym, prawdopodobnie niezamierzone zachowanie, późniejsze wersje komunikatu MSB4120
o problemie MSBuild:
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]
Jeśli odwołanie samodzielne jest zamierzone, masz kilka opcji w zależności od rzeczywistego scenariusza i dokładnych potrzeb:
- Zachowaj kod i ignoruj komunikat
- Definiowanie elementu poza obiektem docelowym
- Definiowanie elementu pomocniczego i wykorzystywanie przekształceń
Korzystanie z elementu pomocnika i przekształcania
Jeśli chcesz zapobiec zachowaniu przetwarzania wsadowego wywołanego przez odwołanie do metadanych, możesz to osiągnąć, definiując oddzielny element, a następnie wykorzystując operację przekształcania w celu utworzenia wystąpień elementów z żądanymi metadanymi:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>