Bagikan melalui


Membuat tugas sebaris MSBuild dengan RoslynCodeTaskFactory

RoslynCodeTaskFactory menggunakan kompilator Roslyn lintas platform untuk menghasilkan rakitan tugas dalam memori untuk digunakan sebagai tugas sebaris. RoslynCodeTaskFactory tugas menargetkan .NET Standard dan dapat bekerja pada runtime .NET Framework dan .NET Core serta platform lain seperti Linux dan macOS.

Nota

RoslynCodeTaskFactory hanya tersedia di MSBuild 15.8 ke atas. Versi MSBuild mengikuti versi Visual Studio, sehingga RoslynCodeTaskFactory tersedia di Visual Studio 2017 versi 15.8 dan yang lebih tinggi.

Struktur tugas sebaris dengan RoslynCodeTaskFactory

RoslynCodeTaskFactory tugas sebaris dideklarasikan dengan menggunakan UsingTask elemen . Tugas sebaris dan UsingTask elemen yang berisinya biasanya disertakan dalam .targets file dan diimpor ke file proyek lain sesuai kebutuhan. Berikut adalah tugas sebaris dasar. Perhatikan bahwa itu tidak melakukan apa-apa.

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

Elemen UsingTask dalam contoh memiliki tiga atribut yang menjelaskan tugas dan pabrik tugas sebaris yang mengkompilasinya.

  • Atribut TaskName menamai tugas, dalam hal ini, DoNothing.

  • Atribut menamai TaskFactory kelas yang mengimplementasikan pabrik tugas sebaris.

  • Atribut AssemblyFile memberikan lokasi pabrik tugas sebaris. Atau, Anda dapat menggunakan AssemblyName atribut untuk menentukan nama yang sepenuhnya memenuhi syarat dari kelas pabrik tugas sebaris, yang biasanya terletak di cache perakitan global (GAC).

Elemen DoNothing tugas yang tersisa kosong dan disediakan untuk mengilustrasikan urutan dan struktur tugas sebaris. Contoh yang lebih kuat disajikan nanti dalam artikel ini.

  • Elemen ParameterGroup adalah opsional. Ketika ditentukan, ia mendeklarasikan parameter untuk tugas. Untuk informasi selengkapnya tentang parameter input dan output, lihat Parameter Input dan Output nanti di artikel ini.

  • Elemen menjelaskan Task dan berisi kode sumber tugas.

  • Elemen Reference menentukan referensi ke rakitan .NET yang Anda gunakan dalam kode Anda. Ini setara dengan menambahkan referensi ke proyek di Visual Studio. Atribut Include menentukan jalur rakitan yang dirujuk.

  • Elemen mencantumkan Using namespace yang ingin Anda akses. Elemen ini menyerupan arahan using dalam Visual C#. Atribut Namespace menentukan namespace layanan yang akan disertakan.

Reference elemen dan Using bersifat agnostik bahasa. Tugas sebaris dapat ditulis dalam salah satu bahasa CodeDom .NET yang didukung, misalnya, Visual Basic atau Visual C#.

Nota

Elemen yang dimuat oleh Task elemen khusus untuk pabrik tugas, dalam hal ini, pabrik tugas kode.

Elemen kode

Elemen turunan terakhir yang muncul dalam Task elemen adalah Code elemen . Elemen Code berisi atau menemukan kode yang ingin Anda kompilasi ke dalam tugas. Apa yang Anda masukkan ke Code dalam elemen tergantung pada bagaimana Anda ingin menulis tugas.

Atribut Language menentukan bahasa tempat kode Anda ditulis. Nilai yang dapat diterima adalah cs untuk C#, vb untuk Visual Basic.

Atribut Type menentukan jenis kode yang ditemukan dalam Code elemen .

  • Jika nilainya Type adalah Class, maka Code elemen berisi kode untuk kelas yang berasal dari ITask antarmuka.

  • Jika nilainya Type adalah Method, maka kode mendefinisikan penimpaan ExecuteITask metode antarmuka.

  • Jika nilai Type adalah Fragment, maka kode mendefinisikan konten Execute metode , tetapi bukan tanda tangan atau return pernyataan.

Kode itu sendiri biasanya muncul antara <![CDATA[ penanda dan ]]> penanda. Karena kode berada di bagian CDATA, Anda tidak perlu khawatir tentang melarikan diri dari karakter yang dipesan, misalnya, "<" atau ">".

Atau, Anda dapat menggunakan Source atribut Code elemen untuk menentukan lokasi file yang berisi kode untuk tugas Anda. Kode dalam file sumber harus dari jenis yang ditentukan oleh Type atribut . Source Jika atribut ada, nilai Type defaultnya adalah Class. Jika Source tidak ada, nilai defaultnya adalah Fragment.

Nota

Jika Anda menentukan kelas tugas dalam file sumber, nama kelas harus setuju dengan TaskName atribut elemen UsingTask yang sesuai.

Halo Dunia

Berikut adalah tugas sebaris yang lebih kuat dengan RoslynCodeTaskFactory. Tugas HelloWorld menampilkan "Halo, dunia!" pada perangkat pengelogan kesalahan default, yang biasanya merupakan konsol sistem atau jendela Output Visual Studio. Elemen Reference dalam contoh disertakan hanya untuk ilustrasi.

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

Anda dapat menyimpan HelloWorld tugas dalam file bernama HelloWorld.targets, lalu memanggilnya dari proyek sebagai berikut.

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

Parameter input dan output

Parameter tugas sebaris adalah elemen turunan dari ParameterGroup sebuah elemen. Setiap parameter mengambil nama elemen yang mendefinisikannya. Kode berikut mendefinisikan parameter Text.

<ParameterGroup>
    <Text />
</ParameterGroup>

Parameter mungkin memiliki satu atau beberapa atribut ini:

  • Required adalah atribut opsional yang secara false default. Jika true, maka parameter diperlukan dan harus diberi nilai sebelum memanggil tugas.

  • ParameterType adalah atribut opsional yang secara System.String default. Ini dapat diatur ke jenis yang sepenuhnya memenuhi syarat yang merupakan item atau nilai yang dapat dikonversi ke dan dari string dengan menggunakan System.Convert.ChangeType. (Dengan kata lain, jenis apa pun yang dapat diteruskan ke dan dari tugas eksternal.)

  • Output adalah atribut opsional yang secara false default. Jika true, maka parameter harus diberi nilai sebelum dikembalikan dari metode Jalankan.

Contohnya,

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

mendefinisikan ketiga parameter ini:

  • Expression adalah parameter input yang diperlukan dari jenis System.String.

  • Files adalah parameter input daftar item yang diperlukan.

  • Tally adalah parameter output dari jenis System.Int32.

Code Jika elemen memiliki Type atribut Fragment atau Method, maka properti secara otomatis dibuat untuk setiap parameter. Dalam RoslynCodeTaskFactory, jika Code elemen memiliki Type atribut Class, maka Anda tidak perlu menentukan ParameterGroup, karena disimpulkan dari kode sumber (ini adalah perbedaan dari CodeTaskFactory). Jika tidak, properti harus dideklarasikan secara eksplisit dalam kode sumber tugas, dan harus sama persis dengan definisi parameternya.

Contoh

Tugas sebaris berikut mencatat beberapa pesan dan mengembalikan string.

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

Tugas sebaris ini dapat menggabungkan jalur dan mendapatkan nama file.

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

Memberikan kompatibilitas mundur

RoslynCodeTaskFactory pertama kali tersedia di MSBuild versi 15.8. Misalkan Anda ingin mendukung versi Visual Studio dan MSBuild sebelumnya, ketika RoslynCodeTaskFactory tidak tersedia, tetapi CodeTaskFactory itu, tetapi Anda ingin menggunakan skrip build yang sama. Anda dapat menggunakan Choose konstruksi yang menggunakan $(MSBuildVersion) properti untuk memutuskan pada waktu build apakah akan menggunakan RoslynCodeTaskFactory atau mundur ke CodeTaskFactory, seperti dalam contoh berikut:

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