Derlemeyi klasöre göre özelleştirme

Varsayılan özellik ayarlarını geçersiz kılmak ve özel hedefler eklemek için MSBuild tarafından içeri aktarılacak bazı dosyaları ekleyebilirsiniz. Bu özelleştirmelerin kapsamı, bu dosyaların yerleştirildiği klasör düzeyinde denetlenebilir.

Bu makale, aşağıdaki senaryolar için geçerli olan özelleştirmeleri kapsar:

  • Çözümdeki birçok proje için derleme ayarlarını özelleştirme
  • Ortak bir dosya dizini altında birçok çözüm için derleme ayarlarını özelleştirme
  • Klasörlerin karmaşık yapısındaki alt klasörler için farklı olabilecek derleme ayarlarını özelleştirme
  • Varsayılan ayarları, varsayılan derleme klasörlerini ve SDK tarafından ayarlanan diğer davranışları geçersiz kılma Microsoft.Net.Sdk
  • Herhangi bir sayıda proje veya çözüm için geçerli olan derleme hedeflerini ekleme veya özelleştirme

C++ projeleriyle çalışıyorsanız, C++ derlemelerini özelleştirme bölümünde açıklanan yöntemleri de kullanabilirsiniz.

Directory.Build.props ve Directory.Build.targets

Kaynağınızı içeren kök klasördeki Directory.Build.props adlı tek bir dosyada tanımlayarak her projeye yeni bir özellik ekleyebilirsiniz.

MSBuild çalıştırıldığında, Microsoft.Common.props dizin yapınızda Directory.Build.props dosyasını arar. Bulursa, dosyayı içeri aktarır ve içinde tanımlanan özellikleri okur. Directory.Build.props , bir dizin altındaki projelere özelleştirmeler sağlayan kullanıcı tanımlı bir dosyadır.

Benzer şekilde, Microsoft.Common.targets da Directory.Build.targets'ı arar.

Directory.Build.props , içeri aktarılan dosyalar dizisinin erken aşamalarında içeri aktarılır; özellikle de çoğu .NET proje dosyasında .NET SDK'sını kullanırken olduğu gibi, özellikle de özniteliği kullanılarak Sdk örtük olarak içeri aktarılanlar için içeri aktarmalar tarafından kullanılan bir özellik ayarlamanız gerekiyorsa önemli olabilir.

Not

Linux tabanlı dosya sistemleri büyük/küçük harfe duyarlıdır. Directory.Build.props dosya adının büyük/küçük harfle tam olarak eşleştiğinden emin olun, aksi zaman derleme işlemi sırasında algılanmayacaktır.

Daha fazla bilgi için bu GitHub konusuna bakın.

Directory.Build.props örneği

Örneğin, visual studio çözümündeki tüm projeler için çıkış dizinini ayarlayan bir Directory.Build.props dosyası aşağıda verilmiştir. Her projenin çıktısı kendi proje adı altına yerleştirilir. Bu örnekte, Directory.Build.props dosyası bir çözüm klasöründedir ve altında birçok proje alt klasörlerde yer alır. $(MSBuildProjectName) özelliği her projenin adını verir. Directory.Build.props dosyası kendi derlemesi sırasında her projeye içeri aktarıldığından, çözümdeki her proje için doğru değere değerlendirilir.

  1. Eski çıkış dosyalarını kaldırmak için çözümü temizleyin.

    msbuild /t:Clean SolutionName.sln

  2. Deponuzun kökünde Directory.Build.props adlı yeni bir dosya oluşturun.

  3. Dosyaya aşağıdaki XML'yi ekleyin.

    <Project>
       <PropertyGroup>
          <OutDir>C:\output\$(MSBuildProjectName)</OutDir>
       </PropertyGroup>
    </Project>
    

    Not

    $(OutDir) özelliği çıkışın mutlak yoludur ve bunu kullanmak normalde .NET projelerinde kullanılan yapılandırma, hedef çerçeve veya çalışma zamanı için alt klasörlerin oluşturulmasını atlar. Her zamanki alt klasörlerin özel bir çıkış yolu altında oluşturulmasını istiyorsanız, bunun yerine özelliğini BaseOutputPath kullanmayı deneyin.

  4. MSBuild'i çalıştırın. Projenizin mevcut Microsoft.Common.props ve Microsoft.Common.targets içeri aktarmaları Directory.Build.props dosyasını bulur ve içeri aktarır ve yeni çıkış klasörü bu klasör altındaki tüm projeler için kullanılır.

Arama kapsamı

Bir Directory.Build.props dosyasını ararken MSBuild, dizin yapısını proje konumunuzdan $(MSBuildProjectFullPath)yukarı doğru gösterir ve bir Directory.Build.props dosyası bulduktan sonra durduruluyor. Örneğin, c$(MSBuildProjectFullPath):\users\username\code\test\case1 olsaydı, MSBuild orada aramaya başlar ve aşağıdaki dizin yapısında olduğu gibi bir Directory.Build.props dosyası bulana kadar dizin yapısını yukarı doğru arar.

c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\

Çözüm dosyasının konumu Directory.Build.props ile ilgisizdir.

İçeri aktarma siparişi

Directory.Build.props, Microsoft.Common.props'un başlarında içeri aktarılır ve daha sonra tanımlanan özellikler kullanılamaz. Bu nedenle, henüz tanımlanmamış (ve boş olarak değerlendirilecek) özelliklere başvurmaktan kaçının.

Directory.Build.props içinde ayarlanan özellikler, proje dosyasının başka bir yerinde veya içeri aktarılan dosyalarda geçersiz kılınabilir, bu nedenle Directory.Build.props içindeki ayarları projeleriniz için varsayılan değerleri belirtmek olarak düşünmelisiniz.

Directory.Build.targets, NuGet paketlerinden dosya içeri aktarıldıktan .targets sonra Microsoft.Common.targets'tan içeri aktarılır. Bu nedenle, derleme mantığının çoğunda tanımlanan özellikleri ve hedefleri geçersiz kılabilir veya tek tek projelerin ayarladığından bağımsız olarak tüm projeleriniz için özellikleri ayarlayabilir.

Önceki ayarları geçersiz kılan tek bir proje için bir özellik ayarlamanız veya hedef tanımlamanız gerektiğinde, bu mantığı son içeri aktarma işleminden sonra proje dosyasına yerleştirin. Bunu SDK stili bir projede yapmak için, önce SDK stili özniteliğini eşdeğer içeri aktarmalarla değiştirmeniz gerekir. Bkz . MSBuild proje SDK'larını kullanma.

Not

MSBuild altyapısı, bir proje (herhangi biri PreBuildEventdahil) için derleme yürütmeye başlamadan önce değerlendirme sırasında içeri aktarılan tüm dosyaları okur, bu nedenle bu dosyaların derleme işleminin PreBuildEvent veya başka bir bölümü tarafından değiştirilmesi beklenmez. Bir sonraki MSBuild.exe veya sonraki Visual Studio derlemesi çağrılana kadar herhangi bir değişiklik geçerli olmaz. Ayrıca, derleme işleminiz birçok proje derlemesi içeriyorsa (çok hedefli veya bağımlı projeler oluştururken olduğu gibi), her bir proje derlemesi için değerlendirme gerçekleştiğinde Directory.build.props dahil olmak üzere içeri aktarılan dosyalar okunur.

Kullanım örneği: çok düzeyli birleştirme

Bu standart çözüm yapısına sahip olduğunuzu varsayalım:

\
  MySolution.sln
  Directory.Build.props     (1)
  \src
    Directory.Build.props   (2-src)
    \Project1
    \Project2
  \test
    Directory.Build.props   (2-test)
    \Project1Tests
    \Project2Tests

Tüm projeler (1) için ortak özelliklere, src projeleri için ortak özelliklere (2-src) ve test projelerinin ortak özelliklerine (2-test) sahip olmak tercih edilebilir.

MSBuild'in "inner" dosyalarını (2-src ve 2-test) "outer" dosyasıyla (1) doğru bir şekilde birleştirmesini sağlamak için, MSBuild bir Directory.Build.props dosyası bulduğunda daha fazla taramayı durdurduğunu dikkate almanız gerekir. Taramaya devam etmek ve dış dosyayla birleştirmek için bu kodu her iki iç dosyaya da yerleştirin:

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

MSBuild'in genel yaklaşımının özeti aşağıdaki gibidir:

  • Belirli bir proje için, MSBuild çözüm yapısında ilk Directory.Build.props öğesini yukarı doğru bulur, varsayılan değerlerle birleştirir ve daha fazlasını taramayı durdurur.
  • Birden çok düzeyin bulunmasını ve birleştirilmesini istiyorsanız , <Import...> (daha önce gösterilen) "iç" dosyadan "dış" dosya.
  • "Dış" dosya üzerinde bir şey de içeri aktarmıyorsa tarama orada durur.

Veya daha basit bir ifadeyle: Hiçbir şeyi içeri aktarmayan ilk Directory.Build.props , MSBuild'in durduğu yerdir.

İçeri aktarma işlemini daha açık bir şekilde denetlemek için , , $(ImportDirectoryBuildProps)$(DirectoryBuildTargetsPath)ve $(ImportDirectoryBuildTargets)özelliklerini $(DirectoryBuildPropsPath)kullanın. özelliği $(DirectoryBuildPropsPath) kullanılacak dosyanın yolunu Directory.Build.props belirtir; benzer şekilde dosyanın $(DirectoryBuildTargetsPath) yolunu Directory.Build.targets belirtir.

Boole özellikleri $(ImportDirectoryBuildProps) ve $(ImportDirectoryBuildTargets) varsayılan olarak olarak ayarlanır true , bu nedenle MSBuild normalde bu dosyaları arar, ancak MSBuild'in bunları içeri aktarmasını önlemek için false bunları olarak ayarlayabilirsiniz.

Örnek

Bu örnekte, bir özelliğin ayarlanacağı yeri belirlemek için önceden işlenmiş çıkışın kullanılması gösterilmektedir.

Ayarlamak istediğiniz belirli bir özelliğin kullanımını analiz etmeye yardımcı olmak için, MSBuild'i /preprocess veya /pp bağımsız değişkeniyle çalıştırabilirsiniz. Çıkış metni, örtük olarak içeri aktarılan Microsoft.Common.props gibi sistem içeri aktarmaları ve kendi içeri aktarmalarınız dahil olmak üzere tüm içeri aktarmaların sonucudur. Bu çıkışla, özelliğinizin değerinin kullanıldığı yere göre ayarlanması gereken yeri görebilirsiniz.

Örneğin, basit bir .NET Core veya .NET 5 veya üzeri Konsol Uygulaması projeniz olduğunu ve ara çıkış klasörünü normalde objözelleştirmek istediğinizi varsayalım. Bu yolu belirten özellik şeklindedir BaseIntermediateOutput. Bunu proje dosyanızdaki bir PropertyGroup öğeye, gibi TargetFrameworkönceden ayarlanmış olan diğer özelliklerle birlikte yerleştirmeyi denerseniz, projeyi oluştururken özelliğin etkin olmadığını keşfedersiniz. MSBuild'i seçeneğiyle /pp çalıştırır ve çıkışta arama BaseIntermediateOutputPathyaparsanız nedenini görebilirsiniz. Bu durumda, BaseIntermediateOutput içinde Microsoft.Common.propsokunur ve kullanılır.

Microsoft.Common.props dosyasında özelliğin başka bir özellik MSBuildProjectExtensionsPathBaseIntermediateOutput tarafından kullanılmadan önce burada ayarlanması gerektiğini belirten bir açıklama vardır. Ayrıca, başlangıçta ayarlandığında, önceden var olan bir değer için bir denetim olduğunu ve tanımlanmamışsa olarak ayarlandığını objda görebilirsinizBaseIntermediateOutputPath.

<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>

Bu nedenle, bu yerleştirme size bu özelliği ayarlamak için bundan daha erken bir yerde belirtilmesi gerektiğini söyler. Önceden işlenmiş çıktıdaki bu kodun hemen öncesinde, içeri aktarıldığını görebilirsiniz Directory.Build.props , böylece orada ayarlayabilirsiniz BaseIntermediateOutputPath ve istenen etkiye sahip olacak kadar erken ayarlanır.

Aşağıdaki kısaltılmış önişlemli çıktı, ayarı Directory.Build.propsiçine koymanın BaseIntermediateOutput sonucunu gösterir. Standart içeri aktarmaların en üstündeki açıklamalar dosya adını ve genellikle bu dosyanın neden içeri aktarıldığını gösteren bazı yararlı bilgileri içerir.

<?xml version="1.0" encoding="IBM437"?>
<!--
============================================================================================================================================
c:\source\repos\ConsoleApp9\ConsoleApp9\ConsoleApp9.csproj
============================================================================================================================================
-->
<Project DefaultTargets="Build">
  <!--
============================================================================================================================================
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk">
  This import was added implicitly because the Project element's Sdk attribute specified "Microsoft.NET.Sdk".

C:\Program Files\dotnet\sdk\7.0.200-preview.22628.1\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props
============================================================================================================================================
-->
  <!--
***********************************************************************************************
Sdk.props

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
  <PropertyGroup xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!--
      Indicate to other targets that Microsoft.NET.Sdk is being used.

      This must be set here (as early as possible, before Microsoft.Common.props)
      so that everything that follows can depend on it.

      In particular, Directory.Build.props and nuget package props need to be able
      to use this flag and they are imported by Microsoft.Common.props.
    -->
    <UsingMicrosoftNETSdk>true</UsingMicrosoftNETSdk>
    <!--
      Indicate whether the set of SDK defaults that makes SDK style project concise are being used.
      For example: globbing, importing msbuild common targets.

      Similar to the property above, it must be set here.
    -->
    <UsingNETSdkDefaults>true</UsingNETSdkDefaults>
  </PropertyGroup>
  <PropertyGroup Condition="'$(MSBuildProjectFullPath)' == '$(ProjectToOverrideProjectExtensionsPath)'" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <MSBuildProjectExtensionsPath>$(ProjectExtensionsPathForSpecifiedProject)</MSBuildProjectExtensionsPath>
  </PropertyGroup>
  <!--<Import Project="$(AlternateCommonProps)" Condition="'$(AlternateCommonProps)' != ''" />-->
  <!--
============================================================================================================================================
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="'$(AlternateCommonProps)' == ''">

C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
  <!--
***********************************************************************************************
Microsoft.Common.props

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->
  <PropertyGroup>
    <ImportByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
    <ImportByWildcardAfterMicrosoftCommonProps Condition="'$(ImportByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportByWildcardAfterMicrosoftCommonProps>
    <ImportUserLocationsByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardBeforeMicrosoftCommonProps>
    <ImportUserLocationsByWildcardAfterMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardAfterMicrosoftCommonProps>
    <ImportDirectoryBuildProps Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
  </PropertyGroup>
  <!--
      Determine the path to the directory build props file if the user did not disable $(ImportDirectoryBuildProps) and
      they did not already specify an absolute path to use via $(DirectoryBuildPropsPath)
  -->
  <PropertyGroup Condition="'$(ImportDirectoryBuildProps)' == 'true' and '$(DirectoryBuildPropsPath)' == ''">
    <_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props</_DirectoryBuildPropsFile>
    <_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)'))</_DirectoryBuildPropsBasePath>
    <DirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsBasePath)' != '' and '$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)'))</DirectoryBuildPropsPath>
  </PropertyGroup>
  <!--
============================================================================================================================================
  <Import Project="$(DirectoryBuildPropsPath)" Condition="'$(ImportDirectoryBuildProps)' == 'true' and exists('$(DirectoryBuildPropsPath)')">

c:\source\repos\ConsoleApp9\Directory.Build.props
============================================================================================================================================
-->
  <!-- Directory.build.props
-->
  <PropertyGroup>
    <BaseIntermediateOutputPath>myBaseIntermediateOutputPath</BaseIntermediateOutputPath>
  </PropertyGroup>
  <!--
============================================================================================================================================
  </Import>

C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
  <!--
      Prepare to import project extensions which usually come from packages.  Package management systems will create a file at:
        $(MSBuildProjectExtensionsPath)\$(MSBuildProjectFile).<SomethingUnique>.props

      Each package management system should use a unique moniker to avoid collisions.  It is a wild-card import so the package
      management system can write out multiple files but the order of the import is alphabetic because MSBuild sorts the list.
  -->
  <PropertyGroup>
    <!--
        The declaration of $(BaseIntermediateOutputPath) had to be moved up from Microsoft.Common.CurrentVersion.targets
        in order for the $(MSBuildProjectExtensionsPath) to use it as a default.
    -->
    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
    <BaseIntermediateOutputPath Condition="!HasTrailingSlash('$(BaseIntermediateOutputPath)')">$(BaseIntermediateOutputPath)\</BaseIntermediateOutputPath>
    <_InitialBaseIntermediateOutputPath>$(BaseIntermediateOutputPath)</_InitialBaseIntermediateOutputPath>
    <MSBuildProjectExtensionsPath Condition="'$(MSBuildProjectExtensionsPath)' == '' ">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
    <!--
        Import paths that are relative default to be relative to the importing file.  However, since MSBuildExtensionsPath
        defaults to BaseIntermediateOutputPath we expect it to be relative to the project directory.  So if the path is relative
        it needs to be made absolute based on the project directory.
    -->
    <MSBuildProjectExtensionsPath Condition="'$([System.IO.Path]::IsPathRooted($(MSBuildProjectExtensionsPath)))' == 'false'">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
    <MSBuildProjectExtensionsPath Condition="!HasTrailingSlash('$(MSBuildProjectExtensionsPath)')">$(MSBuildProjectExtensionsPath)\</MSBuildProjectExtensionsPath>
    <ImportProjectExtensionProps Condition="'$(ImportProjectExtensionProps)' == ''">true</ImportProjectExtensionProps>
    <_InitialMSBuildProjectExtensionsPath Condition=" '$(ImportProjectExtensionProps)' == 'true' ">$(MSBuildProjectExtensionsPath)</_InitialMSBuildProjectExtensionsPath>
  </PropertyGroup>
  ...