RoslynCodeTaskFactory ile BIR MSBuild satır içi görevi oluşturma

CodeTaskFactory'ye benzer şekilde, RoslynCodeTaskFactory de satır içi görevler olarak kullanılmak üzere bellek içi görev derlemeleri oluşturmak üzere platformlar arası Roslyn derleyicilerini kullanır. RoslynCodeTaskFactory görevleri .NET Standard'ı hedefler ve .NET Framework ve .NET Core çalışma zamanlarının yanı sıra Linux ve macOS gibi diğer platformlarda da çalışabilir.

Dekont

RoslynCodeTaskFactory yalnızca MSBuild 15.8 ve üzerinde kullanılabilir. MSBuild sürümleri Visual Studio sürümlerini izler; bu nedenle RoslynCodeTaskFactory, Visual Studio 2017 sürüm 15.8 ve üzeri sürümlerde kullanılabilir.

RoslynCodeTaskFactory ile satır içi görevin yapısı

RoslynCodeTaskFactory satır içi görevleri CodeTaskFactory ile aynı şekilde bildirilir, tek fark .NET Standard'ı hedeflemeleridir. Satır içi görev ve UsingTask onu içeren öğe genellikle bir .targets dosyasına eklenir ve gerektiğinde diğer proje dosyalarına içeri aktarılır. Burada temel bir satır içi görev yer alır. Hiçbir şey yapmaz.

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

UsingTask Örnekteki öğesi, görevi açıklayan üç özniteliği ve onu derleyen satır içi görev fabrikasına sahiptir.

  • TaskName Özniteliği, bu durumda DoNothinggörevi olarak adlandırın.

  • özniteliği, TaskFactory satır içi görev fabrikasını uygulayan sınıfı adlandırıyor.

  • AssemblyFile özniteliği satır içi görev fabrikasının konumunu verir. Alternatif olarak, genellikle genel derleme önbelleğinde AssemblyName (GAC) bulunan satır içi görev fabrikası sınıfının tam adını belirtmek için özniteliğini kullanabilirsiniz.

Görevin kalan öğeleri DoNothing boş ve satır içi görevin sırasını ve yapısını göstermek için sağlanır. Bu konunun ilerleyen bölümlerinde daha sağlam bir örnek verilmiştir.

  • ParameterGroup öğesi isteğe bağlıdır. Belirtildiğinde, görevin parametrelerini bildirir. Giriş ve çıkış parametreleri hakkında daha fazla bilgi için bu konunun devamında yer alan Giriş ve Çıkış Parametreleri bölümüne bakın.

  • Task öğesi görev kaynak kodunu açıklar ve içerir.

  • öğesi, Reference kodunuzda kullandığınız .NET derlemelerine başvuruları belirtir. Bu, Visual Studio'da bir projeye başvuru eklemeye eşdeğerdir. özniteliği, Include başvuruda bulunılan derlemenin yolunu belirtir.

  • öğesi, Using erişmek istediğiniz ad alanlarını listeler. Bu, Visual C# deyimine Using benzer. Namespace özniteliği, eklenecek ad alanını belirtir.

Reference ve Using öğeleri dil-agnostic. Satır içi görevler desteklenen .NET CodeDom dillerinden herhangi birinde yazılabilir; örneğin, Visual Basic veya Visual C# .

Dekont

öğesinin Task içerdiği öğeler, bu örnekte kod görev fabrikası olan görev fabrikasına özeldir.

Kod öğesi

öğesi içinde Task görünecek son alt öğe öğesidir Code . öğesi, Code bir görevde derlenmiş olmasını istediğiniz kodu içerir veya bulur. öğesine ne koyacağınız Code , görevi nasıl yazmak istediğinize bağlıdır.

Language özniteliği kodunuzun yazıldığı dili belirtir. Visual Basic için C# vb için kabul edilebilir değerlerdircs.

Type özniteliği, öğesinde Code bulunan kodun türünü belirtir.

  • değeri Type iseClassCode, öğesi arabiriminden türetilen bir sınıfın ITask kodunu içerir.

  • değeri Type ise Methodkod, arabiriminin Execute yönteminin geçersiz kılmasını ITask tanımlar.

  • değeri Type ise Fragment, kod yöntemin Execute içeriğini tanımlar, ancak imzayı veya deyimini return tanımlamaz.

Kodun kendisi genellikle bir <![CDATA[ işaretçi ile ]]> işaretçi arasında görünür. Kod bir CDATA bölümünde olduğundan, "" veya> "<" gibi ayrılmış karakterlerden kaçış konusunda endişelenmeniz gerekmez.

Alternatif olarak, öğesinin SourceCode özniteliğini kullanarak görevinizin kodunu içeren bir dosyanın konumunu belirtebilirsiniz. Kaynak dosyadaki kod özniteliği tarafından Type belirtilen türde olmalıdır. Source Özniteliği varsa, varsayılan değeri Type olurClass. Yoksa Source , varsayılan değer olur Fragment.

Dekont

Kaynak dosyada görev sınıfını tanımlarken, sınıf adı ilgili UsingTask öğesinin özniteliğini TaskName kabul etmelidir.

Hello World

RoslynCodeTaskFactory ile daha sağlam bir satır içi görev aşağıdadır. HelloWorld görevi, genellikle sistem konsolu veya Visual Studio Çıkış penceresi olan varsayılan hata günlüğü cihazında "Hello, world!" ifadesini görüntüler. Reference Örnekteki öğe yalnızca çizim için eklenmiştir.

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

HelloWorld görevini HelloWorld.targets adlı bir dosyaya kaydedebilir ve sonra bir projeden aşağıdaki gibi çağırabilirsiniz.

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

Giriş ve çıkış parametreleri

Satır içi görev parametreleri bir ParameterGroup öğenin alt öğeleridir. Her parametre, onu tanımlayan öğenin adını alır. Aşağıdaki kod parametresini Texttanımlar.

<ParameterGroup>
    <Text />
</ParameterGroup>

Parametreler şu özniteliklerden birine veya daha fazlasına sahip olabilir:

  • Required varsayılan olarak isteğe bağlı bir özniteliktir false . ise true, parametre gereklidir ve görevi çağırmadan önce bir değer verilmelidir.

  • ParameterType varsayılan olarak isteğe bağlı bir özniteliktir System.String . System.Convert.ChangeType kullanılarak bir dizeye ve dizeden dönüştürülebilen bir öğe veya değer olan herhangi bir tam türe ayarlanabilir. (Başka bir deyişle, dış göreve ve dış görevden geçirilebilen herhangi bir tür.)

  • Output varsayılan olarak isteğe bağlı bir özniteliktir false . ise trueExecute yönteminden dönmeden önce parametresine bir değer verilmelidir.

Örneğin:

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

şu üç parametreyi tanımlar:

  • Expression , System.String türünde gerekli bir giriş parametresidir.

  • Files gerekli bir öğe listesi giriş parametresidir.

  • Tally System.Int32 türünde bir çıkış parametresidir.

Code öğesi veya Methodözniteliğine TypeFragment sahipse, her parametre için otomatik olarak özellikler oluşturulur. RoslynCodeTaskFactory'de, öğesi özniteliğine sahipse CodeType, kaynak koddan çıkarılmadığından öğesini belirtmeniz ParameterGroupgerekmez (bu , ile CodeTaskFactoryClassarasındaki farktır). Aksi takdirde, özelliklerin görev kaynak kodunda açıkça bildirilmesi ve parametre tanımlarıyla tam olarak eşleşmesi gerekir.

Örnek

Aşağıdaki satır içi görev bazı iletileri günlüğe kaydeder ve bir dize döndürür.

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

Bu satır içi görevler yolları birleştirebilir ve dosya adını alabilir.

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

Geriye dönük uyumluluk sağlama

RoslynCodeTaskFactory ilk olarak MSBuild sürüm 15.8'de kullanılabilir hale geldi. Visual Studio ve MSBuild'in RoslynCodeTaskFactory önceki sürümlerini desteklemek istediğiniz bir durumla karşılaştığınızı, ancak mevcut olmadığını, ancak CodeTaskFactory aynı derleme betiğini kullanmak istediğinizi varsayalım. Aşağıdaki örnekte olduğu gibi derleme zamanında öğesinin mi kullanılacağına RoslynCodeTaskFactory yoksa öğesine geri mi döneceğine karar vermek için CodeTaskFactoryözelliğini kullanan $(MSBuildVersion) bir Choose yapı kullanabilirsiniz:

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