MSBuild beágyazott feladat létrehozása a RoslynCodeTaskFactory használatával

RoslynCodeTaskFactory A platformfüggetlen Roslyn-fordítók használatával memóriabeli feladatszerelvényeket hoz létre beágyazott feladatként való használatra. RoslynCodeTaskFactory a feladatok a .NET Standardot célozzák, és a .NET-keretrendszeren és a .NET Core-futtatókörnyezeteken, valamint más platformokon, például Linuxon és macOS-en is használhatók.

Megjegyzés:

A RoslynCodeTaskFactory csak az MSBuild 15.8-ban és újabb verziókban érhető el. Az MSBuild verziók a Visual Studio verzióit követik, így a RoslynCodeTaskFactory a Visual Studio 2017 15.8-s és újabb verzióiban érhető el.

Beágyazott feladat felépítése a RoslynCodeTaskFactory használatával

RoslynCodeTaskFactory a beágyazott tevékenységek deklarálása az UsingTask elem használatával történik. A beágyazott tevékenység és az UsingTask azt tartalmazó elem általában egy .targets fájlban van, és szükség szerint importálva van más projektfájlokba. Íme egy alapszintű beágyazott feladat. Figyelje meg, hogy nem csinál semmit.

<Project>
  <!-- This simple inline task does nothing. -->
  <UsingTask
    TaskName="DoNothing"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="" />
      <Using Namespace="" />
      <Code Type="Fragment" Language="cs">
      </Code>
    </Task>
  </UsingTask>
</Project>

A UsingTask példában szereplő elem három attribútummal rendelkezik, amelyek a feladatot és az azt fordító beágyazott feladat-előállítót írják le.

  • Ebben TaskName az esetben az attribútum neve a tevékenység. DoNothing

  • Az TaskFactory attribútum a beágyazott feladat-előállítót megvalósító osztálynak nevezi el.

  • Az AssemblyFile attribútum a beágyazott feladat-előállító helyét adja meg. Másik lehetőségként az AssemblyName attribútum használatával megadhatja a beágyazott feladat-előállító osztály teljes nevét, amely általában a globális szerelvény-gyorsítótárban (GAC) található.

A tevékenység fennmaradó elemei DoNothing üresek, és a beágyazott tevékenységek sorrendjének és szerkezetének szemléltetésére szolgálnak. A cikk későbbi részében egy robusztusabb példát mutatunk be.

  • Az ParameterGroup elem nem kötelező. Ha meg van adva, deklarálja a tevékenység paramétereit. A bemeneti és kimeneti paraméterekkel kapcsolatos további információkért lásd a cikk későbbi, bemeneti és kimeneti paramétereit .

  • Az Task elem leírja és tartalmazza a tevékenység forráskódját.

  • Az Reference elem a kódban használt .NET-szerelvényekre mutató hivatkozásokat adja meg. Ez egyenértékű azzal, ha egy projektre mutató hivatkozást ad hozzá a Visual Studióban. Az Include attribútum a hivatkozott szerelvény elérési útját adja meg.

  • Az Using elem felsorolja a elérni kívánt névtereket. Ez az elem hasonlít a using Visual C# irányelvére. Az Namespace attribútum megadja a belefoglalandó névteret.

Reference és Using az elemek nyelvi agnosztikusak. A beágyazott feladatok a támogatott .NET CodeDom nyelvek bármelyikében írhatók, például Visual Basic vagy Visual C# nyelven.

Megjegyzés:

Az elem által Task tartalmazott elemek a feladat-előállítóra, ebben az esetben a kód-feladat-előállítóra vonatkoznak.

Kódelem

Az elemen belül Task az utolsó gyermekelem az Code elem. Az Code elem tartalmazza vagy megkeresi a feladatba lefordítani kívánt kódot. Az elembe Code helyezett elemek attól függenek, hogyan szeretné megírni a feladatot.

Az Language attribútum azt a nyelvet határozza meg, amelyben a kód meg van írva. Az elfogadható értékek a cs C#, vb a Visual Basic esetében.

Az Type attribútum megadja az elemben Code található kód típusát.

  • Ha az érték Type az Class, akkor az Code elem egy olyan osztály kódját tartalmazza, amely a ITask felületből származik.

  • Ha az érték Type az Method, akkor a kód a felület metódusának Execute felülbírálását ITask határozza meg.

  • Ha az érték Type az Fragment, akkor a kód határozza meg a Execute metódus tartalmát, de az aláírást vagy az utasítást return nem.

Maga a kód általában egy jelölő és egy <![CDATA[]]> jelölő között jelenik meg. Mivel a kód egy CDATA-szakaszban található, nem kell aggódnia a fenntartott karakterek, például a "<" vagy a ">" elől.

Másik lehetőségként az Source elem attribútumával Code megadhatja a feladat kódját tartalmazó fájl helyét. A forrásfájl kódjának az attribútum által Type megadott típusnak kell lennie. Ha az Source attribútum jelen van, az alapértelmezett érték az TypeClass. Ha Source nincs jelen, az alapértelmezett érték a következő Fragment: .

Megjegyzés:

Ha egy forrásfájlban definiálja a feladatosztályt, az osztály nevének meg kell egyeznie a TaskName megfelelő UsingTask elem attribútumával.

„Helló világ!” alkalmazás

Íme egy robusztusabb beágyazott feladat a következővel RoslynCodeTaskFactory: . A HelloWorld feladat az alapértelmezett hibanaplózó eszközön jeleníti meg a "Hello, world!" szöveget, amely általában a rendszerkonzol vagy a Visual Studio Kimeneti ablaka. A Reference példában szereplő elem csak illusztrációként szerepel.

<Project>
  <!-- This simple inline task displays "Hello, world!" -->
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
    <ParameterGroup />
    <Task>
      <Reference Include="System.Xml"/>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
<![CDATA[
// Display "Hello, world!"
Log.LogError("Hello, world!");
]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

A feladatot egy HelloWorld.targets nevű fájlba menthetiHelloWorld, majd meghívhatja egy projektből az alábbiak szerint.

<Project>
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

Bemeneti és kimeneti paraméterek

A beágyazott tevékenységparaméterek egy ParameterGroup elem gyermekelemei. Minden paraméter az azt meghatározó elem nevét veszi fel. A paramétert Textaz alábbi kód határozza meg.

<ParameterGroup>
    <Text />
</ParameterGroup>

A paraméterek egy vagy több attribútummal rendelkezhetnek:

  • Required alapértelmezés szerint nem kötelező attribútum false . Ha true, akkor a paraméter megadása kötelező, és a feladat meghívása előtt meg kell adni egy értéket.

  • ParameterType alapértelmezés szerint nem kötelező attribútum System.String . Bármilyen teljesen minősített típusra állítható be, amely egy elem vagy egy olyan érték, amely a System.Convert.ChangeType használatával sztringgé alakítható át. (Más szóval minden olyan típus, amely átadható egy külső tevékenységnek, és amelyből át lehet adni.)

  • Output alapértelmezés szerint nem kötelező attribútum false . Ha true, akkor a paraméternek meg kell adni egy értéket, mielőtt az Execute metódusból tér vissza.

Például

<ParameterGroup>
    <Expression Required="true" />
    <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
    <Tally ParameterType="System.Int32" Output="true" />
</ParameterGroup>

a következő három paramétert határozza meg:

  • Expression a System.String típusú kötelező bemeneti paraméter.

  • Files egy kötelező elemlista bemeneti paramétere.

  • Tally Egy System.Int32 típusú kimeneti paraméter.

Ha az Code elem attribútuma TypeFragment vagy Method, akkor a rendszer minden paraméterhez automatikusan létrehozza a tulajdonságokat. A RoslynCodeTaskFactory-ban, ha az Code elem Type attribútuma Classaz , akkor nem kell megadnia azt ParameterGroup, mivel a forráskódból következtet (ez a különbség a forráskódtól CodeTaskFactory). Ellenkező esetben a tulajdonságokat explicit módon deklarálni kell a tevékenység forráskódjában, és pontosan meg kell egyezniük a paraméterdefiníciókkal.

példa

Az alábbi beágyazott feladat naplóz néhány üzenetet, és visszaad egy sztringet.

<Project>

    <UsingTask TaskName="MySample"
               TaskFactory="RoslynCodeTaskFactory"
               AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <Parameter1 ParameterType="System.String" Required="true" />
            <Parameter2 ParameterType="System.String" />
            <Parameter3 ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System" />
            <Code Type="Fragment" Language="cs">
              <![CDATA[
              Log.LogMessage(MessageImportance.High, "Hello from an inline task created by Roslyn!");
              Log.LogMessageFromText($"Parameter1: '{Parameter1}'", MessageImportance.High);
              Log.LogMessageFromText($"Parameter2: '{Parameter2}'", MessageImportance.High);
              Parameter3 = "A value from the Roslyn CodeTaskFactory";
            ]]>
            </Code>
        </Task>
    </UsingTask>

    <Target Name="Demo">
      <MySample Parameter1="A value for parameter 1" Parameter2="A value for parameter 2">
          <Output TaskParameter="Parameter3" PropertyName="NewProperty" />
      </MySample>

      <Message Text="NewProperty: '$(NewProperty)'" />
    </Target>
</Project>

Ezek a beágyazott feladatok egyesíthetik az elérési utakat, és lekérhetik a fájlnevet.

<Project>

    <UsingTask TaskName="PathCombine"
               TaskFactory="RoslynCodeTaskFactory"
               AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <Paths ParameterType="System.String[]" Required="true" />
            <Combined ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System" />
            <Code Type="Fragment" Language="cs">
            <![CDATA[
            Combined = Path.Combine(Paths);
            ]]>
            </Code>
        </Task>
    </UsingTask>

    <UsingTask TaskName="PathGetFileName"
             TaskFactory="RoslynCodeTaskFactory"
             AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <Path ParameterType="System.String" Required="true" />
            <FileName ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System" />
            <Code Type="Fragment" Language="cs">
            <![CDATA[
            FileName = System.IO.Path.GetFileName(Path);
            ]]>
            </Code>
        </Task>
    </UsingTask>

    <Target Name="Demo">
        <PathCombine Paths="$(Temp);MyFolder;$([System.Guid]::NewGuid()).txt">
            <Output TaskParameter="Combined" PropertyName="MyCombinedPaths" />
        </PathCombine>

        <Message Text="Combined Paths: '$(MyCombinedPaths)'" />

        <PathGetFileName Path="$(MyCombinedPaths)">
            <Output TaskParameter="FileName" PropertyName="MyFileName" />
        </PathGetFileName>

        <Message Text="File name: '$(MyFileName)'" />
    </Target>
</Project>

Visszamenőleges kompatibilitás biztosítása

RoslynCodeTaskFactory először elérhetővé vált az MSBuild 15.8-ás verziójában. Tegyük fel, hogy támogatni szeretné a Visual Studio és az MSBuild korábbi verzióit, amikor RoslynCodeTaskFactory nem volt elérhető, de CodeTaskFactory az volt, de ugyanazt a buildszkriptet szeretné használni. Egy olyan szerkezetet használhat Choose , amely a tulajdonságot használja annak $(MSBuildVersion) eldöntésére, hogy a létrehozáskor használja-e a RoslynCodeTaskFactory tulajdonságot, vagy visszaesik-e CodeTaskFactorya következő példához:

<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="RunTask">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <Choose>
    <When Condition=" '$(MSBuildVersion.Substring(0,2))' >= 16 Or
    ('$(MSBuildVersion.Substring(0,2))' == 15 And '$(MSBuildVersion.Substring(3,1))' >= 8)">
      <PropertyGroup>
        <TaskFactory>RoslynCodeTaskFactory</TaskFactory>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <TaskFactory>CodeTaskFactory</TaskFactory>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  
  <UsingTask
    TaskName="HelloWorld"
    TaskFactory="$(TaskFactory)"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup />
    <Task>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
         Log.LogError("Using RoslynCodeTaskFactory");
      ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="RunTask" AfterTargets="Build">
    <Message Text="MSBuildVersion: $(MSBuildVersion)"/>
    <Message Text="TaskFactory: $(TaskFactory)"/>
    <HelloWorld />
  </Target>

</Project>