Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dalam tutorial ini, Anda akan membuat tugas kustom di MSBuild di C# yang menangani pembuatan kode, lalu Anda akan menggunakan tugas dalam build. Contoh ini menunjukkan cara menggunakan MSBuild untuk menangani operasi bersih dan membangun kembali. Contoh ini juga menunjukkan cara mendukung build incremental, sehingga kode dihasilkan hanya ketika file input telah berubah. Teknik yang ditunjukkan berlaku untuk berbagai skenario pembuatan kode. Langkah-langkah ini juga menunjukkan penggunaan NuGet untuk mengemas tugas distribusi, dan tutorial menyertakan langkah opsional untuk menggunakan penampil BinLog untuk meningkatkan pengalaman pemecahan masalah.
Prasyarat
Anda harus memiliki pemahaman tentang konsep MSBuild seperti tugas, target, dan properti. Lihat konsep MSBuild.
Contohnya memerlukan MSBuild, yang diinstal dengan Visual Studio, tetapi juga dapat diinstal secara terpisah. Lihat Unduh MSBuild tanpa Visual Studio.
Pengantar contoh kode
Contoh mengambil file teks input yang berisi nilai yang akan diatur, dan membuat file kode C# dengan kode yang membuat nilai-nilai ini. Meskipun itu adalah contoh sederhana, teknik dasar yang sama dapat diterapkan ke skenario pembuatan kode yang lebih kompleks.
Dalam tutorial ini, Anda akan membuat tugas kustom MSBuild bernama AppSettingStronglyTyped. Tugas akan membaca sekumpulan file teks, dan setiap file dengan baris dengan format berikut:
propertyName:type:defaultValue
Kode menghasilkan kelas C# dengan semua konstanta. Masalah harus menghentikan build dan memberikan informasi yang cukup kepada pengguna untuk mendiagnosis masalah.
Kode sampel lengkap untuk tutorial ini ada di Tugas kustom - pembuatan kode dalam repositori sampel .NET di GitHub.
Membuat proyek AppSettingStronglyTyped
Buat Pustaka Kelas Standar .NET. Kerangka kerja harus .NET Standard 2.0.
Perhatikan perbedaan antara MSBuild penuh (yang digunakan Visual Studio) dan MSBuild portabel, yang dibundel di Baris Perintah .NET Core.
- MSBuild Lengkap: Versi MSBuild ini biasanya berada di dalam Visual Studio. Berjalan pada .NET Framework. Visual Studio menggunakan ini saat Anda menjalankan Build pada solusi atau proyek Anda. Versi ini juga tersedia dari lingkungan antarmuka baris perintah, seperti Visual Studio Developer Command Prompt (Prompt Perintah Pengembang Visual Studio), atau PowerShell.
- .NET MSBuild: Versi MSBuild ini dibundel di Baris Perintah .NET Core. Ini berjalan pada .NET Core. Visual Studio tidak secara langsung memanggil versi MSBuild ini. Ini hanya mendukung proyek yang membangun menggunakan Microsoft.NET.Sdk.
jika Anda ingin berbagi kode antara .NET Framework dan implementasi .NET lainnya, seperti .NET Core, pustaka Anda harus menargetkan .NET Standard 2.0, dan Anda ingin menjalankannya di dalam Visual Studio, yang berjalan pada .NET Framework. .NET Framework tidak mendukung .NET Standard 2.1.
Pilih versi MSBuild API untuk dirujuk
Saat mengkompilasi tugas kustom, Anda harus mereferensikan versi MSBuild API (Microsoft.Build.*) yang cocok dengan versi minimum Visual Studio dan/atau .NET SDK yang ingin Anda dukung. Misalnya, untuk mendukung pengguna di Visual Studio 2019, Anda harus membangun terhadap MSBuild 16.11.
Buat tugas kustom AppSettingStronglyTyped MSBuild
Langkah pertama adalah membuat tugas kustom MSBuild. Informasi tentang cara menulis tugas kustom MSBuild mungkin membantu Anda memahami langkah-langkah berikut. Tugas kustom MSBuild adalah kelas yang mengimplementasikan antarmuka ITask.
Tambahkan referensi ke paket Microsoft.Build.Utilities.Core NuGet, lalu buat kelas bernama AppSettingStronglyTyped yang berasal dari Microsoft.Build.Utilities.Task.
Tambahkan tiga properti. Properti ini menentukan parameter tugas yang ditetapkan pengguna saat mereka menggunakan tugas dalam proyek klien:
//The name of the class which is going to be generated [Required] public string SettingClassName { get; set; } //The name of the namespace where the class is going to be generated [Required] public string SettingNamespaceName { get; set; } //List of files which we need to read with the defined format: 'propertyName:type:defaultValue' per line [Required] public ITaskItem[] SettingFiles { get; set; }Tugas memproses SettingFiles dan menghasilkan kelas
SettingNamespaceName.SettingClassName. Kelas yang dihasilkan akan memiliki sekumpulan konstanta berdasarkan konten file teks.Output tugas harus berupa string yang memberikan nama file kode yang dihasilkan:
// The filename where the class was generated [Output] public string ClassNameFile { get; set; }Saat Anda membuat tugas kustom, Anda mewarisi dari Microsoft.Build.Utilities.Task. Untuk menerapkan tugas, Anda mengambil alih metode Execute(). Metode
Executemengembalikantruejika tugas berhasil, danfalsesebaliknya.Taskmenerapkan Microsoft.Build.Framework.ITask dan menyediakan implementasi default dari beberapa anggotaITaskdan juga, menyediakan beberapa fungsionalitas pengelogan. Penting untuk mengeluarkan status ke log untuk mendiagnosis dan memecahkan masalah tugas, terutama jika masalah terjadi dan tugas harus mengembalikan hasil kesalahan (false). Jika terjadi kesalahan, kelas memberi sinyal kesalahan dengan memanggil TaskLoggingHelper.LogError.public override bool Execute() { //Read the input files and return a IDictionary<string, object> with the properties to be created. //Any format error it will return false and log an error var (success, settings) = ReadProjectSettingFiles(); if (!success) { return !Log.HasLoggedErrors; } //Create the class based on the Dictionary success = CreateSettingClass(settings); return !Log.HasLoggedErrors; }Task API memungkinkan mengembalikan nilai false, menunjukkan kegagalan, tanpa memberi tahu pengguna apa yang salah. Sebaiknya mengembalikan
!Log.HasLoggedErrorsalih-alih kode boolean, dan mencatat kesalahan ketika ada yang tidak beres.
Catat kesalahan
Praktik terbaik saat mencatat kesalahan adalah memberikan detail seperti nomor baris dan kode kesalahan yang berbeda saat mencatat kesalahan. Kode berikut mengurai file input teks dan menggunakan metode TaskLoggingHelper.LogError dengan nomor baris dalam file teks yang menghasilkan kesalahan.
private (bool, IDictionary<string, object>) ReadProjectSettingFiles()
{
var values = new Dictionary<string, object>();
foreach (var item in SettingFiles)
{
int lineNumber = 0;
var settingFile = item.GetMetadata("FullPath");
foreach (string line in File.ReadLines(settingFile))
{
lineNumber++;
var lineParse = line.Split(':');
if (lineParse.Length != 3)
{
Log.LogError(subcategory: null,
errorCode: "APPS0001",
helpKeyword: null,
file: settingFile,
lineNumber: lineNumber,
columnNumber: 0,
endLineNumber: 0,
endColumnNumber: 0,
message: "Incorrect line format. Valid format prop:type:defaultvalue");
return (false, null);
}
var value = GetValue(lineParse[1], lineParse[2]);
if (!value.Item1)
{
return (value.Item1, null);
}
values[lineParse[0]] = value.Item2;
}
}
return (true, values);
}
Dengan menggunakan teknik yang ditunjukkan dalam kode sebelumnya, kesalahan dalam sintaks file masukan teks akan muncul sebagai kesalahan build dengan informasi diagnostik yang bermanfaat.
Microsoft (R) Build Engine version 17.2.0 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 2/16/2022 10:23:24 AM.
Project "S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" on node 1 (default targets).
S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\error-prop.setting(1): error APPS0001: Incorrect line format. Valid format prop:type:defaultvalue [S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild]
Done Building Project "S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" (default targets) -- FAILED.
Build FAILED.
"S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild" (default target) (1) ->
(generateSettingClass target) ->
S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\error-prop.setting(1): error APPS0001: Incorrect line format. Valid format prop:type:defaultvalue [S:\work\msbuild-examples\custom-task-code-generation\AppSettingStronglyTyped\AppSettingStronglyTyped.Test\bin\Debug\net6.0\Resources\testscript-fail.msbuild]
0 Warning(s)
1 Error(s)
Saat Anda menangkap pengecualian dalam tugas Anda, gunakan metode TaskLoggingHelper.LogErrorFromException. Ini akan meningkatkan output kesalahan, misalnya dengan mendapatkan call stack tempat pengecualian terjadi.
catch (Exception ex)
{
// This logging helper method is designed to capture and display information
// from arbitrary exceptions in a standard way.
Log.LogErrorFromException(ex, showStackTrace: true);
return false;
}
Implementasi metode lain yang menggunakan input ini untuk membangun teks untuk file kode yang dihasilkan tidak ditampilkan di sini; lihat AppSettingStronglyTyped.cs dalam repositori sampel.
Contoh kode menghasilkan kode C# selama proses build. Tugas ini seperti kelas C# lainnya, jadi ketika Anda selesai dengan tutorial ini, Anda dapat menyesuaikannya dan menambahkan fungsionalitas apa pun yang diperlukan untuk skenario Anda sendiri.
Membuat aplikasi konsol dan menggunakan tugas kustom
Di bagian ini, Anda akan membuat Aplikasi Konsol Inti .NET standar yang menggunakan tugas.
Penting
Penting untuk menghindari pembuatan tugas kustom MSBuild dalam proses MSBuild yang sama yang akan menggunakannya. Proyek baru harus berada dalam solusi Visual Studio yang benar-benar berbeda, atau proyek baru menggunakan DLL yang telah dibuat sebelumnya dan dipindahkan dari hasil output standar.
Buat proyek Konsol .NET MSBuildConsoleExample di Solusi Visual Studio baru.
Cara normal untuk mendistribusikan tugas adalah melalui paket NuGet, tetapi selama pengembangan dan penelusuran kesalahan, Anda dapat menyertakan semua informasi tentang
.propsdan.targetslangsung dalam file proyek aplikasi Anda dan kemudian pindah ke format NuGet saat Anda mendistribusikan tugas kepada orang lain.Ubah file proyek untuk menggunakan tugas pembuatan kode. Daftar kode di bagian ini menunjukkan file proyek yang dimodifikasi setelah merujuk tugas, mengatur parameter input untuk tugas, dan menulis target untuk menangani operasi bersih dan bangun kembali sehingga file kode yang dihasilkan dihapus seperti yang Anda harapkan.
Tugas didaftarkan menggunakan elemen UsingTask (MSBuild). Elemen
UsingTaskmendaftarkan tugas; ini memberi tahu MSBuild nama tugas dan cara menemukan dan menjalankan rakitan yang berisi kelas tugas. Jalur perakitan relatif terhadap file proyek.PropertyGroupberisi definisi properti yang sesuai dengan properti yang ditentukan dalam tugas. Properti ini diatur menggunakan atribut, dan nama tugas digunakan sebagai nama elemen.TaskNameadalah nama tugas yang dirujuk dari perakitan. Atribut ini harus selalu menggunakan namespace yang ditentukan sepenuhnya.AssemblyFileadalah jalur berkas assembly.Untuk memanggil tugas, tambahkan tugas ke target yang sesuai, dalam hal ini
GenerateSetting.Target
ForceGenerateOnRebuildmenangani operasi pembersihan dan pembaruan dengan menghapus file yang dihasilkan. Ini dirancang untuk berjalan setelah targetCoreCleandengan mengatur atributAfterTargetskeCoreClean.<Project Sdk="Microsoft.NET.Sdk"> <UsingTask TaskName="AppSettingStronglyTyped.AppSettingStronglyTyped" AssemblyFile="..\..\AppSettingStronglyTyped\AppSettingStronglyTyped\bin\Debug\netstandard2.0\AppSettingStronglyTyped.dll"/> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <RootFolder>$(MSBuildProjectDirectory)</RootFolder> <SettingClass>MySetting</SettingClass> <SettingNamespace>MSBuildConsoleExample</SettingNamespace> <SettingExtensionFile>mysettings</SettingExtensionFile> </PropertyGroup> <ItemGroup> <SettingFiles Include="$(RootFolder)\*.mysettings" /> </ItemGroup> <Target Name="GenerateSetting" BeforeTargets="CoreCompile" Inputs="@(SettingFiles)" Outputs="$(RootFolder)\$(SettingClass).generated.cs"> <AppSettingStronglyTyped SettingClassName="$(SettingClass)" SettingNamespaceName="$(SettingNamespace)" SettingFiles="@(SettingFiles)"> <Output TaskParameter="ClassNameFile" PropertyName="SettingClassFileName" /> </AppSettingStronglyTyped> <ItemGroup> <Compile Remove="$(SettingClassFileName)" /> <Compile Include="$(SettingClassFileName)" /> </ItemGroup> </Target> <Target Name="ForceReGenerateOnRebuild" AfterTargets="CoreClean"> <Delete Files="$(RootFolder)\$(SettingClass).generated.cs" /> </Target> </Project>Nota
Alih-alih menggantikan target seperti
CoreClean, kode ini menggunakan cara lain untuk mengatur urutan target (BeforeTarget dan AfterTarget). Proyek bergaya SDK memiliki impor implisit target setelah baris terakhir file proyek; ini berarti Anda tidak dapat mengambil alih target default kecuali Anda menentukan impor Anda secara manual. Lihat Mengambil alih target yang telah ditentukan sebelumnya.Atribut
InputsdanOutputsmembantu MSBuild menjadi lebih efisien dengan memberikan informasi untuk build bertahap. Tanggal input dibandingkan dengan output untuk melihat apakah target perlu dijalankan, atau apakah output build sebelumnya dapat digunakan kembali.Buat file teks input dengan ekstensi yang ditentukan agar dapat ditemukan. Dengan menggunakan ekstensi default, buat
MyValues.mysettingsdi root, dengan konten berikut:Greeting:string:Hello World!Bangun ulang, dan file yang dihasilkan harus dibuat dan dikompilasi. Periksa folder proyek untuk file MySetting.generated.cs.
Kelas mySetting berada di namespace yang salah, jadi sekarang buat perubahan untuk menggunakan namespace aplikasi kami. Buka file proyek dan tambahkan kode berikut:
<PropertyGroup> <SettingNamespace>MSBuildConsoleExample</SettingNamespace> </PropertyGroup>Bangun ulang, dan amati bahwa kelas berada di namespace
MSBuildConsoleExample. Dengan cara ini, Anda dapat menentukan ulang nama kelas yang dihasilkan (SettingClass), file ekstensi teks (SettingExtensionFile) untuk digunakan sebagai input, dan lokasi (RootFolder) jika Anda mau.Buka
Program.csdan ubah 'Halo Dunia!!' yang di-hardcode. ke konstanta yang ditentukan pengguna:static void Main(string[] args) { Console.WriteLine(MySetting.Greeting); }
Jalankan program; itu akan mencetak salam dari kelas yang dihasilkan.
(Opsional) Mencatat peristiwa selama proses build
Dimungkinkan untuk mengkompilasi menggunakan perintah baris perintah. Navigasikan ke folder proyek. Anda akan menggunakan opsi -bl (log biner) untuk menghasilkan log biner. Log biner akan memiliki informasi yang berguna untuk mengetahui apa yang terjadi selama proses build.
# Using dotnet MSBuild (run core environment)
dotnet build -bl
# or full MSBuild (run on net framework environment; this is used by Visual Studio)
msbuild -bl
Kedua perintah menghasilkan file log msbuild.binlog, yang dapat dibuka dengan MSBuild Biner dan Penampil Log Terstruktur. Opsi /t:rebuild berarti menjalankan target pembangunan ulang. Ini akan memaksa regenerasi file kode yang dihasilkan.
Selamat! Anda telah membangun tugas yang menghasilkan kode, dan menggunakannya dalam build.
Mengemas tugas untuk distribusi
Jika Anda hanya perlu menggunakan tugas kustom Anda dalam beberapa proyek atau dalam satu solusi, menggunakan tugas sebagai rakitan mentah mungkin semua yang Anda butuhkan, tetapi cara terbaik untuk menyiapkan tugas Anda untuk menggunakannya di tempat lain atau membagikannya dengan orang lain adalah sebagai paket NuGet.
Paket Tugas MSBuild memiliki beberapa perbedaan utama dari pustaka NuGet:
- Mereka harus membundel dependensi perangkaian mereka sendiri, daripada mengekspos dependensi tersebut ke proyek konsumen.
- Mereka tidak mengemas rakitan yang diperlukan ke folder
lib/<target framework>, karena itu akan menyebabkan NuGet menyertakan rakitan dalam paket apa pun yang menggunakan tugas - Mereka hanya perlu mengkompilasi terhadap rakitan Microsoft.Build - pada runtime ini akan disediakan oleh mesin MSBuild yang sebenarnya sehingga tidak perlu disertakan dalam paket
- Mereka menghasilkan file
.deps.jsonkhusus yang membantu MSBuild memuat dependensi Tugas (terutama dependensi asli) dengan cara yang konsisten
Untuk mencapai semua tujuan ini, Anda harus melakukan beberapa perubahan lebih lanjut pada file proyek standar yang mungkin sudah Anda kenal.
Membuat paket NuGet
Membuat paket NuGet adalah cara yang disarankan untuk mendistribusikan tugas kustom Anda kepada orang lain.
Bersiap untuk menghasilkan paket
Untuk bersiap menghasilkan paket NuGet, buat beberapa perubahan pada file proyek untuk menentukan detail yang menjelaskan paket. File proyek awal yang Anda buat menyerupai kode berikut.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" />
</ItemGroup>
</Project>
Untuk menghasilkan paket NuGet, tambahkan kode berikut untuk mengatur properti untuk paket. Anda dapat melihat daftar lengkap properti MSBuild yang didukung dalam dokumentasi Paket :
<PropertyGroup>
...
<IsPackable>true</IsPackable>
<Version>1.0.0</Version>
<Title>AppSettingStronglyTyped</Title>
<Authors>Your author name</Authors>
<Description>Generates a strongly typed setting class base on a text file.</Description>
<PackageTags>MyTags</PackageTags>
<Copyright>Copyright ©Contoso 2022</Copyright>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
...
</PropertyGroup>
Properti CopyLocalLockFileAssemblies diperlukan untuk memastikan bahwa dependensi disalin ke direktori output.
Menandai dependensi sebagai pribadi
Dependensi tugas MSBuild Anda harus dibungkus di dalam paket; mereka tidak dapat diekspresikan sebagai referensi paket normal. Paket tidak akan mengekspos dependensi reguler apa pun kepada pengguna eksternal. Ini membutuhkan dua langkah untuk dicapai: menandai rakitan Anda sebagai privat dan benar-benar menyematkannya dalam paket yang dihasilkan. Untuk contoh ini, kami akan berasumsi bahwa tugas Anda bergantung pada Microsoft.Extensions.DependencyInjection berfungsi, jadi tambahkan PackageReference ke Microsoft.Extensions.DependencyInjection pada versi 6.0.0.
<ItemGroup>
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0" />
<PackageReference
Include="Microsoft.Extensions.DependencyInjection"
Version="6.0.0" />
</ItemGroup>
Sekarang, tandai setiap dependensi proyek Tugas ini, baik PackageReference maupun ProjectReference dengan atribut PrivateAssets="all". Ini akan memberi tahu NuGet untuk tidak mengekspos dependensi ini ke proyek yang menggunakannya. Anda dapat membaca selengkapnya tentang mengontrol aset dependensi di dokumentasi NuGet.
<ItemGroup>
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0"
PrivateAssets="all"
/>
<PackageReference
Include="Microsoft.Extensions.DependencyInjection"
Version="6.0.0"
PrivateAssets="all"
/>
</ItemGroup>
Satukan dependensi ke dalam paket
Anda juga harus menyematkan aset runtime dependensi kami ke dalam Paket Tugas. Ada dua bagian untuk ini: target MSBuild yang menambahkan dependensi kita ke BuildOutputInPackage ItemGroup, dan beberapa properti yang mengontrol tata letak item BuildOutputInPackage tersebut. Anda dapat mempelajari selengkapnya tentang proses ini dalam dokumentasi NuGet.
<PropertyGroup>
...
<!-- This target will run when MSBuild is collecting the files to be packaged, and we'll implement it below. This property controls the dependency list for this packaging process, so by adding our custom property we hook ourselves into the process in a supported way. -->
<TargetsForTfmSpecificBuildOutput>
$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
</TargetsForTfmSpecificBuildOutput>
<!-- This property tells MSBuild where the root folder of the package's build assets should be. Because we are not a library package, we should not pack to 'lib'. Instead, we choose 'tasks' by convention. -->
<BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
<!-- NuGet does validation that libraries in a package are exposed as dependencies, but we _explicitly_ do not want that behavior for MSBuild tasks. They are isolated by design. Therefore we ignore this specific warning. -->
<NoWarn>NU5100</NoWarn>
<!-- Suppress NuGet warning NU5128. -->
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
...
</PropertyGroup>
...
<!-- This is the target we defined above. It's purpose is to add all of our PackageReference and ProjectReference's runtime assets to our package output. -->
<Target
Name="CopyProjectReferencesToPackage"
DependsOnTargets="ResolveReferences">
<ItemGroup>
<!-- The TargetPath is the path inside the package that the source file will be placed. This is already precomputed in the ReferenceCopyLocalPaths items' DestinationSubPath, so reuse it here. -->
<BuildOutputInPackage
Include="@(ReferenceCopyLocalPaths)"
TargetPath="%(ReferenceCopyLocalPaths.DestinationSubPath)" />
</ItemGroup>
</Target>
Jangan bundel rakitan Microsoft.Build.Utilities.Core
Seperti yang dibahas di atas, dependensi ini akan disediakan oleh MSBuild itu sendiri saat runtime, jadi kita tidak perlu membundelkannya ke dalam paket. Untuk melakukannya, tambahkan atribut ExcludeAssets="Runtime" ke PackageReference.
...
<PackageReference
Include="Microsoft.Build.Utilities.Core"
Version="17.0.0"
PrivateAssets="all"
ExcludeAssets="Runtime"
/>
...
Membuat dan menyematkan file deps.json
File deps.json dapat digunakan oleh MSBuild untuk memastikan versi dependensi Anda yang benar dimuat. Anda harus menambahkan beberapa properti MSBuild agar file tersebut dapat dihasilkan, karena tidak dihasilkan sebagai default untuk pustaka. Kemudian, tambahkan target untuk menyertakannya dalam output paket kami, mirip dengan bagaimana Anda melakukannya untuk dependensi paket kami.
<PropertyGroup>
...
<!-- Tell the SDK to generate a deps.json file -->
<GenerateDependencyFile>true</GenerateDependencyFile>
...
</PropertyGroup>
...
<!-- This target adds the generated deps.json file to our package output -->
<Target
Name="AddBuildDependencyFileToBuiltProjectOutputGroupOutput"
BeforeTargets="BuiltProjectOutputGroup"
Condition=" '$(GenerateDependencyFile)' == 'true'">
<ItemGroup>
<BuiltProjectOutputGroupOutput
Include="$(ProjectDepsFilePath)"
TargetPath="$(ProjectDepsFileName)"
FinalOutputPath="$(ProjectDepsFilePath)" />
</ItemGroup>
</Target>
Menyertakan properti dan target MSBuild dalam paket
Untuk latar belakang di bagian ini, baca tentang properti dan target lalu cara menyertakan properti dan target dalam paket NuGet.
Dalam beberapa kasus, Anda mungkin ingin menambahkan target atau properti build kustom dalam proyek yang menggunakan paket Anda, seperti menjalankan alat atau proses kustom selama build. Anda melakukan ini dengan menempatkan file dalam formulir <package_id>.targets atau <package_id>.props dalam folder build dalam proyek.
File di folder build akar proyek dianggap cocok untuk semua kerangka kerja target.
Di bagian ini, Anda akan menyambungkan implementasi tugas dalam file .props dan .targets, yang akan disertakan dalam paket NuGet kami dan dimuat secara otomatis dari proyek referensi.
Dalam file proyek tugas, AppSettingStronglyTyped.csproj, tambahkan kode berikut:
<ItemGroup> <!-- these lines pack the build props/targets files to the `build` folder in the generated package. by convention, the .NET SDK will look for build\<Package Id>.props and build\<Package Id>.targets for automatic inclusion in the build. --> <Content Include="build\AppSettingStronglyTyped.props" PackagePath="build\" /> <Content Include="build\AppSettingStronglyTyped.targets" PackagePath="build\" /> </ItemGroup>Buat folder build dan di folder tersebut, tambahkan dua file teks:
AppSettingStronglyTyped.propsdan AppSettingStronglyTyped.targets.AppSettingStronglyTyped.propsdiimpor lebih awal di Microsoft.Common.props, dan properti yang ditentukan kemudian tidak dapat digunakan olehnya. Jadi, hindari mengacu pada properti yang belum ditentukan; mereka akan dianggap kosong.Directory.Build.targets diimpor dari Microsoft.Common.targets setelah mengimpor file
.targetsdari paket NuGet. Jadi, ini dapat menggantikan properti dan target yang ditentukan dalam sebagian besar logika pembangunan, atau mengatur properti untuk semua proyek Anda tanpa mempedulikan pengaturan dari masing-masing proyek. Lihat pesanan impor .AppSettingStronglyTyped.props menyertakan tugas dan menentukan beberapa properti dengan nilai default:
<?xml version="1.0" encoding="utf-8" ?> <Project> <!--defining properties interesting for my task--> <PropertyGroup> <!--The folder where the custom task will be present. It points to inside the nuget package. --> <_AppSettingsStronglyTyped_TaskFolder>$(MSBuildThisFileDirectory)..\tasks\netstandard2.0</_AppSettingsStronglyTyped_TaskFolder> <!--Reference to the assembly which contains the MSBuild Task--> <CustomTasksAssembly>$(_AppSettingsStronglyTyped_TaskFolder)\$(MSBuildThisFileName).dll</CustomTasksAssembly> </PropertyGroup> <!--Register our custom task--> <UsingTask TaskName="$(MSBuildThisFileName).AppSettingStronglyTyped" AssemblyFile="$(CustomTasksAssembly)"/> <!--Task parameters default values, this can be overridden--> <PropertyGroup> <RootFolder Condition="'$(RootFolder)' == ''">$(MSBuildProjectDirectory)</RootFolder> <SettingClass Condition="'$(SettingClass)' == ''">MySetting</SettingClass> <SettingNamespace Condition="'$(SettingNamespace)' == ''">example</SettingNamespace> <SettingExtensionFile Condition="'$(SettingExtensionFile)' == ''">mysettings</SettingExtensionFile> </PropertyGroup> </Project>File
AppSettingStronglyTyped.propssecara otomatis disertakan ketika paket diinstal. Kemudian, klien memiliki tugas yang tersedia dan beberapa nilai default. Namun, itu tidak pernah digunakan. Untuk menempatkan kode ini dalam tindakan, tentukan beberapa target dalam fileAppSettingStronglyTyped.targets, yang juga akan secara otomatis disertakan ketika paket diinstal:<?xml version="1.0" encoding="utf-8" ?> <Project> <!--Defining all the text files input parameters--> <ItemGroup> <SettingFiles Include="$(RootFolder)\*.$(SettingExtensionFile)" /> </ItemGroup> <!--A target that generates code, which is executed before the compilation--> <Target Name="BeforeCompile" Inputs="@(SettingFiles)" Outputs="$(RootFolder)\$(SettingClass).generated.cs"> <!--Calling our custom task--> <AppSettingStronglyTyped SettingClassName="$(SettingClass)" SettingNamespaceName="$(SettingNamespace)" SettingFiles="@(SettingFiles)"> <Output TaskParameter="ClassNameFile" PropertyName="SettingClassFileName" /> </AppSettingStronglyTyped> <!--Our generated file is included to be compiled--> <ItemGroup> <Compile Remove="$(SettingClassFileName)" /> <Compile Include="$(SettingClassFileName)" /> </ItemGroup> </Target> <!--The generated file is deleted after a general clean. It will force the regeneration on rebuild--> <Target Name="AfterClean"> <Delete Files="$(RootFolder)\$(SettingClass).generated.cs" /> </Target> </Project>Langkah pertama adalah pembuatan ItemGroup, yang mewakili file teks (bisa lebih dari satu) untuk dibaca dan itu akan menjadi beberapa parameter tugas kami. Ada nilai default untuk lokasi dan ekstensi tempat kami mencari, tetapi Anda dapat mengambil alih nilai yang menentukan properti dalam file proyek MSBuild klien.
Kemudian tentukan dua target MSBuild . Kami memperluas proses MSBuild, mengambil alih target yang telah ditentukan sebelumnya:
BeforeCompile: Tujuannya adalah untuk memanggil tugas kustom untuk menghasilkan kelas dan menyertakan kelas yang akan dikompilasi. Tugas dalam target ini disisipkan sebelum kompilasi inti selesai. Kolom Input dan Output terkait dengan pembangunan bertahap . Jika semua item keluaran adalah up-to-date, MSBuild akan mengabaikan target. Build bertahap target ini dapat secara signifikan meningkatkan performa build Anda. Item dianggap up-to-date jika file outputnya berusia sama atau lebih baru dari file atau file inputnya.AfterClean: Tujuannya adalah untuk menghapus file kelas yang dihasilkan setelah pembersihan umum terjadi. Tugas dalam target ini dimasukkan setelah fungsi pembersihan inti dipanggil. Ini memaksa langkah pembuatan kode untuk diulang ketika target Rebuild dijalankan.
Hasilkan paket NuGet
Untuk menghasilkan paket NuGet, Anda dapat menggunakan Visual Studio (klik kanan pada simpul proyek di Solution Explorer, dan pilih Pack). Anda juga dapat melakukannya dengan menggunakan baris perintah. Navigasi ke folder tempat file proyek tugas AppSettingStronglyTyped.csproj ada, dan jalankan perintah berikut:
// -o is to define the output; the following command chooses the current folder.
dotnet pack -o .
Selamat! Anda telah membuat paket NuGet bernama \AppSettingStronglyTyped\AppSettingStronglyTyped\AppSettingStronglyTyped.1.0.0.nupkg.
Paket ini memiliki ekstensi .nupkg dan merupakan file zip terkompresi. Anda dapat membukanya dengan alat zip. File .target dan .props berada di folder build. File .dll ada di folder lib\netstandard2.0\. File AppSettingStronglyTyped.nuspec berada di tingkat akar.
(Opsional) Mendukung penargetan ganda
Anda harus mempertimbangkan untuk mendukung distribusi MSBuild Full (.NET Framework) dan Core (termasuk .NET 5 dan yang lebih baru) untuk mendukung basis pengguna yang paling luas.
Untuk proyek .NET SDK 'normal', multitarget berarti mengatur beberapa TargetFrameworks dalam file proyek Anda. Ketika Anda melakukan ini, build akan dipicu untuk kedua TargetFrameworkMonikers, dan seluruh hasilnya dapat dikemas sebagai artefak tunggal.
Itu bukan cerita lengkap untuk MSBuild. MSBuild memiliki dua kendaraan pengiriman utama: Visual Studio dan .NET SDK. Ini adalah lingkungan runtime yang sangat berbeda; satu berjalan pada runtime .NET Framework, dan lainnya berjalan pada CoreCLR. Artinya, meskipun kode Anda dapat menargetkan netstandard2.0, logika tugas Anda mungkin memiliki perbedaan berdasarkan jenis runtime MSBuild yang saat ini digunakan. Secara praktis, karena ada begitu banyak API baru di .NET 5.0 dan versi-versi yang lebih baru, masuk akal untuk melakukan multitarget kode sumber tugas MSBuild Anda untuk beberapa TargetFrameworkMonikers serta multitarget logika target MSBuild Anda untuk beberapa jenis runtime MSBuild.
Perubahan yang diperlukan untuk penargetan ganda
Untuk menargetkan beberapa TargetFrameworkMonikers (TFM):
Ubah file proyek Anda untuk menggunakan
net472dannet6.0TFM (yang terakhir dapat berubah berdasarkan tingkat SDK mana yang ingin Anda targetkan). Anda mungkin ingin menargetkannetcoreapp3.1sampai .NET Core 3.1 tidak didukung. Ketika Anda melakukan ini, struktur folder paket berubah daritasks/ketasks/<TFM>/.<TargetFrameworks>net472;net6.0</TargetFrameworks>Perbarui file
.targetsAnda untuk menggunakan TFM yang benar untuk memuat tugas Anda. TFM yang dibutuhkan akan berubah sesuai dengan pilihan TFM .NET yang Anda tentukan di atas, tetapi untuk proyek yang menargetkannet472dannet6.0, Anda akan memiliki properti yang mirip dengan berikut:
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net472</AppSettingStronglyTyped_TFM>
<AppSettingStronglyTyped_TFM Condition=" '$(MSBuildRuntimeType)' == 'Core' ">net6.0</AppSettingStronglyTyped_TFM>
Kode ini menggunakan properti MSBuildRuntimeType sebagai proksi untuk lingkungan hosting aktif. Setelah properti ini diatur, Anda dapat menggunakannya di UsingTask untuk memuat AssemblyFileyang benar:
<UsingTask
AssemblyFile="$(MSBuildThisFileDirectory)../tasks/$(AppSettingStronglyTyped_TFM)/AppSettingStronglyTyped.dll"
TaskName="AppSettingStrongTyped.AppSettingStronglyTyped" />
Langkah berikutnya
Banyak tugas melibatkan memanggil program yang dapat dijalankan. Dalam beberapa skenario, Anda dapat menggunakan tugas Exec, tetapi jika batasan tugas Exec adalah masalah, Anda juga dapat membuat tugas kustom. Tutorial berikut menelusuri kedua opsi dengan skenario pembuatan kode yang lebih realistis: membuat tugas kustom untuk menghasilkan kode klien untuk REST API.
Atau, pelajari cara menguji tugas kustom.