Estendere il processo di compilazione di Visual Studio
Il processo di compilazione di Visual Studio è definito da una serie di file MSBuild .targets
importati nel file di progetto. Queste importazioni sono implicite, se si usa un SDK come progetti di Visual Studio in genere. Uno di questi file importati, Microsoft.Common.targets, può essere esteso per consentire l'esecuzione di attività personalizzate in diversi punti del processo di compilazione. Questo articolo illustra tre metodi che è possibile usare per estendere il processo di compilazione di Visual Studio:
Creare una destinazione personalizzata e specificare quando deve essere eseguita usando
BeforeTargets
gli attributi eAfterTargets
.Eseguire l'override delle
DependsOn
proprietà definite nelle destinazioni comuni.Eseguire l'override di destinazioni predefinite specifiche definite nelle destinazioni comuni (Microsoft.Common.targets o i file importati).
AfterTargets e BeforeTargets
È possibile usare AfterTargets
gli attributi e BeforeTargets
nella destinazione personalizzata per specificare quando deve essere eseguito.
Nell'esempio seguente viene illustrato come usare l'attributo AfterTargets
per aggiungere una destinazione personalizzata che esegue operazioni con i file di output. In questo caso, copia i file di output in una nuova cartella CustomOutput. Nell'esempio viene inoltre illustrato come pulire i file creati dall'operazione di compilazione personalizzata con una CustomClean
destinazione usando un BeforeTargets
attributo e specificando che l'operazione pulita personalizzata viene eseguita prima della CoreClean
destinazione.
<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>
Avviso
Assicurarsi di usare nomi diversi rispetto alle destinazioni predefinite (ad esempio, la destinazione di compilazione personalizzata qui è CustomAfterBuild
, non AfterBuild
), poiché tali destinazioni predefinite vengono sostituite dall'importazione dell'SDK che le definisce anche. Fare riferimento alla tabella alla fine di questo articolo per un elenco di destinazioni predefinite.
Estendere le proprietà DependsOn
Un altro modo per estendere il processo di compilazione consiste nell'usare le DependsOn
proprietà ,ad esempio BuildDependsOn
, per specificare le destinazioni che devono essere eseguite prima di una destinazione standard.
Questo metodo è preferibile eseguire l'override delle destinazioni predefinite, descritte nella sezione successiva. L'override delle destinazioni predefinite è un metodo meno recente ancora supportato, ma poiché MSBuild valuta la definizione di destinazioni in sequenza, non è possibile impedire a un altro progetto che importa il progetto di eseguire l'override delle destinazioni già sottoposte a override. Di conseguenza, ad esempio, l'ultima destinazione AfterBuild
definita nel file di progetto, al termine dell'importazione di tutti gli altri progetti, sarà quella usata durante la compilazione.
È possibile proteggersi dagli override imprevisti delle destinazioni eseguendo l'override delle DependsOn
proprietà usate negli DependsOnTargets
attributi in tutte le destinazioni comuni. Nella destinazione Build
, ad esempio, il valore dell'attributo DependsOnTargets
è "$(BuildDependsOn)"
. Tenere in considerazione:
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
Questa parte di codice XML indica che la destinazione Build
può essere eseguita solo dopo l'esecuzione di tutte le destinazioni specificate nella proprietà BuildDependsOn
. La proprietà BuildDependsOn
è definita come segue:
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
È possibile eseguire l'override di questo valore di proprietà dichiarando un'altra proprietà denominata BuildDependsOn
alla fine del file di progetto. In un progetto in stile SDK, questo significa che è necessario usare importazioni esplicite. Vedere Importazioni implicite ed esplicite, in modo da poter inserire la DependsOn
proprietà dopo l'ultima importazione. L'inclusione della proprietà BuildDependsOn
precedente nella nuova proprietà consente di aggiungere nuove destinazioni all'inizio e alla fine dell'elenco di destinazioni. Ad esempio:
<PropertyGroup>
<BuildDependsOn>
MyCustomTarget1;
$(BuildDependsOn);
MyCustomTarget2
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget1">
<Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
<Message Text="Running MyCustomTarget2..."/>
</Target>
I progetti che importano il file di progetto possono estendere ulteriormente queste proprietà senza sovrascrivere le personalizzazioni apportate.
Per eseguire l'override di una proprietà DependsOn
Identificare una proprietà predefinita
DependsOn
nelle destinazioni comuni di cui si vuole eseguire l'override. Per un elenco delle proprietà comunemente sottoposteDependsOn
a override, vedere la tabella seguente.Definire un'altra istanza della proprietà o delle proprietà alla fine del file di progetto. Nella nuova proprietà includere la proprietà originale, ad esempio
$(BuildDependsOn)
.Definire le destinazioni personalizzate prima o dopo la definizione della proprietà.
Compilare il file di progetto.
Proprietà DependsOn comunemente sottoposte a override
Nome della proprietà | Le destinazioni aggiunte vengono eseguite prima di questo punto: |
---|---|
BuildDependsOn |
Punto di ingresso della build principale. Eseguire l'override di questa proprietà se si desidera inserire destinazioni personalizzate prima o dopo l'intero processo di compilazione. |
RebuildDependsOn |
Oggetto Rebuild |
RunDependsOn |
Esecuzione dell'output di compilazione finale (se si tratta di un .EXE) |
CompileDependsOn |
Compilazione (Compile destinazione). Eseguire l'override di questa proprietà se si desidera inserire processi personalizzati prima o dopo il passaggio di compilazione. |
CreateSatelliteAssembliesDependsOn |
Creazione degli assembly satellite |
CleanDependsOn |
Destinazione Clean (eliminazione di tutti gli output di compilazione intermedi e finali). Eseguire l'override di questa proprietà se si vuole pulire l'output dal processo di compilazione personalizzato. |
PostBuildEventDependsOn |
Destinazione PostBuildEvent |
PublishBuildDependsOn |
Pubblicazione della compilazione |
ResolveAssemblyReferencesDependsOn |
Destinazione ResolveAssemblyReferences (individuazione della chiusura transitiva delle dipendenze per una determinata dipendenza). Vedere ResolveAssemblyReference . |
Esempio: BuildDependsOn e CleanDependsOn
L'esempio seguente è simile all'esempio BeforeTargets
e AfterTargets
, ma mostra come ottenere funzionalità simili. Estende la compilazione usando BuildDependsOn
per aggiungere un'attività CustomAfterBuild
personalizzata che copia i file di output dopo la compilazione e aggiunge anche l'attività corrispondente CustomClean
usando CleanDependsOn
.
In questo esempio si tratta di un progetto di tipo SDK. Come accennato nella nota sui progetti in stile SDK in precedenza in questo articolo, è necessario usare il metodo di importazione manuale anziché l'attributo Sdk
usato da Visual Studio quando genera i file di progetto.
<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->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles="@(_FilesToCopy->'$(_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>
L'ordine degli elementi è importante. Gli BuildDependsOn
elementi e CleanDependsOn
devono essere visualizzati dopo l'importazione del file di destinazione dell'SDK standard.
Eseguire l'override di destinazioni predefinite
I file comuni .targets
contengono un set di destinazioni vuote predefinite chiamate prima e dopo alcune delle principali destinazioni nel processo di compilazione. Ad esempio, MSBuild chiama la destinazione prima della BeforeBuild
destinazione principale CoreBuild
e della AfterBuild
destinazione dopo la CoreBuild
destinazione. Per impostazione predefinita, le destinazioni vuote nelle destinazioni comuni non eseguono alcuna operazione, ma è possibile eseguire l'override del comportamento predefinito definendo le destinazioni desiderate in un file di progetto. I metodi descritti in precedenza in questo articolo sono preferiti, ma si potrebbe riscontrare codice meno recente che usa questo metodo.
Se il progetto usa un SDK (ad esempio Microsoft.Net.Sdk
), è necessario apportare una modifica da importazioni implicite a esplicite, come descritto in Importazioni esplicite e implicite.
Per eseguire l'override di una destinazione predefinita
Se il progetto usa l'attributo
Sdk
, impostarlo sulla sintassi di importazione esplicita. Vedere Importazioni esplicite e implicite.Identificare una destinazione predefinita nelle destinazioni comuni di cui si vuole eseguire l'override. Vedere la tabella seguente per l'elenco completo delle destinazioni di cui è possibile eseguire l'override in modo sicuro.
Definire la destinazione o le destinazioni alla fine del file di progetto, immediatamente prima del
</Project>
tag e dopo l'importazione esplicita dell'SDK. Ad esempio:<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>
Si noti che l'attributo nell'elemento
Sdk
di primo livelloProject
è stato rimosso.Compilare il file di progetto.
Tabella di destinazioni predefinite
La tabella seguente illustra tutte le destinazioni nelle destinazioni comuni di cui è possibile eseguire l'override.
Nome di destinazione | Descrizione |
---|---|
BeforeCompile , AfterCompile |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo il completamento della compilazione principale. La maggior parte delle personalizzazioni avviene in una di queste due destinazioni. |
BeforeBuild , AfterBuild |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo qualsiasi altra attività nella compilazione. Nota: le destinazioni BeforeBuild e AfterBuild sono già definite nei commenti alla fine della maggior parte dei file di progetto, consentendo di aggiungere facilmente eventi di pre e post-compilazione nel file di progetto. |
BeforeRebuild , AfterRebuild |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo il richiamo della funzionalità di base per la ricompilazione. L'ordine di esecuzione delle destinazioni in Microsoft.Common.targets è: BeforeRebuild , Clean , Build e quindi AfterRebuild . |
BeforeClean , AfterClean |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo il richiamo della funzionalità di base per la pulitura. |
BeforePublish , AfterPublish |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo il richiamo della funzionalità di base per la pubblicazione. |
BeforeResolveReferences , AfterResolveReferences |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo la risoluzione dei riferimenti all'assembly. |
BeforeResGen , AfterResGen |
Le attività inserite in una di queste destinazioni vengono eseguite prima o dopo la generazione delle risorse. |
Esistono molte altre destinazioni nel sistema di compilazione e .NET SDK, vedere Destinazioni MSBuild - SDK e destinazioni di compilazione predefinite.
Procedure consigliate per destinazioni personalizzate
Le proprietà DependsOnTargets
e BeforeTargets
possono specificare che una destinazione deve essere eseguita prima di un'altra destinazione, ma sono entrambi necessari in scenari diversi. Si differenziano per specificare il requisito di dipendenza. Si ha solo il controllo sulle destinazioni personalizzate e non è possibile modificare in modo sicuro le destinazioni di sistema o altre destinazioni importate, in modo che vincoli la scelta dei metodi.
Quando si crea una destinazione personalizzata, seguire queste linee guida generali per assicurarsi che la destinazione venga eseguita nell'ordine previsto.
Usare l'attributo
DependsOnTargets
per specificare le destinazioni che è necessario eseguire prima dell'esecuzione della destinazione. Per una catena di destinazioni controllate, ogni destinazione può specificare il membro precedente della catena inDependsOnTargets
.Usare
BeforeTargets
per qualsiasi destinazione che non è possibile controllare prima di eseguire , ad esempioBeforeTargets="PrepareForBuild"
per una destinazione che deve essere eseguita all'inizio della compilazione.Usare
AfterTargets
per qualsiasi destinazione che non si controlla che garantisce che gli output necessari siano disponibili. Ad esempio, specificareAfterTargets="ResolveReferences"
per un elemento che modificherà un elenco di riferimenti.È possibile usarli in combinazione. Ad esempio:
DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"
.
Importazioni esplicite e implicite
I progetti generati da Visual Studio usano in genere l'attributo Sdk
nell'elemento del progetto. Questi tipi di progetti sono denominati progetti in stile SDK. Vedere Usare GLI SDK del progetto MSBuild. Ecco un esempio:
<Project Sdk="Microsoft.Net.Sdk">
Quando il progetto usa l'attributo Sdk
, due importazioni vengono aggiunte in modo implicito, una all'inizio del file di progetto e una alla fine.
Le importazioni implicite sono equivalenti alla presenza di un'istruzione import simile alla seguente come prima riga nel file di progetto, dopo l'elemento Project
:
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
e l'istruzione import seguente come ultima riga nel file di progetto:
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Questa sintassi viene definita importazione esplicita dell'SDK. Quando si usa questa sintassi esplicita, è necessario omettere l'attributo Sdk
nell'elemento del progetto.
L'importazione implicita dell'SDK equivale all'importazione dei file o "comuni" .props
.targets
specifici che sono un costrutto tipico nei file di progetto meno recenti, ad esempio:
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
e
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Eventuali riferimenti precedenti devono essere sostituiti con la sintassi esplicita dell'SDK illustrata in precedenza in questa sezione.
Usando la sintassi esplicita dell'SDK è possibile aggiungere codice personalizzato prima della prima importazione o dopo l'importazione finale dell'SDK. Ciò significa che è possibile modificare il comportamento impostando le proprietà prima della prima importazione che avrà effetto nel file importato .props
ed è possibile eseguire l'override di una destinazione definita in uno dei file SDK .targets
dopo l'importazione finale. Usando questo metodo, è possibile eseguire l'override BeforeBuild
o AfterBuild
come illustrato di seguito.
Passaggi successivi
È possibile eseguire molte altre operazioni con MSBuild per personalizzare la compilazione. Vedere Personalizzare la compilazione.