Bagikan melalui


Memperluas proses build Visual Studio

Proses build Visual Studio ditentukan oleh serangkaian file MSBuild .targets yang diimpor ke dalam file proyek Anda. Impor ini implisit, jika Anda menggunakan SDK seperti yang biasanya dilakukan proyek Visual Studio. Salah satu file yang diimpor ini, Microsoft.Common.targets, dapat diperluas agar memungkinkan Anda menjalankan tugas kustom di beberapa titik dalam proses build. Artikel ini menjelaskan tiga metode yang dapat Anda gunakan untuk memperluas proses build Visual Studio:

  • Buat target kustom dan tentukan kapan harus dijalankan dengan menggunakan BeforeTargets atribut dan AfterTargets .

  • Ambil alih properti yang DependsOn ditentukan dalam target umum.

  • Ambil alih target tertentu yang telah ditentukan sebelumnya yang ditentukan dalam target umum (Microsoft.Common.targets atau file yang diimpornya).

AfterTargets dan BeforeTargets

Anda dapat menggunakan AfterTargets atribut dan BeforeTargets pada target kustom Anda untuk menentukan kapan harus berjalan.

Contoh berikut menunjukkan cara menggunakan atribut AfterTargets untuk menambahkan target kustom yang memengaruhi file output. Dalam hal ini, atribut menyalin file output ke folder baru CustomOutput. Contohnya juga menunjukkan cara membersihkan file yang dihasilkan oleh operasi build kustom dengan target CustomClean yang menggunakan atribut BeforeTargets dan menentukan spesifikasi operasi pembersihan kustom berjalan sebelum target CoreClean.

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

  <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild" AfterTargets="Build">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Text="_FilesToCopy: @(_FilesToCopy)" Importance="high"/>

    <Message Text="DestFiles:
        @(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles=
          "@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean" BeforeTargets="CoreClean">
    <Message Text="Inside Custom Clean" Importance="high"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files='@(_CustomFilesToDelete)'/>
  </Target>
</Project>

Peringatan

Pastikan untuk menggunakan nama yang berbeda dari target yang telah ditentukan sebelumnya (misalnya, target build kustom di sini adalah CustomAfterBuild, bukan AfterBuild), karena target yang telah ditentukan sebelumnya ditimpa oleh impor SDK yang juga menentukannya. Lihat tabel di akhir artikel ini untuk daftar target yang telah ditentukan sebelumnya.

Memperluas properti DependsOn

Cara lain untuk memperluas proses build adalah dengan menggunakan DependsOn properti (misalnya, BuildDependsOn), untuk menentukan target yang harus dijalankan sebelum target standar.

Metode ini lebih disukai untuk mengambil alih target yang telah ditentukan sebelumnya, yang dibahas di bagian berikutnya. Mengesampingkan target yang telah ditentukan sebelumnya adalah metode lama yang masih didukung, tetapi, karena MSBuild mengevaluasi definisi target secara berurutan, tidak ada cara untuk mencegah proyek lain yang mengimpor proyek Anda mengesampingkan target yang sudah Anda timpa. Jadi, misalnya, target terakhir AfterBuild yang ditentukan dalam file proyek akan menjadi target yang digunakan selama build setelah semua proyek lain diimpor.

Anda dapat melindungi dari penimpaan target yang tidak diinginkan dengan mengesampingkan DependsOn properti yang digunakan dalam atribut di DependsOnTargets seluruh target umum. Misalnya,target Build berisi nilai atribut DependsOnTargets dari "$(BuildDependsOn)". Pertimbangkan:

<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>

Bagian XML ini menunjukkan bahwa sebelum target Build dapat berjalan, semua target yang ditentukan dalam properti BuildDependsOn harus berjalan terlebih dahulu. Properti BuildDependsOn didefinisikan sebagai:

<PropertyGroup>
    <BuildDependsOn>
        $(BuildDependsOn);
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

Anda dapat mengambil alih nilai properti ini dengan menentukan properti lain bernama BuildDependsOn di akhir file proyek Anda. Dalam proyek bergaya SDK, ini berarti Anda harus menggunakan impor eksplisit. Lihat Impor impor implisit dan eksplisit, sehingga Anda dapat menempatkan DependsOn properti setelah impor terakhir. Dengan menyertakan properti BuildDependsOn sebelumnya di properti baru, Anda dapat menambahkan target baru ke awal dan akhir daftar target. Contohnya:

<PropertyGroup>
    <BuildDependsOn>
        MyCustomTarget1;
        $(BuildDependsOn);
        MyCustomTarget2
    </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCustomTarget1">
    <Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
    <Message Text="Running MyCustomTarget2..."/>
</Target>

Proyek yang mengimpor file proyek Anda dapat memperluas properti ini lebih lanjut tanpa menimpa kustomisasi yang telah Anda buat.

Untuk mengambil alih properti DependsOn

  1. Identifikasi properti yang telah DependsOn ditentukan sebelumnya di target umum yang ingin Anda ambil alih. Lihat tabel berikut untuk daftar properti yang umumnya ditimpa DependsOn .

  2. Tentukan contoh sebuah properti atau beberapa properti lain di akhir file proyek Anda. Sertakan properti asli, misalnya $(BuildDependsOn), di properti baru.

  3. Tentukan target kustom Anda sebelum atau sesudah definisi properti.

  4. Bangun file proyek.

Properti DependsOn yang umumnya diambil alih

Nama properti Target yang ditambahkan berjalan sebelum titik ini:
BuildDependsOn Titik masuk build utama. Ambil alih properti ini jika Anda ingin menyisipkan target kustom sebelum atau sesudah seluruh proses build.
RebuildDependsOn Tje Rebuild
RunDependsOn Eksekusi output build akhir (jika itu adalah .EXE)
CompileDependsOn Kompilasi (Compile target). Ambil alih properti ini jika Anda ingin menyisipkan proses kustom sebelum atau sesudah langkah kompilasi.
CreateSatelliteAssembliesDependsOn Pembuatan rakitan satelit
CleanDependsOn Target Clean (Menghapus semua output build perantara dan akhir). Ambil alih properti ini jika Anda ingin membersihkan output dari proses build kustom Anda.
PostBuildEventDependsOn PostBuildEvent Target
PublishBuildDependsOn Membangun penerbitan
ResolveAssemblyReferencesDependsOn ResolveAssemblyReferences Target (menemukan penutupan dependensi transitif untuk dependensi tertentu). Lihat ResolveAssemblyReference.

Contoh: BuildDependsOn dan CleanDependsOn

Contoh berikut mirip dengan contoh BeforeTargets dan AfterTargets, tetapi contoh ini menunjukkan cara mencapai fungsionalitas serupa. Contoh ini memperluas build dengan menggunakan BuildDependsOn untuk menambahkan tugas CustomAfterBuild Anda sendiri yang menyalin file output setelah build, dan juga menambahkan tugas CustomClean yang sesuai dengan menggunakan CleanDependsOn.

Dalam contoh ini, ini adalah proyek bergaya SDK. Seperti disebutkan dalam catatan tentang proyek gaya SDK sebelumnya dalam artikel ini, Anda harus menggunakan metode impor manual, bukan atribut Sdk yang digunakan oleh Visual Studio saat menghasilkan file proyek.

<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);CustomAfterBuild
    </BuildDependsOn>

    <CleanDependsOn>
      $(CleanDependsOn);CustomClean
    </CleanDependsOn>

    <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Importance="high" Text="_FilesToCopy: @(_FilesToCopy)"/>

    <Message Text="DestFiles:
      @(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles="@(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean">
    <Message Importance="high" Text="Inside Custom Clean"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files="@(_CustomFilesToDelete)"/>
  </Target>
</Project>

Urutan elemen itu penting. Elemen BuildDependsOn dan CleanDependsOn harus muncul setelah mengimpor file target SDK standar.

Mengambil alih target yang telah ditentukan sebelumnya

File umum .targets berisi sekumpulan target kosong yang telah ditentukan sebelumnya yang dipanggil sebelum dan sesudah beberapa target utama dalam proses build. Misalnya, MSBuild memanggil target BeforeBuild sebelum target utama CoreBuild dan target AfterBuild setelah target CoreBuild. Secara default, target kosong dalam target umum tidak melakukan apa pun, tetapi Anda dapat mengambil alih perilaku defaultnya dengan menentukan target yang Anda inginkan dalam file proyek. Metode yang dijelaskan sebelumnya dalam artikel ini lebih disukai, tetapi Anda mungkin menemukan kode lama yang menggunakan metode ini.

Jika proyek Anda menggunakan SDK (misalnya Microsoft.Net.Sdk), Anda perlu membuat perubahan dari implisit ke impor eksplisit, seperti yang dibahas dalam Impor eksplisit dan implisit.

Untuk mengambil alih target yang telah ditentukan sebelumnya

  1. Jika proyek menggunakan Sdk atribut , ubah ke sintaks impor eksplisit. Lihat Impor eksplisit dan implisit.

  2. Identifikasi target yang telah ditentukan sebelumnya dalam target umum yang ingin Anda ambil alih. Lihat tabel berikut untuk daftar lengkap target yang bisa Anda ambil alih dengan aman.

  3. Tentukan target atau target di akhir file proyek Anda, segera sebelum </Project> tag dan setelah impor SDK eksplisit. Contohnya:

    <Project>
        <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
        ...
        <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
        <Target Name="BeforeBuild">
            <!-- Insert tasks to run before build here -->
        </Target>
        <Target Name="AfterBuild">
            <!-- Insert tasks to run after build here -->
        </Target>
    </Project>
    

    Perhatikan bahwa Sdk atribut pada elemen tingkat Project atas telah dihapus.

  4. Bangun file proyek.

Tabel target yang telah ditentukan sebelumnya

Tabel berikut ini memperlihatkan semua target dalam target umum yang bisa Anda ambil alih.

Nama target Deskripsi
BeforeCompile, AfterCompile Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah kompilasi inti dilakukan. Sebagian besar penyesuaian dilakukan di salah satu dari dua target ini.
BeforeBuild, AfterBuild Tugas yang disisipkan dalam salah satu target ini akan berjalan sebelum atau sesudah tugas lainnya dalam build. Catatan: Target BeforeBuild dan AfterBuild sudah ditentukan dalam komentar di akhir sebagian besar file proyek, yang memungkinkan Anda untuk dengan mudah menambahkan peristiwa pra-dan pasca-build ke file proyek Anda.
BeforeRebuild, AfterRebuild Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah fungsionalitas pembangunan ulang inti dilakukan. Urutan eksekusi target di Microsoft.Common.targets adalah: BeforeRebuild, Clean, Build, lalu AfterRebuild.
BeforeClean, AfterClean Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah fungsionalitas pembersihan inti dilakukan.
BeforePublish, AfterPublish Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah fungsionalitas penerbitan inti dilakukan.
BeforeResolveReferences, AfterResolveReferences Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah referensi rakitan diselesaikan.
BeforeResGen, AfterResGen Tugas yang disisipkan dalam salah satu target ini berjalan sebelum atau sesudah sumber daya dihasilkan.

Ada lebih banyak target dalam sistem build dan .NET SDK, lihat target MSBuild - SDK dan target build default.

Praktik terbaik untuk target kustom

Properti DependsOnTargets dan BeforeTargets dapat menentukan bahwa target harus berjalan sebelum target lain, tetapi keduanya diperlukan dalam skenario yang berbeda. Mereka berbeda di mana target persyaratan dependensi ditentukan. Anda hanya memiliki kontrol atas target Anda sendiri dan tidak dapat memodifikasi target sistem atau target impor lainnya dengan aman, sehingga membatasi pilihan metode Anda.

Saat menulis target kustom, ikuti panduan umum ini untuk memastikan target Anda dijalankan dalam urutan yang dimaksudkan.

  1. DependsOnTargets Gunakan atribut untuk menentukan target yang harus Anda lakukan sebelum target Anda dijalankan. Untuk rantai target yang Anda kontrol, setiap target dapat menentukan anggota rantai sebelumnya di DependsOnTargets.

  2. Gunakan BeforeTargets untuk target apa pun yang tidak Anda kontrol yang harus Anda jalankan sebelumnya (seperti BeforeTargets="PrepareForBuild" untuk target yang perlu dijalankan di awal build).

  3. Gunakan AfterTargets untuk target apa pun yang tidak Anda kontrol yang menjamin output yang Anda butuhkan tersedia. Misalnya, tentukan AfterTargets="ResolveReferences" untuk sesuatu yang akan memodifikasi daftar referensi.

  4. Anda dapat menggunakannya dalam kombinasi. Contohnya,DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile".

Impor eksplisit dan implisit

Proyek yang dihasilkan oleh Visual Studio biasanya menggunakan Sdk atribut pada elemen proyek. Jenis proyek ini disebut proyek bergaya SDK. Lihat Menggunakan SDK proyek MSBuild. Berikut contohnya:

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

Saat proyek Anda menggunakan Sdk atribut , dua impor ditambahkan secara implisit, satu di awal file proyek Anda, dan satu di akhir.

Impor implisit setara dengan memiliki pernyataan impor seperti ini sebagai baris pertama dalam file proyek, setelah Project elemen:

<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />

dan pernyataan impor berikut sebagai baris terakhir dalam file proyek:

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

Sintaks ini disebut sebagai impor SDK eksplisit. Saat Anda menggunakan sintaks eksplisit ini, Anda harus menghilangkan Sdk atribut pada elemen proyek.

Impor SDK implisit setara dengan mengimpor "umum" .props atau .targets file tertentu yang merupakan konstruksi umum dalam file proyek yang lebih lama, seperti:

<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />

dan

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

Setiap referensi lama tersebut harus diganti dengan sintaks SDK eksplisit yang ditunjukkan sebelumnya di bagian ini.

Menggunakan sintaks SDK eksplisit berarti Anda dapat menambahkan kode Anda sendiri sebelum impor pertama, atau setelah impor SDK akhir. Itu berarti Anda dapat mengubah perilaku dengan mengatur properti sebelum impor pertama yang akan berlaku dalam file yang diimpor .props , dan Anda dapat mengambil alih target yang ditentukan dalam salah satu file SDK .targets setelah impor akhir. Dengan menggunakan metode ini, Anda dapat mengambil alih BeforeBuild atau AfterBuild sebagaimana dibahas berikutnya.

Langkah berikutnya

Ada lebih banyak lagi yang dapat Anda lakukan dengan MSBuild untuk menyesuaikan build. Lihat Menkustomisasi build Anda.