Anpassa bygget per mapp
Du kan lägga till vissa filer som ska importeras av MSBuild för att åsidosätta standardegenskapsinställningar och lägga till anpassade mål. Omfånget för dessa anpassningar kan styras på mappnivå genom var filerna placeras.
Den här artikeln beskriver anpassningar som gäller för följande scenarier:
- Anpassa bygginställningar för många projekt i en lösning
- Anpassa bygginställningar för många lösningar under en gemensam filkatalog
- Anpassa bygginställningar som kan vara olika för undermappar i en komplex struktur med mappar
- Åsidosätt standardinställningar, standardversionsmappar och andra beteenden som anges av ett SDK, till exempel
Microsoft.Net.Sdk
- Lägga till eller anpassa byggmål som gäller för valfritt antal projekt eller lösningar
Om du arbetar med C++-projekt kan du också använda metoderna som beskrivs i Anpassa C++-versioner.
Directory.Build.props och Directory.Build.targets
Du kan lägga till en ny egenskap i varje projekt genom att definiera den i en enda fil som heter Directory.Build.props i rotmappen som innehåller källan.
När MSBuild körs söker Microsoft.Common.props din katalogstruktur efter filen Directory.Build.props. Om den hittar en importerar den filen och läser de egenskaper som definierats i den. Directory.Build.props är en användardefinierad fil som tillhandahåller anpassningar till projekt under en katalog.
På samma sätt letar Microsoft.Common.targets efter Directory.Build.targets.
Directory.Build.props importeras tidigt i sekvensen av importerade filer, vilket kan vara viktigt om du behöver ange en egenskap som används av importer, särskilt de som implicit importeras med hjälp av attributet Sdk
, till exempel när du använder .NET SDK i de flesta .NET-projektfiler.
Not
Linux-baserade filsystem är skiftlägeskänsliga. Kontrollera att höljet för Directory.Build.props filnamn matchar exakt, eller så identifieras det inte under byggprocessen.
Mer information finns i detta GitHub-ärende.
Directory.Build.props-exempel
Här är till exempel en Directory.Build.props fil som anger utdatakatalogen för alla projekt i en Visual Studio-lösning. Utdata för varje projekt placeras under ett eget projektnamn. I det här exemplet finns filen Directory.Build.props i en lösningsmapp, med många projekt i undermappar under den. Egenskapen $(MSBuildProjectName)
ger namnet på varje projekt. Eftersom Directory.Build.props-filen importeras till varje projekt under den egna versionen utvärderas den till rätt värde för varje enskilt projekt i lösningen.
Rensa lösningen för att ta bort alla gamla utdatafiler.
msbuild /t:Clean SolutionName.sln
Skapa en ny fil i roten på lagringsplatsen med namnet Directory.Build.props.
Lägg till följande XML i filen.
<Project> <PropertyGroup> <OutDir>C:\output\$(MSBuildProjectName)</OutDir> </PropertyGroup> </Project>
Not
Egenskapen
$(OutDir)
är en absolut sökväg till utdata, och om du använder den kringgås skapandet av undermappar för konfiguration, målramverk eller körning som normalt används i .NET-projekt. Prova att använda egenskapenBaseOutputPath
i stället om du vill att de vanliga undermapparna ska skapas under en anpassad utdatasökväg.Kör MSBuild. Projektets befintliga importer av Microsoft.Common.props och Microsoft.Common.targets hittar och importerar filen Directory.Build.props, och den nya utdatamappen används för alla projekt i den mappen.
Sökomfång
När du söker efter en Directory.Build.props--fil går MSBuild katalogstrukturen uppåt från projektplatsen $(MSBuildProjectFullPath)
och stoppas när den har hittat en Directory.Build.props fil. Om din $(MSBuildProjectFullPath)
till exempel var c:\users\username\code\test\case1, skulle MSBuild börja söka där och sedan söka i katalogstrukturen uppåt tills den hittade en fil Directory.Build.props, som i följande katalogstruktur.
c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\
Platsen för lösningsfilen är irrelevant för Directory.Build.props.
Importordning
Directory.Build.props importeras tidigt i Microsoft.Common.propsoch egenskaper som definierats senare är inte tillgängliga för den. Undvik därför att referera till egenskaper som ännu inte har definierats (och utvärderas till tomma).
Egenskaper som anges i Directory.Build.props kan åsidosättas någon annanstans i projektfilen eller i importerade filer, så du bör tänka på inställningarna i Directory.Build.props som att ange standardvärdena för dina projekt.
Directory.Build.targets importeras från Microsoft.Common.targets när du har importerat .targets
filer från NuGet-paket. Därför kan den åsidosätta egenskaper och mål som definierats i de flesta bygglogik, eller ange egenskaper för alla dina projekt oavsett vad de enskilda projekten har angett.
När du behöver ange en egenskap eller definiera ett mål för ett enskilt projekt som åsidosätter tidigare inställningar lägger du den logiken i projektfilen efter den slutliga importen. För att kunna göra detta i ett SDK-projekt måste du först ersätta attributet SDK-format med motsvarande importer. Se Hur man använder MSBuild-projekt-SDK:er.
Obs.
MSBuild-motorn läser in alla importerade filer under utvärderingsfasen, innan byggkörning för ett projekt (inklusive eventuella PreBuildEvent
), så förväntas dessa filer inte att ändras av PreBuildEvent
eller någon annan del av byggprocessen. Eventuella ändringar träder inte i kraft förrän nästa anrop av MSBuild.exe eller nästa Visual Studio-version. Om din byggprocess innehåller många projektbyggen (som med multitargeting eller att bygga beroende projekt) läses importerade filer, inklusive Directory.build.props, när utvärderingen sker för varje enskilt projektbygge.
Användningsfall: sammanslagning på flera nivåer
Anta att du har den här standardlösningsstrukturen:
\
MySolution.sln
Directory.Build.props (1)
\src
Directory.Build.props (2-src)
\Project1
\Project2
\test
Directory.Build.props (2-test)
\Project1Tests
\Project2Tests
Det kan vara önskvärt att ha gemensamma egenskaper för alla projekt (1), gemensamma egenskaper för src projekt (2-src)och gemensamma egenskaper för test projekt (2-test).
Om du vill att MSBuild ska sammanfoga "inre" filer (2-src och 2-test) med den "yttre" filen (1), måste du ta hänsyn till att när MSBuild hittar en Directory.Build.props fil, stoppas ytterligare genomsökning. Om du vill fortsätta genomsökningen och sammanfogningen i den yttre filen placerar du koden i båda de inre filerna:
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
En sammanfattning av MSBuilds allmänna metod är följande:
- För ett visst projekt hittar MSBuild den första Directory.Build.props uppåt i lösningsstrukturen, sammanfogar den med standardvärden och slutar söka efter mer.
- Om du vill att flera nivåer ska hittas och sammanfogas, markera
<Import...>
(visas tidigare) den "yttre" filen från den "inre" filen. - Om den "yttre" filen inte själv också importerar något ovanför den, stoppas genomsökningen där.
Eller helt enkelt: den första Directory.Build.props som inte importerar något är där MSBuild slutar.
Om du vill styra importprocessen mer explicit använder du egenskaperna $(DirectoryBuildPropsPath)
, $(ImportDirectoryBuildProps)
, $(DirectoryBuildTargetsPath)
och $(ImportDirectoryBuildTargets)
. Egenskapen $(DirectoryBuildPropsPath)
anger sökvägen till den Directory.Build.props
fil som ska användas. på samma sätt anger $(DirectoryBuildTargetsPath)
sökvägen till Directory.Build.targets
-filen.
De booleska egenskaperna $(ImportDirectoryBuildProps)
och $(ImportDirectoryBuildTargets)
är inställda på true
som standard, så MSBuild söker normalt efter dessa filer, men du kan ställa in dem på false
för att förhindra att MSBuild importerar dem.
Exempel
Det här exemplet visar användningen av förbearbetade utdata för att avgöra var en egenskap ska anges.
För att hjälpa dig att analysera användningen av en viss egenskap som du vill ange kan du köra MSBuild med argumentet /preprocess
eller /pp
. Utdatatexten är resultatet av alla importer, inklusive systemimporter som Microsoft.Common.props som implicit importeras och någon av dina egna importer. Med dessa utdata kan du se var din egenskap måste anges i förhållande till var dess värde används.
Anta till exempel att du har ett enkelt .NET Core- eller .NET 5- eller senare-projekt för en konsolapp, och du vill anpassa den mellanliggande utdatamappen, som vanligtvis är obj
. Egenskapen som anger den här sökvägen är BaseIntermediateOutput
. Om du försöker placera detta i ett PropertyGroup
-element i projektfilen tillsammans med de olika andra egenskaper som redan har angetts där, till exempel TargetFramework
, upptäcker du när du skapar projektet att egenskapen inte börjar gälla. Om du kör MSBuild med alternativet /pp
och söker i utdata efter BaseIntermediateOutputPath
kan du se varför. I det här fallet läses BaseIntermediateOutput
och används i Microsoft.Common.props
.
Det finns en kommentar i Microsoft.Common.props som säger att egenskapen BaseIntermediateOutput
måste anges här, innan den används av en annan egenskap, MSBuildProjectExtensionsPath
. Du kan också se att när BaseIntermediateOutputPath
ursprungligen har angetts, finns det en kontroll av ett befintligt värde och om det är odefinierat anges det till obj
.
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
Den här placeringen anger därför att den här egenskapen måste anges någonstans tidigare än så. Precis innan den här koden i de förbearbetade utdata kan du se att Directory.Build.props
importeras, så att du kan ange BaseIntermediateOutputPath
där och det kommer att anges tillräckligt tidigt för att få önskad effekt.
Följande förkortade förbearbetade utdata visar resultatet av att ange inställningen BaseIntermediateOutput
i Directory.Build.props
. Kommentarerna överst i standardimporterna innehåller filnamnet och vanligtvis viss användbar information om varför filen importeras.
<?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>
...