Vytvoření vložené úlohy MSBuild pomocí RoslynCodeTaskFactory

Podobně jako CodeTaskFactory používá RoslynCodeTaskFactory kompilátory Roslyn pro různé platformy k vygenerování sestavení úloh v paměti pro použití jako vložené úlohy. Úlohy RoslynCodeTaskFactory cílí na .NET Standard a můžou pracovat na modulech runtime .NET Framework a .NET Core a také na jiných platformách, jako je Linux a macOS.

Poznámka:

RoslynCodeTaskFactory je k dispozici pouze v nástroji MSBuild 15.8 a vyšší. Verze NÁSTROJE MSBuild se řídí verzemi sady Visual Studio, takže roslynCodeTaskFactory je k dispozici v sadě Visual Studio 2017 verze 15.8 a vyšší.

Struktura vložené úlohy pomocí RoslynCodeTaskFactory

Vložené úlohy RoslynCodeTaskFactory jsou deklarovány stejným způsobem jako CodeTaskFactory, jediným rozdílem je, že cílí na .NET Standard. Vložený úkol a UsingTask prvek, který ho obsahuje, jsou obvykle zahrnuty do souboru .targets a podle potřeby importovány do jiných souborů projektu. Tady je základní vložený úkol. Všimněte si, že nic nedělá.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- 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>

Prvek UsingTask v příkladu má tři atributy, které popisují úkol a vloženou úlohu továrnu, která ji zkompiluje.

  • Atribut TaskName v tomto případě DoNothingpojmenuje úkol .

  • Atributy TaskFactory pojmenují třídu, která implementuje vloženou úlohu factory.

  • Atribut AssemblyFile poskytuje umístění vloženého objektu pro vytváření úloh. Případně můžete pomocí atributu AssemblyName zadat plně kvalifikovaný název vložené třídy objektu pro vytváření úloh, která se obvykle nachází v globální mezipaměti sestavení (GAC).

Zbývající prvky DoNothing úkolu jsou prázdné a jsou k dispozici pro ilustraci pořadí a struktury vloženého úkolu. Robustnější příklad je uveden dále v tomto tématu.

  • Prvek ParameterGroup je nepovinný. Při zadání deklaruje parametry pro úlohu. Další informace o vstupních a výstupních parametrech naleznete v části Vstupní a výstupní parametry dále v tomto tématu.

  • Prvek Task popisuje a obsahuje zdrojový kód úkolu.

  • Element Reference určuje odkazy na sestavení .NET, která používáte v kódu. To je ekvivalentem přidání odkazu na projekt v sadě Visual Studio. Atribut Include určuje cestu odkazovaného sestavení.

  • Tento Using prvek obsahuje seznam oborů názvů, ke kterým chcete získat přístup. Podobá se Using tomu příkaz v jazyce Visual C#. Atribut Namespace určuje obor názvů, který se má zahrnout.

Reference a Using prvky jsou nezávislé na jazyce. Vložené úlohy lze psát v některém z podporovaných jazyků .NET CodeDom, například v jazyce Visual Basic nebo Visual C#.

Poznámka:

Prvky obsažené v elementu Task jsou specifické pro objekt pro vytváření úloh, v tomto případě objekt pro vytváření úloh kódu.

Prvek kódu

Posledním podřízeným prvkem, který se má objevit uvnitř elementu TaskCode , je element. Prvek Code obsahuje nebo vyhledá kód, který chcete zkompilovat do úkolu. To, co vložíte do elementu Code , závisí na tom, jak chcete napsat úkol.

Atribut Language určuje jazyk, ve kterém je váš kód napsán. Přijatelné hodnoty jsou cs pro jazyk C# vb pro Visual Basic.

Atribut Type určuje typ kódu, který se nachází v elementu Code .

  • Pokud je Classhodnota Type , pak Code prvek obsahuje kód pro třídu, která je odvozena z ITask rozhraní.

  • Pokud je Methodhodnota Type , pak kód definuje přepsání Execute metody ITask rozhraní.

  • Pokud je Fragmenthodnota Type , pak kód definuje obsah Execute metody, ale ne podpis nebo return příkaz.

Samotný kód se obvykle zobrazuje mezi značkou <![CDATA[ a značkou ]]> . Protože kód je v části CDATA, nemusíte se starat o únik vyhrazených znaků, například "<" nebo ">".

Případně můžete pomocí Source atributu Code elementu určit umístění souboru, který obsahuje kód pro váš úkol. Kód ve zdrojovém souboru musí být typu, který je určen atributem Type . Source Pokud atribut existuje, výchozí hodnota Type je Class. Pokud Source není k dispozici, výchozí hodnota je Fragment.

Poznámka:

Při definování třídy úkolu ve zdrojovém souboru musí název třídy souhlasit s TaskName atributem odpovídajícího UsingTask elementu.

Hello World

Tady je robustnější vložený úkol s RoslynCodeTaskFactory. Úloha HelloWorld zobrazí "Hello, world!" na výchozím zařízení protokolování chyb, což je obvykle systémová konzola nebo okno Výstup sady Visual Studio. Prvek Reference v příkladu je zahrnut pouze pro ilustraci.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- 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>

Úkol HelloWorld můžete uložit do souboru s názvem HelloWorld.targets a pak ho vyvolat z projektu následujícím způsobem.

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="HelloWorld.targets" />
  <Target Name="Hello">
    <HelloWorld />
  </Target>
</Project>

Vstupní a výstupní parametry

Vložené parametry úkolu jsou podřízené prvky elementu ParameterGroup . Každý parametr přebírá název prvku, který ho definuje. Následující kód definuje parametr Text.

<ParameterGroup>
    <Text />
</ParameterGroup>

Parametry můžou mít jeden nebo více těchto atributů:

  • Required je volitelný atribut, který je false ve výchozím nastavení. Pokud trueje parametr povinný a musí být před voláním úkolu uvedena hodnota.

  • ParameterType je volitelný atribut, který je System.String ve výchozím nastavení. Může být nastaven na libovolný plně kvalifikovaný typ, který je buď položka nebo hodnota, která lze převést na řetězec pomocí System.Convert.ChangeType. (Jinými slovy, jakýkoli typ, který lze předat externímu úkolu a z externího úkolu.)

  • Output je volitelný atribut, který je false ve výchozím nastavení. Pokud true, pak parametr musí být před vrácením z Execute metoda.

Příklad:

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

definuje tyto tři parametry:

  • Expression je povinný vstupní parametr typu System.String.

  • Files je povinný vstupní parametr seznamu položek.

  • Tally je výstupní parametr typu System.Int32.

Pokud prvek CodeType atribut Fragment nebo Method, vlastnosti jsou automaticky vytvořeny pro každý parametr. V RoslynCodeTaskFactory, pokud Code prvek má Type atribut Class, pak nemusíte zadávat ParameterGroup, protože je odvozen ze zdrojového kódu (to je rozdíl od CodeTaskFactory). V opačném případě musí být vlastnosti explicitně deklarovány ve zdrojovém kódu úlohy a musí přesně odpovídat jejich definici parametrů.

Příklad

Následující vložená úloha zaznamená některé zprávy a vrátí řetězec.

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0">

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

Tyto vložené úkoly mohou kombinovat cesty a získat název souboru.

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0">

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

Zajištění zpětné kompatibility

RoslynCodeTaskFactory poprvé začal být k dispozici ve verzi 15.8 nástroje MSBuild. Předpokládejme, že máte situaci, kdy chcete podporovat předchozí verze sady Visual Studio a MSBuild, pokud RoslynCodeTaskFactory nebyly dostupné, ale CodeTaskFactory chcete použít stejný skript sestavení. Můžete použít Choose konstruktor, který používá $(MSBuildVersion) vlastnost k rozhodnutí v době sestavení, zda použít RoslynCodeTaskFactory nebo se vrátit zpět CodeTaskFactory, jako v následujícím příkladu:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</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>