Delen via


Hoe MSBuild projecten bouwt

Hoe werkt MSBuild eigenlijk? In dit artikel leert u hoe MSBuild uw projectbestanden verwerkt, ongeacht of deze worden aangeroepen vanuit Visual Studio of vanaf een opdrachtregel of script. Als u weet hoe MSBuild werkt, kunt u problemen beter diagnosticeren en uw buildproces beter aanpassen. In dit artikel wordt het buildproces beschreven en is grotendeels van toepassing op alle projecttypen.

Het volledige buildproces bestaat uit eerste opstartproces, evaluatieen uitvoering van de doelen en taken die het project bouwen. Naast deze invoer definiëren externe importbewerkingen de details van het buildproces, waaronder zowel standaardimports zoals Microsoft.Common.targets en door de gebruiker configureerbare importbewerkingen op oplossings- of projectniveau.

Opstarten

MSBuild kan vanuit Visual Studio worden aangeroepen via het MSBuild-objectmodel in Microsoft.Build.dll, of door het uitvoerbare bestand (MSBuild.exe of dotnet build) rechtstreeks aan te roepen op de opdrachtregel of in een script, zoals in CI-systemen. In beide gevallen bevatten invoer die van invloed is op het buildproces het projectbestand (of het projectobject intern in Visual Studio), mogelijk een oplossingsbestand, omgevingsvariabelen en opdrachtregelswitches of de equivalenten van het objectmodel. Tijdens de opstartfase worden de opdrachtregelopties of objectmodelequivalenten gebruikt om MSBuild-instellingen te configureren, zoals het configureren van loggers. Eigenschappen die zijn ingesteld op de opdrachtregel met behulp van de schakeloptie -property of -p worden ingesteld als globale eigenschappen, waardoor waarden worden overschreven die in de projectbestanden worden ingesteld, zelfs als projectbestanden later worden gelezen.

De volgende secties gaan over de invoerbestanden, zoals oplossingsbestanden of projectbestanden.

Oplossingen en projecten

MSBuild-exemplaren kunnen bestaan uit één project of veel projecten als onderdeel van een oplossing. Oplossingsbestanden in de .slnx-indeling of de .sln-indeling worden beide ondersteund (in MSBuild 17.12 en hoger). Het oplossingsbestand (.sln) is geen MSBuild XML-bestand, maar MSBuild interpreteert het om te weten welke projecten moeten worden gebouwd voor de opgegeven configuratie- en platforminstellingen. Wanneer MSBuild deze invoer verwerkt, wordt dit de build van de oplossing genoemd. Het bevat enkele uitbreidbare punten waarmee u iets kunt uitvoeren bij elke build van de oplossing, maar omdat deze build een afzonderlijke uitvoering is van de afzonderlijke projectbuilds, zijn er geen instellingen van eigenschappen of doeldefinities van de oplossingsbuild relevant voor elke projectbuild.

U kunt ontdekken hoe u de build van de oplossing kunt uitbreiden bij De build van de oplossing aanpassen.

Visual Studio-builds versus MSBuild.exe-builds

Er zijn enkele belangrijke verschillen tussen wanneer projecten worden gebouwd in Visual Studio versus wanneer u MSBuild rechtstreeks aanroept, via het uitvoerbare MSBuild-bestand of wanneer u het MSBuild-objectmodel gebruikt om een build te starten. Visual Studio beheert de projectbuildvolgorde voor Visual Studio-builds; het roept alleen MSBuild aan op het niveau van het afzonderlijke project en wanneer dit het geval is, worden een aantal Booleaanse eigenschappen (BuildingInsideVisualStudio, BuildProjectReferences) ingesteld die aanzienlijk van invloed zijn op wat MSBuild doet. Binnen elk project gebeurt de uitvoering hetzelfde als wanneer deze wordt aangeroepen via MSBuild, maar het verschil ontstaat met projecten waarnaar wordt verwezen. In MSBuild, wanneer naar projecten wordt verwezen, vindt er daadwerkelijk een build plaats; Dat wil gezegd, het voert taken en hulpprogramma's uit en genereert de uitvoer. Wanneer een Visual Studio-build een project vindt waarnaar wordt verwezen, retourneert MSBuild alleen de verwachte uitvoer van het project waarnaar wordt verwezen; Hiermee kan Visual Studio het bouwen van die andere projecten beheren. Visual Studio bepaalt de buildvolgorde en roept msBuild afzonderlijk aan (indien nodig), allemaal volledig onder het beheer van Visual Studio.

Er ontstaat een ander verschil wanneer MSBuild wordt aangeroepen met een oplossingsbestand, MSBuild het oplossingsbestand parseert, een standaard XML-invoerbestand maakt, evalueert en uitvoert als een project. De build van de oplossing wordt uitgevoerd vóór een project. Bij het bouwen vanuit Visual Studio gebeurt dit niet; MSBuild ziet het oplossingsbestand nooit. Als gevolg hiervan is de aanpassing van de oplossingenbouw (met behulp van vóór SolutionName.sln.targets en na SolutionName.sln.targets) alleen van toepassing op MSBuild.exe, dotnet build, of op objectmodelgestuurde builds, niet op Visual Studio-builds.

Project SDK's

De SDK-functie voor MSBuild-projectbestanden is relatief nieuw. Vóór deze wijziging importeerden projectbestanden expliciet de .targets en .props bestanden die het buildproces voor een bepaald projecttype hebben gedefinieerd.

.NET Core-projecten importeren de versie van de .NET SDK die hiervoor geschikt is. Zie het overzicht, SDK's voor .NET Core-project en de verwijzing naar de eigenschappen .

Evaluatiefase

In deze sectie wordt beschreven hoe deze invoerbestanden worden verwerkt en geparseerd om in-memory objecten te produceren die bepalen wat er wordt gebouwd.

Het doel van de evaluatiefase is het maken van de objectstructuren in het geheugen op basis van de XML-invoerbestanden en de lokale omgeving. De evaluatiefase bestaat uit zes cycli waarmee de invoerbestanden worden verwerkt, zoals de XML-projectbestanden of de geïmporteerde XML-bestanden, meestal aangeduid als .props of .targets bestanden, afhankelijk van of ze voornamelijk eigenschappen instellen of doelen definiëren. Elke pass bouwt een deel van de in-memory objecten die later worden gebruikt in de uitvoeringsfase om de projecten te bouwen, maar er worden geen werkelijke buildacties uitgevoerd tijdens de evaluatiefase. Binnen elke pas worden elementen verwerkt in de volgorde waarin ze worden weergegeven.

Tijdens de evaluatiefase zijn de resultaten als volgt:

  • Omgevingsvariabelen evalueren
  • Import- en eigenschappen evalueren
  • Itemdefinities evalueren
  • Items evalueren
  • UsingTask--elementen evalueren
  • Doelen evalueren

De importen en eigenschappen worden geëvalueerd in dezelfde doorgang in volgorde van verschijning, alsof de importen ter plekke worden uitgebreid. Eigenschappeninstellingen in eerder geïmporteerde bestanden zijn dus beschikbaar in latere geïmporteerde bestanden.

De volgorde van deze passen heeft aanzienlijke gevolgen en is belangrijk om te weten wanneer u het projectbestand kunt aanpassen. Zie eigenschaps- en itemevaluatievolgorde.

Omgevingsvariabelen evalueren

In deze fase worden omgevingsvariabelen gebruikt om gelijkwaardige eigenschappen in te stellen. De omgevingsvariabele PATH wordt bijvoorbeeld beschikbaar gesteld als een eigenschap $(PATH). Wanneer de opdracht wordt uitgevoerd vanaf de opdrachtregel of een script, wordt de opdrachtomgeving zoals gebruikelijk gebruikt, en wanneer deze wordt uitgevoerd vanuit Visual Studio, wordt de omgeving gebruikt die actief is wanneer Visual Studio wordt gestart.

Import- en eigenschappen evalueren

In deze fase wordt de volledige invoer-XML gelezen, inclusief de projectbestanden en de hele keten van importbewerkingen. MSBuild maakt een in-memory XML-structuur die de XML van het project en alle geïmporteerde bestanden vertegenwoordigt. Op dit moment worden eigenschappen die zich niet in doelen bevinden geëvalueerd en ingesteld.

Als gevolg van MSBuild die al vroeg in het proces alle XML-invoerbestanden leest, hebben wijzigingen in deze invoer tijdens het buildproces geen invloed op de huidige build.

Eigenschappen buiten een doel worden anders verwerkt dan eigenschappen binnen doelen. In deze fase worden alleen de eigenschappen geëvalueerd die buiten een doel zijn gedefinieerd.

Omdat eigenschappen in de volgorde van de eigenschappen worden verwerkt, heeft een eigenschap op elk punt in de invoer toegang tot eigenschapswaarden die eerder in de invoer worden weergegeven, maar niet tot eigenschappen die later worden weergegeven.

Omdat de eigenschappen worden verwerkt voordat items worden geëvalueerd, hebt u geen toegang tot de waarde van een item gedurende enige fase van de eigenschapscyclus.

Itemdefinities evalueren

In deze fase worden itemdefinities geïnterpreteerd en wordt er een in-memory weergave van deze definities gemaakt.

Items evalueren

Items die in een doel zijn gedefinieerd, worden anders verwerkt dan items buiten een doel. In deze fase worden items buiten een doel en de bijbehorende metagegevens verwerkt. Metagegevens die op itemdefinities zijn ingesteld, worden overschreven door metagegevens die op items zijn ingesteld. Omdat items worden verwerkt in de volgorde waarin ze worden weergegeven, kunt u verwijzen naar items die eerder zijn gedefinieerd, maar niet naar items die later worden weergegeven. Omdat de items worden verwerkt nadat de eigenschappen zijn verwerkt, kunnen items toegang krijgen tot elke eigenschap, mits deze buiten de doelstellingen is gedefinieerd, ongeacht of de eigenschapsdefinitie later wordt vermeld.

Evalueren van UsingTask-elementen

In deze fase worden UsingTask--elementen gelezen en worden de taken gedeclareerd voor later gebruik tijdens de uitvoeringsfase.

Doelen evalueren

In deze fase worden alle doelobjectstructuren gemaakt in het geheugen, ter voorbereiding op uitvoering. Er vindt geen daadwerkelijke uitvoering plaats.

Uitvoeringsfase

In de uitvoeringsfase worden de doelen geordend en uitgevoerd en worden alle taken uitgevoerd. Maar eerst worden eigenschappen en items die binnen doelen zijn gedefinieerd, in één fase geëvalueerd in de volgorde waarin ze worden weergegeven. De volgorde van de verwerking verschilt met name van de manier waarop eigenschappen en items die zich niet in een doel bevinden, worden verwerkt: alle eigenschappen eerst en vervolgens alle items, in afzonderlijke doorgangen. Wijzigingen in eigenschappen en items binnen een doel kunnen worden waargenomen na het doel waar ze zijn gewijzigd.

Doelbuildvolgorde

In één project worden doelen in serie uitgevoerd. Het centrale probleem is hoe u kunt bepalen in welke volgorde alles moet worden gebouwd, zodat afhankelijkheden worden gebruikt om de doelen in de juiste volgorde te bouwen.

De doelbuildvolgorde wordt bepaald door het gebruik van de BeforeTargets, DependsOnTargetsen AfterTargets kenmerken voor elk doel. De volgorde van latere doelen kan worden beïnvloed tijdens de uitvoering van een eerder doel als het eerdere doel een eigenschap wijzigt waarnaar in deze kenmerken wordt verwezen.

De regels voor bestellen worden beschreven in Bepaal de doel-bouwvolgorde. Het proces wordt bepaald door een stackstructuur die doelen bevat om te bouwen. Het doel boven aan deze taak begint met de uitvoering en als deze afhankelijk is van iets anders, worden deze doelen naar de bovenkant van de stack gepusht en worden ze uitgevoerd. Wanneer er een doel is zonder afhankelijkheden, wordt het volledig uitgevoerd en wordt het bovenliggende doel hervat.

Projectverwijzingen

Er zijn twee codepaden die MSBuild kan aannemen, de normale, die hier wordt beschreven en de grafiekoptie die in de volgende sectie wordt beschreven.

Afzonderlijke projecten geven hun afhankelijkheid van andere projecten aan via ProjectReference items. Wanneer een project boven aan de stack begint met bouwen, bereikt het het punt waar het ResolveProjectReferences doel wordt uitgevoerd, een standaarddoel dat is gedefinieerd in de algemene doelbestanden.

ResolveProjectReferences roept de MSBuild-taak aan met de ProjectReference items als input om de uitvoer te verkrijgen. De ProjectReference items worden omgezet in lokale items, zoals Reference. De MSBuild-uitvoeringsfase voor het huidige project wordt onderbroken terwijl de uitvoeringsfase begint met het verwerken van het project waarnaar wordt verwezen (de evaluatiefase wordt indien nodig eerst uitgevoerd). Het project waarnaar wordt verwezen, wordt alleen gebouwd nadat u het afhankelijke project hebt gebouwd, waardoor er een structuur van projecten wordt gemaakt.

Met Visual Studio kunt u projectafhankelijkheden maken in oplossingsbestanden (.sln). De afhankelijkheden worden opgegeven in het oplossingsbestand en worden alleen gerespecteerd bij het bouwen van een oplossing of bij het bouwen in Visual Studio. Als u één project bouwt, wordt dit type afhankelijkheid genegeerd. Oplossingsverwijzingen worden door MSBuild omgezet in ProjectReference items en daarna worden ze op dezelfde manier behandeld.

Grafiekoptie

Als u de grafiekbuildswitch (-graphBuild of -graph) opgeeft, wordt het ProjectReference een eersteklas concept dat door MSBuild wordt gebruikt. MSBuild parseert alle projecten en bouwt de buildordergrafiek, een werkelijke afhankelijkheidsgrafiek van projecten, die vervolgens wordt doorkruist om de buildvolgorde te bepalen. Net als bij doelen in individuele projecten zorgt MSBuild ervoor dat de projecten waarnaar verwezen wordt pas gebouwd worden nadat de projecten waarop ze afhankelijk zijn, voltooid zijn.

Parallelle uitvoering

Als u ondersteuning voor meerdere processors gebruikt (-maxCpuCount of -m switch), maakt MSBuild knooppunten. Dit zijn MSBuild-processen die gebruikmaken van de beschikbare CPU-kernen. Elk project wordt verzonden naar een beschikbaar knooppunt. Binnen een knooppunt worden afzonderlijke projectbuilds in serie uitgevoerd.

Taken kunnen worden ingeschakeld voor parallelle uitvoering door een booleaanse variabele in te stellen BuildInParallel, die is ingesteld op basis van de waarde van de eigenschap $(BuildInParallel) in MSBuild. Voor taken die zijn ingeschakeld voor parallelle uitvoering, beheert een werkplanner knooppunten en wijst werk toe aan knooppunten.

Zie Meerdere projecten parallel bouwen met MSBuild

Standaardimport

Microsoft.Common.props en Microsoft.Common.targets worden zowel geïmporteerd door .NET-projectbestanden (expliciet of impliciet in SDK-projecten) en bevinden zich in de map MSBuild\Current\bin in een Visual Studio-installatie. C++-projecten hebben hun eigen importhiërarchie; zie MSBuild Internals voor C++-projecten.

De Microsoft.Common.props-bestand stelt standaardwaarden in die u kunt overschrijven. Het wordt (expliciet of impliciet) geïmporteerd aan het begin van een projectbestand. Op die manier worden de instellingen van uw project weergegeven na de standaardinstellingen, zodat ze deze overschrijven.

De Microsoft.Common.targets bestand en de doelbestanden die worden geïmporteerd definiëren het standaard buildproces voor .NET-projecten. Het biedt ook uitbreidingspunten die u kunt gebruiken om de build aan te passen.

In de implementatie is Microsoft.Common.targets een thin wrapper waarmee Microsoft.Common.CurrentVersion.targetsworden geïmporteerd. Dit bestand bevat instellingen voor standaardeigenschappen en definieert de werkelijke doelen die het buildproces definiëren. Het Build doel wordt hier gedefinieerd, maar is eigenlijk leeg. Het Build doel bevat echter het kenmerk DependsOnTargets waarmee de afzonderlijke doelen worden opgegeven waaruit de daadwerkelijke buildstappen bestaan, die BeforeBuild, CoreBuilden AfterBuildzijn. Het Build doel wordt als volgt gedefinieerd:

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

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

BeforeBuild en AfterBuild zijn uitbreidingspunten. Ze zijn leeg in het bestand Microsoft.Common.CurrentVersion.targets bestand, maar projecten kunnen hun eigen BeforeBuild en AfterBuild doelen bieden met taken die vóór of na het hoofdbuildproces moeten worden uitgevoerd. AfterBuild wordt uitgevoerd vóór het no-op doel, Build, omdat AfterBuild wordt weergegeven in het kenmerk DependsOnTargets op het Build doel, maar dit gebeurt na CoreBuild.

Het CoreBuild-doel bevat de oproepen naar de build-tools op de volgende manier:

  <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>

In de volgende tabel worden deze doelen beschreven; sommige doelen zijn alleen van toepassing op bepaalde projecttypen.

Doel Beschrijving
BuildOnlySettings Instellingen voor echte builds, niet voor wanneer MSBuild wordt aangeroepen bij het laden van het project door Visual Studio.
PrepareForBuild De vereisten voor het bouwen voorbereiden
PreBuildEvent Uitbreidingspunt voor projecten om taken te definiëren die vóór de build moeten worden uitgevoerd
ResolveProjectReferences Projectafhankelijkheden analyseren en projecten maken waarnaar wordt verwezen
ResolveAssemblyReferences Zoek assembly's waarnaar wordt verwezen.
ResolveReferences Bestaat uit ResolveProjectReferences en ResolveAssemblyReferences om alle afhankelijkheden te vinden
PrepareResources Resourcebestanden verwerken
ResolveKeySource Los de sterke naamsleutel op die wordt gebruikt om de assembly te ondertekenen en het certificaat dat wordt gebruikt om de ClickOnce- manifesten te ondertekenen.
Compileren Roept de compiler aan
ExportWindowsMDFile Genereer een WinMD--bestand op basis van de WinMDModule-bestanden die door de compiler zijn gegenereerd.
Niet-beheerde afmelding De COM Interop registervermeldingen uit een eerdere build verwijderen/opschonen
GenereerSerialisatieAssemblies Genereer een XML-serialisatieassembly met behulp van sgen.exe.
CreateSatelliteAssemblies (satellietassemblies aanmaken) Maak één satellietassemblage voor elke unieke cultuur in de bronnen.
Manifesten genereren Genereert ClickOnce toepassings- en implementatiemanifesten of een systeemeigen manifest.
VerkrijgDoelpad Retourneer een item met het buildproduct (uitvoerbaar bestand of assembly) voor dit project, met metagegevens.
PrepareForRun Kopieer de build-uitvoer naar de uiteindelijke map als deze zijn gewijzigd.
Niet-Beheerde Registratie Registervermeldingen instellen voor COM Interop
IncrementalClean Verwijder bestanden die zijn geproduceerd in een eerdere build, maar die niet zijn geproduceerd in de huidige build. Dit is nodig om Clean te laten werken in incrementele builds.
PostBuildEvent Uitbreidingspunt voor projecten om taken te definiëren die na de build moeten worden uitgevoerd

Veel van de doelen in de vorige tabel zijn te vinden in taalspecifieke importbewerkingen, zoals Microsoft.CSharp.targets. Dit bestand definieert de stappen in het standaard buildproces dat specifiek is voor C# .NET-projecten. Het bevat bijvoorbeeld het Compile doel dat de C#-compiler daadwerkelijk aanroept.

Door de gebruiker configureerbare importbewerkingen

Naast de standaardimporten zijn er verschillende importbewerkingen die u kunt toevoegen om het buildproces aan te passen.

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

Deze bestanden worden ingelezen door de standaardimport voor elk project in welke submap dan ook onder hen. Dit is meestal op oplossingsniveau voor instellingen om alle projecten in de oplossing te beheren, maar kan ook hoger in de bestandenstructuur zijn, tot aan de root van het station.

Het bestand Directory.Build.props wordt geïmporteerd door Microsoft.Common.props, zodat de daarin gedefinieerde eigenschappen beschikbaar zijn in het projectbestand. Ze kunnen opnieuw worden gedefinieerd in het projectbestand om de waarden per project aan te passen. Het Directory.Build.targets bestand wordt gelezen na het projectbestand. Het bevat doorgaans doelen, maar hier kunt u ook eigenschappen definiëren die u niet wilt dat afzonderlijke projecten opnieuw worden gedefinieerd.

Aanpassingen in een projectbestand

Visual Studio werkt uw projectbestanden bij wanneer u wijzigingen aanbrengt in Solution Explorer, het venster Eigenschappen of in Projecteigenschappen, maar u kunt ook uw eigen wijzigingen aanbrengen door het projectbestand rechtstreeks te bewerken.

Veel buildgedrag kan worden geconfigureerd door MSBuild-eigenschappen in te stellen, ofwel in het projectbestand voor instellingen die lokaal zijn voor een project, of zoals vermeld in de vorige sectie, door een Directory.Build.props-bestand te maken om eigenschappen globaal in te stellen voor volledige mappen van projecten en oplossingen. Voor ad-hoc builds op de opdrachtregel of in scripts kunt u ook de optie /p op de opdrachtregel gebruiken, om eigenschappen in te stellen voor een bepaalde aanroep van MSBuild. Zie Common MSBuild-projecteigenschappen voor informatie over eigenschappen die u kunt instellen.