Tevékenységek vagy célok futtatása kötegekben az elem metaadatai alapján

Az MSBuild az elemlistákat különböző kategóriákra vagy kötegekre osztja az elem metaadatai alapján, és minden köteggel egyszer futtat egy célt vagy feladatot.

Feladatok csoportosítása

A tevékenységek kötegelése egyszerűsíti a projektfájlokat azáltal, hogy az elemlistákat különböző kötegekre osztja fel, és ezeket a kötegeket külön-külön adja át feladatként. A kötegelés azt jelenti, hogy egy projektfájlnak csak egyszer kell deklarálnia a feladatot és annak attribútumait, annak ellenére, hogy többször is futtatható.

Megadhatja, hogy az MSBuild kötegelést végezzen egy feladattal az egyik feladat attribútumában az %(ItemMetaDataName) jelölés használatával. Az alábbi példa az Example elemlistát kötegekre osztja az Color elem metaadatainak értéke alapján, és külön továbbítja az egyes kötegeket a MyTask feladatnak.

Megjegyzés:

Ha nem hivatkozik az elemlistára a tevékenységattribútumok más részein, vagy a metaadatok neve nem egyértelmű, a %(<ItemCollection.ItemMetaDataName>) jelöléssel teljes mértékben minősítheti a kötegeléshez használandó elem metaadat-értékét.

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

Konkrétabb kötegelési példákért tekintse meg a tétel metadatáit a feladatok kötegelésében.

Célbeli kötegelés

Az MSBuild ellenőrzi, hogy a cél bemenetei és kimenetei naprakészek-e a cél futtatása előtt. Ha a bemenetek és a kimenetek is naprakészek, a cél ki lesz hagyva. Ha egy célon belüli tevékenység kötegelést használ, az MSBuildnek meg kell állapítania, hogy az egyes elemek kötegének bemenetei és kimenetei naprakészek-e. Ellenkező esetben a cél minden találatkor végrehajtódik.

Az alábbi példa egy Target olyan elemet mutat be, amely egy Outputs attribútumot tartalmaz a %(ItemMetadataName) jelöléssel. Az MSBuild az Example elemlistát kötegekre osztja az Color elem metaadatai alapján, és elemzi az egyes kötegekhez tartozó kimeneti fájlok időbélyegeit. Ha egy köteg kimenetei nem naprakészek, a cél végrehajtásra kerül. Ellenkező esetben a cél ki lesz hagyva.

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

A célkötegelés egy másik példáját az Elem metaadatai a cél kötegelésben című témakörben talál.

Elem- és tulajdonságmutációk

Ez a szakasz bemutatja, hogyan érthetjük meg a tulajdonságok és/vagy az elemek metaadatainak módosításának hatásait a cél- vagy feladatkötegelés során.

Mivel a cél kötegelés és a tevékenység kötegelése két különböző MSBuild művelet, fontos tisztában lenni azzal, hogy az MSBuild melyik kötegelési formáját használja mindegyik esetben. Ha a kötegelés szintaxisa %(ItemMetadataName) egy cél egy tevékenységében jelenik meg, de nem a Cél attribútumában, akkor az MSBuild tevékenység kötegelést használ. A célkötegezés megadásának egyetlen módja az, ha a célattribútum kötegelési szintaxisát használja, általában az Outputs attribútumot.

A cél kötegelés és a tevékenység kötegelése mellett a kötegek egymástól függetlenül futtathatók. Minden köteg azonos kezdeti állapotú tulajdonság- és elem metaadatértékek másolatával kezdődik. A tulajdonságértékek bármely mutációja a köteg végrehajtása során nem látható más kötegek számára. Vegye figyelembe a következő példát:

  <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 kimenet a következő:

Target DemoIndependentBatches:
  Things: 2 is red; needed change=true;1 is red; needed change=

A ItemGroup a célban implicit módon tevékenységként értelmezendő, és az %(Color) attribútumban a Condition feladatok csoportosítása és kezelése történik. Két tétel van: az egyik piros, a másik kék. A tulajdonság %(NeededColorChange) csak akkor van beállítva, ha a %(Color) metaadatok kékek, és a beállítás csak azokat az elemeket érinti, amelyek megfeleltek a kék köteg futtatásának feltételének. A Message tevékenység attribútuma Text a szintaxis ellenére nem aktiválja a %(ItemMetadataName) kötegelést, mert egy elemátalakításon belül használják.

A kötegek egymástól függetlenül futnak, de nem egyszerre. Ez különbséget tesz a kötegelt végrehajtás során változó metaadat-értékek elérésekor. Abban az esetben, ha a kötegelt végrehajtás bizonyos metaadatai alapján állít be egy tulajdonságot, a tulajdonság az utolsó értékhalmazt veszi fel:

   <PropertyGroup>
       <SomeProperty>%(SomeItem.MetadataValue)</SomeProperty>
   </PropertyGroup>

A tömeges végrehajtás után a tulajdonság megtartja a %(MetadataValue) végső értékét.

Bár a kötegek egymástól függetlenül futnak, fontos figyelembe venni a célzott kötegelés és a feladatok kötegelése közötti különbséget, és tudni, hogy melyik típus vonatkozik az Ön helyzetére. Az alábbi példában jobban megértheti ennek a különbségnek a fontosságát.

A feladatok lehetnek implicit módon megadottak ahelyett, hogy explicitek lennének, ami zavaró lehet, amikor a feladatok csoportosítása implicit módon történik. Amikor egy vagy PropertyGroup több ItemGroup elem megjelenik egy Targetcsoportban, a csoport minden tulajdonságdeklarációja implicit módon egy különálló CreateProperty vagy CreateItem feladatként lesz kezelve. Ez a viselkedés azt jelenti, hogy a build végrehajtása eltérő akkor, ha a célpont kötegelve van, szemben azzal, amikor a célpont nincs kötegelve (vagyis ha hiányzik az %(ItemMetadataName) szintaxis az Outputs attribútumban). Amikor a cél kötegelve van, a ItemGroup végrehajtódik célonként egyszer, de ha a cél nincs kötegelve, az CreateItem vagy CreateProperty feladatok implicit megfelelőit feladatok kötegelésével hajtja végre, így a cél csak egyszer hajtódik végre, és a csoport minden egyes elemét vagy tulajdonságát külön kötegeli feladatok kötegelésével.

Az alábbi példa a célzott kötegelést és a feladatok kötegelését mutatja be abban az esetben, ha a metaadatok módosulnak. Fontolja meg azt a helyzetet, amikor az A és a B mappában van néhány fájl:

A\1.stub
B\2.stub
B\3.stub

Most tekintse meg a két hasonló projekt kimenetét.

    <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 kimenet a következő:

Test1:
  >> A\ 'A\' 'A'
Test1:
  >> B\ 'B\' 'B'

Most távolítsa el a Outputs attribútumot, amely a cél kötegelést meghatározta.

    <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 kimenet a következő:

Test1:
  >> A\ 'B\' 'B'
  >> B\ 'B\' 'B'

Figyelje meg, hogy a címsor Test1 csak egyszer van kinyomtatva, de az előző példában kétszer nyomtatták ki. Ez azt jelenti, hogy a cél nincs kötegelve. Ennek eredményeképpen a kimenet zavaróan eltérő.

Ennek az az oka, hogy a cél kötegelés használatakor minden célköteg a célban lévő összes tulajdonságot és elemet saját, független másolattal hajtja végre, de ha kihagyja az Outputs attribútumot, a tulajdonságcsoport egyes sorai különálló, esetleg kötegelt tevékenységekként lesznek kezelve. Ebben az esetben a ComponentDir feladat kötegelve van (a %(ItemMetadataName) szintaxist használja), így mire a ComponentName sor végrehajtásra kerül, a ComponentDir sor mindkét kötege befejeződött, és a másodikként lefutott határozta meg az értéket, amit a második sorban láthatunk.

Tulajdonságfüggvények metaadatok használatával

A kötegelés metaadatokat tartalmazó tulajdonságfüggvényekkel szabályozható. Például

$([System.IO.Path]::Combine($(RootPath),%(Compile.Identity)))

Combine arra szolgál, hogy egy gyökérmappa elérési útját kombinálja egy fordítási elem elérési útjával.

Előfordulhat, hogy a tulajdonságfüggvények nem jelennek meg a metaadatértékek között. Például

%(Compile.FullPath.Substring(0,3))

nem engedélyezett.

A tulajdonságfüggvényekről további információt a Tulajdonságfüggvények című témakörben talál.

Elem kötegelése önhivatkozási metaadatokon

Fontolja meg a metaadatok elemdefiníción belüli hivatkozását az alábbi példában:

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

Fontos megjegyezni, hogy a viselkedés akkor különbözik, ha a célon kívül és a célon belül van meghatározva.

Elem önhivatkozási metaadatai bármely célterületen kívül

<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 metaadatokra való hivatkozás elempéldányonként oldódik fel (a korábban definiált vagy létrehozott elempéldányok nem befolyásolják) – ami a várt kimenethez vezet:

  i=[a/b.txt;c/d.txt;g/h.txt]
  i->MyPath=[b.txt;d.txt;h.txt]

Elem önhivatkozási metaadatai egy célobjektumon belül

<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 metaadatokra való hivatkozás ebben az esetben kötegeléshez vezet, amely esetleg váratlan és nem várt kimenetet eredményez:

  i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
  i->MyPath=[;b.txt;b.txt;d.txt]

Minden egyes elempéldány esetében a motor az összes már meglévő elempéldány metaadatait alkalmazza (emiatt az első elemnél üres MyPath, és a második elemhez tartozik b.txt). Több már meglévő példány esetén ez a viselkedés az aktuális elempéldány szorzásához vezet (ezért fordul elő kétszer az g/h.txt elempéldány az eredményül kapott listában).

Ha explicit módon szeretne tájékoztatást adni erről a valószínűleg nem szándékos viselkedésről, az MSBuild hibaüzenet MSB4120későbbi verziói:

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]

Ha az önhivatkozás szándékos, a tényleges forgatókönyvtől és a pontos igényektől függően néhány lehetőség közül választhat:

  • Tartsa meg a kódot, és hagyja figyelmen kívül az üzenetet
  • Az elem meghatározása a célterületen kívül
  • Segédelem és az átalakítási művelet használata

Segédelem és az átalakítási művelet használata

Ha meg szeretné akadályozni a metaadat-hivatkozás által kiváltott kötegelési viselkedést, ezt egy külön elem definiálásával, majd az átalakítási művelettel a kívánt metaadatokkal rendelkező elempéldányok létrehozásához teheti meg:

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