Dela via


Utöka Visual Studio-byggprocessen

Visual Studio-kompileringsprocessen definieras av en serie MSBuild-filer som importeras .targets till projektfilen. Dessa importer är implicita om du använder ett SDK som Visual Studio-projekt vanligtvis gör. En av dessa importerade filer, Microsoft.Common.targets, kan utökas så att du kan köra anpassade uppgifter på flera platser i byggprocessen. I den här artikeln beskrivs tre metoder som du kan använda för att utöka Visual Studio-byggprocessen:

  • Skapa ett anpassat mål och ange när det ska köras med hjälp av attributen BeforeTargets och AfterTargets.

  • Åsidosätt de DependsOn egenskaper som definierats i de gemensamma målen.

  • Åsidosätt specifika fördefinierade mål som definierats i de gemensamma målen (Microsoft.Common.targets eller de filer som importeras).

AfterTargets och BeforeTargets

Du kan använda AfterTargets attribut och BeforeTargets på ditt anpassade mål för att ange när det ska köras.

I följande exempel visas hur du använder AfterTargets attributet för att lägga till ett anpassat mål som gör något med utdatafilerna. I det här fallet kopieras utdatafilerna till en ny mapp CustomOutput. Exemplet visar också hur du rensar filerna som skapats av den anpassade byggåtgärden med ett CustomClean mål med hjälp av ett BeforeTargets attribut och anger att den anpassade rensningsåtgärden CoreClean körs före målet.

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

Varning

Se till att använda andra namn än de fördefinierade målen (till exempel är CustomAfterBuilddet anpassade byggmålet här , inte AfterBuild), eftersom de fördefinierade målen åsidosättas av SDK-importen som också definierar dem. En lista över fördefinierade mål finns i tabellen i slutet av den här artikeln.

Utöka egenskaperna för DependsOn

Ett annat sätt att utöka byggprocessen är att använda DependsOn egenskaperna (till exempel BuildDependsOn), för att ange mål som ska köras före ett standardmål.

Den här metoden är att föredra framför att åsidosätta fördefinierade mål, vilket beskrivs i nästa avsnitt. Att åsidosätta fördefinierade mål är en äldre metod som fortfarande stöds, men eftersom MSBuild utvärderar definitionen av mål sekventiellt finns det inget sätt att förhindra att ett annat projekt som importerar projektet åsidosätter de mål som du redan har åsidosatt. Det sista AfterBuild målet som definierats i projektfilen, efter att alla andra projekt har importerats, är till exempel det som används under bygget.

Du kan skydda dig mot oavsiktliga åsidosättningar av mål genom att åsidosätta de DependsOn egenskaper som används i attribut i DependsOnTargets de gemensamma målen. Till exempel innehåller Build-målet ett attributvärde för DependsOnTargets"$(BuildDependsOn)". Tänk på att:

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

Den här XML-delen anger att innan Build målet kan köras måste alla mål som anges i BuildDependsOn egenskapen köras först. Egenskapen BuildDependsOn definieras som:

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

Du kan åsidosätta det här egenskapsvärdet genom att deklarera en annan egenskap med namnet BuildDependsOn i slutet av projektfilen. I ett SDK-projekt innebär det att du måste använda explicita importer. Se Implicita och explicita importer så att du kan placera DependsOn egenskapen efter den senaste importen. Genom att inkludera den tidigare BuildDependsOn egenskapen i den nya egenskapen kan du lägga till nya mål i början och slutet av mållistan. Till exempel:

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

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

Projekt som importerar projektfilen kan utöka dessa egenskaper ytterligare utan att skriva över de anpassningar som du har gjort.

Så här åsidosätter du en DependsOn-egenskap

  1. Identifiera en fördefinierad DependsOn egenskap i de gemensamma mål som du vill åsidosätta. I följande tabell finns en lista över de vanligtvis åsidosatta DependsOn egenskaperna.

  2. Definiera en annan instans av egenskapen eller egenskaperna i slutet av projektfilen. Inkludera den ursprungliga egenskapen, till exempel $(BuildDependsOn), i den nya egenskapen.

  3. Definiera dina anpassade mål före eller efter egenskapsdefinitionen.

  4. Skapa projektfilen.

Vanliga åsidosatta DependsOn-egenskaper

Egenskapsnamn Tillagda objekt körs innan denna punkt.
BuildDependsOn Huvudingångspunkten för byggprocessen. Åsidosätt den här egenskapen om du vill infoga anpassade mål före eller efter hela byggprocessen.
RebuildDependsOn Den Rebuild
RunDependsOn Körningen av den slutliga byggversionens resultat (om det är en .EXE)
CompileDependsOn Kompilering (Compile mål). Åsidosätt den här egenskapen om du vill infoga anpassade processer före eller efter kompileringssteget.
CreateSatelliteAssembliesDependsOn Skapandet av satellitsammansättningarna
CleanDependsOn Målet Clean (ta bort alla mellanliggande och slutgiltiga build-utdata). Åsidosätt den här egenskapen om du vill rensa utdata från din anpassade byggprocess.
PostBuildEventDependsOn Målet PostBuildEvent
PublishBuildDependsOn Skapa publicering
ResolveAssemblyReferencesDependsOn Målet ResolveAssemblyReferences (att hitta transitiv stängning av beroenden för ett visst beroende). Se även ResolveAssemblyReference.

Exempel: BuildDependsOn och CleanDependsOn

Följande exempel liknar BeforeTargets exemplet och AfterTargets men visar hur du uppnår liknande funktioner. Den utökar bygget genom att använda BuildDependsOn för att lägga till din egen uppgift CustomAfterBuild som kopierar utdatafilerna efter bygget, och lägger också till den motsvarande uppgiften CustomClean med hjälp av CleanDependsOn.

I det här exemplet är det här ett SDK-projekt. Som du nämnde i anteckningen om SDK-liknande projekt tidigare i den här artikeln måste du använda den manuella importmetoden i stället för Sdk det attribut som Visual Studio använder när den genererar projektfiler.

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

Ordningen på element är viktig. Elementen BuildDependsOn och CleanDependsOn måste visas när standard-SDK-målfilen har importerats.

Åsidosätt fördefinierade mål

De vanliga .targets filerna innehåller en uppsättning fördefinierade tomma mål som anropas före och efter några av de viktigaste målen i byggprocessen. MSBuild anropar BeforeBuild till exempel målet före huvudmålet CoreBuild och målet AfterBuild efter CoreBuild målet. Som standard gör de tomma målen i de gemensamma målen ingenting, men du kan åsidosätta deras standardbeteende genom att definiera de mål som du vill använda i en projektfil. De metoder som beskrivs tidigare i den här artikeln är att föredra, men du kan stöta på äldre kod som använder den här metoden.

Om ditt projekt använder ett SDK (till exempel Microsoft.Net.Sdk) måste du göra en ändring från implicit till explicit import, enligt beskrivningen i Explicit och implicit import.

Så här åsidosätter du ett fördefinierat mål

  1. Om projektet använder Sdk attributet ändrar du det till den explicita importsyntaxen. Se Explicita och implicita importer.

  2. Identifiera ett fördefinierat mål i de gemensamma mål som du vill åsidosätta. I följande tabell finns en fullständig lista över mål som du kan åsidosätta på ett säkert sätt.

  3. Definiera målet eller målen i slutet av projektfilen, omedelbart före taggen </Project> och efter den explicita SDK-importen. Till exempel:

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

    Observera att Sdk attributet för elementet på den översta nivån Project har tagits bort.

  4. Skapa projektfilen.

Tabell över fördefinierade mål

I följande tabell visas alla mål i de gemensamma mål som du kan åsidosätta.

Målnamn Beskrivning
BeforeCompile, AfterCompile Uppgifter som infogas i något av dessa mål körs före eller efter kärnkompileringen. De flesta anpassningar görs i ett av dessa två mål.
BeforeBuild, AfterBuild Aktiviteter som infogas i något av dessa mål körs före eller efter allt annat i bygget. Obs: Målen BeforeBuild och AfterBuild har redan definierats i kommentarer i slutet av de flesta projektfiler, vilket gör att du enkelt kan lägga till för- och efterkompileringshändelser i projektfilen.
BeforeRebuild, AfterRebuild Uppgifter som infogas i något av dessa mål körs före eller efter att kärnfunktionen för återskapande anropas. Ordningen på målkörningen i Microsoft.Common.targets är: BeforeRebuild, Clean, Build och sedan AfterRebuild.
BeforeClean, AfterClean Aktiviteter som infogas i något av dessa mål körs före eller efter att kärnfunktionaliteten för rengöring anropas.
BeforePublish, AfterPublish Aktiviteter som infogas i något av dessa mål körs före eller efter att huvudpubliceringsfunktionen anropas.
BeforeResolveReferences, AfterResolveReferences Uppgifter som infogas i något av dessa mål körs före eller efter att sammanställningsreferenser är lösta.
BeforeResGen, AfterResGen Aktiviteter som infogas i något av dessa mål körs före eller efter att resurser genereras.

Det finns många fler mål i byggsystemet och .NET SDK, se MSBuild-mål – SDK och standardversionsmål.

Metodtips för anpassade mål

Egenskaperna DependsOnTargets och BeforeTargets kan båda ange att ett mål måste köras före ett annat mål, men båda behövs i olika scenarier. De skiljer sig åt när det gäller vilket mål som beroendekravet anges för. Du har bara kontroll över dina egna mål och kan inte på ett säkert sätt ändra systemmålen eller andra importerade mål, så det begränsar ditt val av metoder.

När du skapar ett anpassat mål följer du dessa allmänna riktlinjer för att säkerställa att målet körs i den avsedda ordningen.

  1. Använd DependsOnTargets attributet för att specificera mål som måste genomföras innan ditt mål exekveras. För en kedja med mål som du styr kan varje mål ange den tidigare medlemmen i kedjan i DependsOnTargets.

  2. Använd BeforeTargets för alla mål som du inte kontrollerar som du måste köra tidigare (till exempel BeforeTargets="PrepareForBuild" för ett mål som måste köras tidigt i bygget).

  3. Använd AfterTargets för alla mål som du inte kontrollerar som garanterar att de utdata du behöver är tillgängliga. Ange till exempel AfterTargets="ResolveReferences" för något som ändrar en lista med referenser.

  4. Du kan använda dessa i kombination. Till exempel DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile".

Explicit och implicit importera

Projekt som genereras av Visual Studio använder Sdk vanligtvis attributet för projektelementet. Dessa typer av projekt kallas för SDK-liknande projekt. Se Använda MSBuild-projekt-SDK:er. Här är ett exempel:

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

När projektet använder Sdk attributet läggs två importer implicit till, en i början av projektfilen och en i slutet.

Implicita importer motsvarar att ha en importinstruktion som den här som den första raden i projektfilen, efter elementet Project :

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

och följande importinstruktion som den sista raden i projektfilen:

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

Den här syntaxen kallas explicita SDK-importer. När du använder den här explicita syntaxen Sdk bör du utelämna attributet för projektelementet.

Den implicita SDK-importen motsvarar import av specifika "vanliga" .props eller .targets filer som är en typisk konstruktion i äldre projektfiler, till exempel:

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

och

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

Alla sådana gamla referenser bör ersättas med den explicita SDK-syntaxen som visades tidigare i det här avsnittet.

Med den explicita SDK-syntaxen kan du lägga till din egen kod före den första importen eller efter den slutliga SDK-importen. Det innebär att du kan ändra beteendet genom att ange egenskaper före den första importen som börjar gälla i den importerade .props filen, och du kan åsidosätta ett mål som definieras i en av SDK-filerna .targets efter den slutliga importen. Med den här metoden kan du åsidosätta BeforeBuild eller AfterBuild enligt beskrivningen nedan.

Nästa steg

Det finns mycket mer du kan göra med MSBuild för att anpassa versionen. Se Anpassa din bygg.