Cara MSBuild membangun proyek

Bagaimana cara MSBuild bekerja sebenarnya? Dalam artikel ini, Anda akan mempelajari bagaimana MSBuild memproses file proyek Anda, baik yang dipanggil dari Visual Studio, atau dari baris perintah atau skrip. Mengetahui cara kerja MSBuild dapat membantu Anda mendiagnosis masalah dengan lebih baik dan menyesuaikan proses pembuatan dengan lebih baik. Artikel ini menjelaskan proses build dan sebagian besar berlaku untuk semua jenis proyek.

Proses build lengkap terdiri dari startup awal, evaluasi, dan eksekusi dari target dan tugas yang membangun proyek. Selain masukan ini, impor eksternal menentukan detail proses build, termasuk impor standar seperti Microsoft.Common.targets dan impor yang dapat dikonfigurasi pengguna pada tingkat solusi atau proyek.

Startup

MSBuild dapat dipanggil dari Visual Studio melalui model objek MSBuild di Microsoft.Build.dll, atau dengan memanggil executable (MSBuild.exe atau dotnet build) langsung pada baris perintah, atau dalam skrip, seperti di sistem CI. Dalam kedua kasus tersebut, input yang memengaruhi proses build menyertakan file proyek (atau objek proyek internal ke Visual Studio), mungkin file solusi, variabel lingkungan, dan sakelar baris perintah atau model objek yang setara. Selama fase startup, opsi baris perintah atau model objek yang setara digunakan untuk mengonfigurasi pengaturan MSBuild seperti mengonfigurasi pencatat. Properti yang diatur pada baris perintah menggunakan sakelar -property atau -p diatur sebagai properti global, yang menimpa nilai apa pun yang akan diatur dalam file proyek, meskipun file proyek dibaca nanti.

Bagian selanjutnya adalah tentang file input, seperti file solusi atau file proyek.

Solusi dan proyek

Instans MSBuild dapat terdiri dari satu proyek, atau beberapa proyek sebagai bagian dari solusi. File solusi bukan file XML MSBuild, tetapi MSBuild menafsirkannya untuk mengetahui semua proyek yang diperlukan untuk dibangun untuk konfigurasi dan pengaturan platform yang diberikan. Ketika MSBuild memproses input XML ini, ini disebut sebagai build solusi. Ini memiliki beberapa poin yang dapat diperluas yang memungkinkan Anda untuk menjalankan sesuatu di setiap build solusi, tetapi karena build ini dijalankan secara terpisah dari masing-masing build proyek, tidak ada pengaturan properti atau definisi target dari build solusi yang relevan untuk setiap build proyek.

Anda dapat mengetahui cara memperluas versi solusi di Menyesuaikan versi solusi.

Visual Studio builds vs. MSBuild.exe builds

Ada beberapa perbedaan signifikan antara ketika proyek dibangun di Visual Studio vs. ketika Anda menjalankan MSBuild secara langsung, baik melalui MSBuild yang dapat dijalankan, atau saat Anda menggunakan model objek MSBuild untuk memulai build. Visual Studio mengelola urutan build proyek untuk build Visual Studio; itu hanya memanggil MSBuild di tingkat proyek individu, dan ketika itu terjadi, beberapa properti Boolean (BuildingInsideVisualStudio, BuildProjectReferences) diatur yang secara signifikan memengaruhi apa yang dilakukan MSBuild. Di dalam setiap proyek, eksekusi terjadi sama seperti ketika dipanggil melalui MSBuild, tetapi perbedaannya muncul dengan proyek yang direferensikan. Di MSBuild, ketika proyek yang direferensikan diperlukan, build benar-benar terjadi; yaitu, menjalankan tugas dan alat, dan menghasilkan output. Ketika build Visual Studio menemukan proyek yang direferensikan, MSBuild hanya mengembalikan output yang diharapkan dari proyek yang direferensikan; itu memungkinkan Visual Studio mengontrol build proyek-proyek lain tersebut. Visual Studio menentukan urutan build dan panggilan ke MSBuild secara terpisah (sesuai kebutuhan), semua sepenuhnya di bawah kontrol Visual Studio.

Perbedaan lain muncul ketika MSBuild dipanggil dengan file solusi, MSBuild mengurai file solusi, membuat file input XML standar, mengevaluasinya, dan mengeksekusinya sebagai sebuah proyek. Build solusi dijalankan sebelum proyek apa pun. Ketika membangun dari Visual Studio, semua ini tidak terjadi; MSBuild tidak pernah melihat file solusi. Sebagai konsekuensinya, penyesuaian build solusi (menggunakan sebelumnya. SolutionName.sln.targets dan setelahnya. SolutionName.sln.targets) hanya berlaku untuk build berbasis MSBuild.exe, dotnet build, atau model objek, bukan build Visual Studio.

SDK Proyek

Fitur SDK untuk file proyek MSBuild relatif baru. Sebelum perubahan ini, file proyek secara eksplisit mengimpor file .targets dan .props yang menentukan proses build untuk jenis proyek tertentu.

Proyek .NET Core mengimpor versi .NET SDK yang sesuai untuk mereka. Lihat gambaran umum, .NET Core project SDKs, dan referensi ke properti.

Fase evaluasi

Bagian ini membahas bagaimana file input ini diproses dan diurai untuk menghasilkan objek dalam memori yang menentukan apa yang akan dibangun.

Tujuan dari fase evaluasi adalah untuk membuat struktur objek dalam memori berdasarkan input file XML dan lingkungan lokal. Fase evaluasi terdiri dari enam pass yang memproses file input seperti file XML proyek atau, dan file XML yang diimpor, umumnya disebut sebagai file .props atau .targets, bergantung pada apakah mereka terutama menetapkan properti atau menentukan target build. Setiap pass membangun bagian dari objek dalam memori yang kemudian digunakan dalam fase eksekusi untuk membangun proyek, tetapi tidak ada tindakan build yang terjadi selama fase evaluasi. Dalam setiap pass, elemen diproses sesuai urutan kemunculannya.

Lolos dalam fase evaluasi adalah sebagai berikut:

  • Mengevaluasi variabel lingkungan
  • Mengevaluasi impor dan properti
  • Mengevaluasi definisi item
  • Mengevaluasi item
  • Mengevaluasi elemen UsingTask
  • Mengevaluasi target

Impor dan properti dievaluasi dalam pass yang sama secara berurutan tampilan, seolah-olah impor diperluas di tempat. Dengan demikian, pengaturan properti dalam file yang diimpor sebelumnya tersedia dalam file yang diimpor nanti.

Urutan lintasan ini memiliki implikasi yang signifikan dan penting untuk diketahui saat menyesuaikan file proyek. Lihat Urutan evaluasi properti dan item.

Mengevaluasi variabel lingkungan

Pada fase ini, variabel lingkungan digunakan untuk mengatur properti yang setara. Misalnya, variabel lingkungan PATH tersedia sebagai properti $(PATH). Saat dijalankan dari baris perintah atau skrip, lingkungan perintah digunakan seperti biasa, dan saat dijalankan dari Visual Studio, lingkungan yang berlaku saat peluncuran Visual Studio digunakan.

Mengevaluasi impor dan properti

Pada fase ini, seluruh input XML dibaca, termasuk file proyek dan seluruh rantai impor. MSBuild membuat struktur XML dalam memori yang mewakili XML proyek dan semua file yang diimpor. Saat ini, properti yang tidak ada dalam target dievaluasi dan ditetapkan.

Sebagai konsekuensi dari MSBuild membaca semua file input XML di awal prosesnya, setiap perubahan pada input tersebut selama proses build tidak memengaruhi build saat ini.

Properti di luar target mana pun ditangani secara berbeda dari properti di dalam target. Pada fase ini, hanya properti yang ditentukan di luar target yang dievaluasi.

Karena properti diproses secara berurutan dalam properti yang lulus, properti di titik mana pun di input dapat mengakses nilai properti yang muncul sebelumnya di input, tetapi bukan properti yang muncul nanti.

Karena properti diproses sebelum item dievaluasi, Anda tidak dapat mengakses nilai item apa pun selama bagian mana pun dari properti yang lulus.

Mengevaluasi definisi item

Dalam fase ini, definisi item ditafsirkan dan ditafsirkan dalam memori dari definisi ini dibuat.

Mengevaluasi item

Item yang ditentukan di dalam target ditangani secara berbeda dari item di luar target apa pun. Dalam fase ini, item di luar target apa pun, dan metadata yang terkait, diproses. Metadata yang diatur menurut definisi item diambil alih oleh metadata yang diatur pada item. Karena item diproses dalam urutan yang muncul, Anda dapat merujuk item yang telah ditentukan sebelumnya, tetapi bukan item yang muncul nanti. Karena item yang lulus setelah properti lulus, item dapat mengakses properti apa pun jika ditentukan di luar target apa pun, terlepas dari apakah definisi properti muncul nanti.

Mengevaluasi UsingTask elemen

Dalam fase ini, elemen UsingTask dibaca, dan tugas dinyatakan untuk digunakan nanti selama fase eksekusi.

Mengevaluasi target

Pada fase ini, semua struktur objek target dibuat di dalam memori, sebagai persiapan untuk dijalankan. Tidak ada eksekusi aktual yang terjadi.

Fase Eksekusi

Pada fase eksekusi, target dipesan dan dijalankan, dan semua tugas dijalankan. Tetapi pertama-tama, properti dan item yang ditentukan di dalam target dievaluasi bersama dalam satu fase sesuai urutan kemunculannya. Urutan pemrosesan sangat berbeda dari cara properti dan item yang tidak berada dalam target yang diproses: semua properti terlebih dahulu, lalu semua item, dalam pass terpisah. Perubahan pada properti dan item dalam target dapat diamati setelah target diubah.

Urutan build target

Dalam satu proyek, target dijalankan secara serial. Masalah utamanya adalah cara menentukan urutan untuk membangun semuanya sehingga dependensi digunakan untuk membangun target dalam urutan yang benar.

Urutan build target ditentukan oleh penggunaan atribut BeforeTargets, DependsOnTargets, dan AfterTargets pada setiap target. Urutan target selanjutnya dapat dipengaruhi selama eksekusi target sebelumnya jika target sebelumnya memodifikasi properti yang direferensikan dalam atribut ini.

Aturan untuk pengurutan dijelaskan dalam Menentukan urutan build target. Proses ini ditentukan oleh struktur tumpukan yang berisi target untuk build. Target di bagian atas tugas ini memulai eksekusi, dan jika itu bergantung pada hal lain, maka target tersebut didorong ke bagian atas tumpukan, dan mereka mulai mengeksekusi. Ketika ada target tanpa dependensi apa pun, target dijalankan hingga selesai dan target induknya dilanjutkan.

Referensi Proyek

Ada dua jalur kode yang dapat diambil MSBuild, yang normal, dijelaskan di sini, dan opsi grafik dijelaskan di bagian berikutnya.

Proyek individu menentukan dependensi mereka pada proyek lain melalui ProjectReference item. Ketika proyek di bagian atas tumpukan mulai dibangun, proyek tersebut mencapai titik di mana target ResolveProjectReferences dijalankan, target standar yang ditentukan dalam file target umum.

ResolveProjectReferences memanggil tugas MSBuild dengan input dari item ProjectReference untuk mendapatkan output. Item ProjectReference diubah menjadi item lokal seperti Reference. Fase eksekusi MSBuild untuk proyek saat ini berhenti sementara fase eksekusi mulai memproses proyek yang direferensikan (fase evaluasi dilakukan terlebih dahulu sesuai kebutuhan). Proyek yang direferensikan hanya dibangun setelah Anda mulai membangun proyek dependen, sehingga ini membuat pohon build proyek.

Visual Studio memungkinkan build dependensi proyek dalam file solusi (.sln). Dependensi ditentukan dalam file solusi dan hanya dihormati saat membangun solusi, atau saat membangun di dalam Visual Studio. Jika Anda membangun satu proyek, jenis dependensi ini akan diabaikan. Referensi solusi diubah oleh MSBuild menjadi ProjectReference item dan setelah itu diperlakukan dengan cara yang sama.

Opsi grafik

Jika Anda menentukan sakelar pembuatan grafik (-graphBuild atau -graph), ProjectReference menjadi konsep kelas satu yang digunakan oleh MSBuild. MSBuild akan mengurai semua proyek dan membuat grafik urutan build, grafik dependensi proyek yang sebenarnya, yang kemudian dilalui untuk menentukan urutan build. Seperti target dalam proyek individu, MSBuild memastikan bahwa proyek yang direferensikan dibangun setelah proyek yang mereka andalkan.

Eksekusi paralel

Jika menggunakan dukungan multiprosesor (sakelar -maxCpuCount atau -m), MSBuild membuat node, yaitu proses MSBuild yang menggunakan inti CPU yang tersedia. Setiap proyek dikirimkan ke node yang tersedia. Dalam sebuah node, proyek build individu dijalankan secara serial.

Tugas dapat diaktifkan untuk eksekusi paralel dengan mengatur variabel boolean BuildInParallel, yang diatur menurut nilai properti $(BuildInParallel) di MSBuild. Untuk tugas yang diaktifkan untuk eksekusi paralel, penjadwal kerja mengelola node dan menugaskan pekerjaan ke node.

Lihat Membangun beberapa proyek secara paralel dengan MSBuild

Impor standar

Microsoft.Common.props dan Microsoft.Common.targets keduanya diimpor oleh file proyek .NET (secara eksplisit atau implisit dalam proyek bergaya SDK) dan terletak di MSBuild \Current\bin dalam penginstalan Visual Studio. Proyek C++ memiliki hierarki impornya sendiri; lihat MSBuild Internal untuk proyek C++.

File Microsoft.Common.props mengatur default yang dapat Anda ambil alih. Hal ini diimpor (secara eksplisit atau implisit) di awal file proyek. Dengan begitu, pengaturan proyek Anda muncul setelah default, sehingga mereka mengambil alihnya.

File Microsoft.Common.targets dan file target yang diimpornya menentukan proses build standar untuk proyek .NET. Ini juga menyediakan titik ekstensi yang dapat Anda gunakan untuk menyesuaikan build.

Dalam penerapannya, Microsoft.Common.targets adalah pembungkus tipis yang mengimpor Microsoft.Common.CurrentVersion.targets. File ini berisi pengaturan untuk properti standar, dan mendefinisikan target aktual yang menentukan proses build. Target Build didefinisikan di sini, tetapi sebenarnya kosong. Namun, target Build berisi atribut DependsOnTargets yang menetapkan target individual yang membentuk langkah-langkah build aktual, yaitu BeforeBuild, CoreBuild, dan AfterBuild. Target Build didefinisikan sebagai berikut:

  <PropertyGroup>
    <BuildDependsOn>
      BeforeBuild;
      CoreBuild;
      AfterBuild
    </BuildDependsOn>
  </PropertyGroup>

  <Target
      Name="Build"
      Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
      DependsOnTargets="$(BuildDependsOn)"
      Returns="@(TargetPathWithTargetPlatformMoniker)" />

BeforeBuild dan AfterBuild adalah titik ekstensi. File tersebut kosong dalam file Microsoft.Common.CurrentVersion.targets, tetapi proyek dapat menyediakan target BeforeBuild dan AfterBuild mereka sendiri dengan tugas yang perlu dilakukan sebelum atau setelah proses build utama. AfterBuild dijalankan sebelum target no-op, Build, karena AfterBuild muncul di atribut DependsOnTargets pada target Build, tetapi muncul setelah CoreBuild.

Target CoreBuild berisi panggilan ke alat build, sebagai berikut:

  <PropertyGroup>
    <CoreBuildDependsOn>
      BuildOnlySettings;
      PrepareForBuild;
      PreBuildEvent;
      ResolveReferences;
      PrepareResources;
      ResolveKeySource;
      Compile;
      ExportWindowsMDFile;
      UnmanagedUnregistration;
      GenerateSerializationAssemblies;
      CreateSatelliteAssemblies;
      GenerateManifests;
      GetTargetPath;
      PrepareForRun;
      UnmanagedRegistration;
      IncrementalClean;
      PostBuildEvent
    </CoreBuildDependsOn>
  </PropertyGroup>
  <Target
      Name="CoreBuild"
      DependsOnTargets="$(CoreBuildDependsOn)">

    <OnError ExecuteTargets="_TimeStampAfterCompile;PostBuildEvent" Condition="'$(RunPostBuildEvent)'=='Always' or '$(RunPostBuildEvent)'=='OnOutputUpdated'"/>
    <OnError ExecuteTargets="_CleanRecordFileWrites"/>

  </Target>

Tabel berikut menjelaskan target ini; beberapa target hanya berlaku untuk jenis proyek tertentu.

Target Deskripsi
BuildOnlySettings Pengaturan untuk build nyata saja, bukan untuk saat MSBuild dipanggil pada beban proyek oleh Visual Studio.
PrepareForBuild Menyiapkan prasyarat untuk membangun
PreBuildEvent Titik ekstensi untuk proyek menentukan tugas yang harus dijalankan sebelum membangun
ResolveProjectReferences Menganalisis dependensi proyek dan build proyek yang direferensikan
ResolveAssemblyReferences Temukan rakitan yang dirujuk.
ResolveReferences Terdiri dari ResolveProjectReferences dan ResolveAssemblyReferences untuk menemukan semua dependensi
PrepareResources Memproses file sumber daya
ResolveKeySource Selesaikan kunci nama yang kuat yang digunakan untuk menandatangani rakitan dan sertifikat yang digunakan untuk menandatangani manifes ClickOnce.
Mengompilasi Memanggil kompiler
ExportWindowsMDFile Membuat file WinMD dari file WinMDModule yang dibuat oleh kompiler.
UnmanagedUnregistration Menghapus/membersihkan entri pendaftaran COM Interop dari versi sebelumnya
GenerateSerializationAssemblies Hasilkan rakitan serialisasi XML menggunakan sgen.exe.
CreateSatelliteAssemblies Buat satu perakitan satelit untuk setiap budaya unik di dalam sumber daya.
Hasilkan Manifes Menghasilkan manifes penerapan dan penyebaran ClickOnce atau manifes asli.
GetTargetPath Mengembalikan item yang berisi produk build (yang dapat dieksekusi atau dirakit) untuk proyek ini, dengan metadata.
PrepareForRun Salin output build ke direktori akhir jika sudah berubah.
UnmanagedRegistration Mengatur entri registri untuk COM Interop
IncrementalClean Hapus file yang diproduksi di dalam versi build sebelumnya tetapi tidak diproduksi di dalam versi saat ini. Ini diperlukan untuk membuat Clean berfungsi dalam versi inkremental.
PostBuildEvent Titik ekstensi untuk proyek untuk menentukan tugas yang akan dijalankan setelah build

Banyak target di tabel sebelumnya ditemukan dalam impor khusus bahasa komputer, seperti Microsoft.CSharp.targets. File ini menentukan langkah-langkah dalam proses build standar khusus untuk proyek C# .NET. Misalnya, berisi target Compile yang sebenarnya memanggil kompiler C#.

Impor yang dapat dikonfigurasi pengguna

Selain impor standar, ada beberapa impor yang dapat Anda tambahkan untuk menyesuaikan proses build.

  • Directory.Build.props
  • Directory.Build.targets

File-file ini dibaca oleh impor standar untuk proyek apa pun di subfolder mana pun di bawahnya. Itu biasanya berada di tingkat solusi untuk pengaturan untuk mengontrol semua proyek dalam solusi, tetapi bisa juga lebih tinggi di sistem file, hingga akar drive.

File Directory.Build.props diimpor oleh Microsoft.Common.props, sehingga properti yang ditentukan di dalamnya tersedia dalam file proyek. Mereka dapat ditentukan ulang dalam file proyek untuk menyesuaikan nilai berdasarkan per proyek. File Directory.Build.targets dibaca setelah file proyek. Biasanya berisi target, tetapi di sini Anda juga dapat menentukan properti yang tidak ingin didefinisikan ulang oleh masing-masing proyek.

Kustomisasi dalam file proyek

Visual Studio memperbarui file proyek Anda saat Anda membuat perubahan di Penjelajah Solusi, jendela Properties, atau di Properti Proyek, tetapi Anda juga dapat membuat perubahan sendiri dengan langsung mengedit file proyek.

Banyak perilaku build dapat dikonfigurasi dengan mengatur properti MSBuild, baik di file proyek untuk pengaturan lokal ke proyek, atau seperti yang disebutkan di bagian sebelumnya, dengan membuat file Directory.Build.props untuk mengatur properti secara global untuk seluruh folder proyek dan solusi. Untuk build ad hoc pada baris perintah, atau skrip, Anda juga dapat menggunakan opsi /p pada baris perintah untuk mengatur properti untuk pemanggilan MSBuild tertentu. Lihat Properti proyek MSBuild umum untuk informasi tentang properti yang dapat Anda atur.