Étendre le processus de génération Visual Studio
Le processus de génération Visual Studio est défini par une série de fichiers .targets
MSBuild importés dans votre fichier projet. Ces importations sont implicites, si vous utilisez un Kit de développement logiciel (SDK) comme c'est généralement le cas pour les projets Visual Studio.. Parmi ces fichiers importés, Microsoft.Common.targets peut être étendu de manière à exécuter des tâches personnalisées à différentes étapes du processus de génération. Cet article décrit deux méthodes permettant d’étendre le processus de génération de la version de Visual Studio :
Créez une cible personnalisée et spécifiez quand elle doit s’exécuter à l’aide des attributs
BeforeTargets
etAfterTargets
.Remplacez les propriétés
DependsOn
définies dans les cibles courantes.Remplacement de cibles prédéfinies spécifiques définies dans les cibles courantes (Microsoft.Common.targets ou les fichiers importés).
AfterTargets et BeforeTargets
Vous pouvez utiliser les attributs AfterTargets
et BeforeTargets
sur votre cible personnalisée pour spécifier quand elle doit s’exécuter.
L’exemple suivant montre comment utiliser l’attribut AfterTargets
pour ajouter une cible personnalisée qui effectue une action avec les fichiers de sortie. Dans ce cas, les fichiers de sortie sont copiés dans un nouveau dossier CustomOutput. L’exemple montre également comment nettoyer les fichiers créés par l’opération de build personnalisée avec une cible CustomClean
à l’aide d’un attribut BeforeTargets
et en spécifiant que l’opération de nettoyage personnalisée s’exécute avant la cible CoreClean
.
<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>
Avertissement
Veillez à utiliser des noms différents de ceux des cibles prédéfinies (nous avons par exemple nommé la cible de version personnalisée CustomAfterBuild
, et non AfterBuild
), car ces cibles prédéfinies sont remplacées lors de l’importation du kit SDK qui les définit également. Reportez-vous au tableau à la fin de cet article pour obtenir la liste des cibles prédéfinies.
Étendre les propriétés DependsOn
Une autre façon d’étendre le processus de génération consiste à utiliser les propriétés DependsOn
(par exemple, BuildDependsOn
) pour spécifier des cibles qui doivent être exécutées avant une cible standard.
Cette méthode est préférable à la substitution de cibles prédéfinies, décrites dans la section suivante. Remplacer les cibles prédéfinies est une ancienne méthode qui est toujours supportée, mais, parce que MSBuild évalue la définition des cibles de manière séquentielle, il n'y a aucun moyen d'empêcher un autre projet qui importe votre projet de remplacer les cibles que vous avez déjà remplacées. Ainsi, par exemple, la dernière cible AfterBuild
définie dans le fichier projet, une fois que tous les autres projets ont été importés, sera celle utilisée pour la génération.
Vous pouvez empêcher la substitution involontaire des cibles en écrasant les propriétés DependsOn
qui sont utilisées dans les attributs DependsOnTargets
des cibles courantes. Par exemple, la cible Build
contient une valeur d’attribut DependsOnTargets
égale à "$(BuildDependsOn)"
. Considérez les aspects suivants :
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
Ce code XML indique que pour exécuter la cible Build
, vous devez d’abord exécuter toutes les cibles spécifiées dans la propriété BuildDependsOn
. La propriété BuildDependsOn
est définie de la manière suivante :
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
Vous pouvez remplacer cette valeur de propriété en déclarant une autre propriété nommée BuildDependsOn
à la fin de votre fichier projet. Dans un projet de style SDK, cela signifie que vous devez utiliser des importations explicites. Consultez les importations implicites et explicites pour pouvoir placer la propriété DependsOn
après la dernière importation. En incluant la propriété BuildDependsOn
précédente dans la nouvelle propriété, vous pouvez ajouter de nouvelles cibles au début et à la fin de la liste de cibles. Par exemple :
<PropertyGroup>
<BuildDependsOn>
MyCustomTarget1;
$(BuildDependsOn);
MyCustomTarget2
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget1">
<Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
<Message Text="Running MyCustomTarget2..."/>
</Target>
Les projets qui importent votre fichier de projet peuvent étendre ces propriétés sans remplacer les personnalisations que vous avez effectuées.
Pour substituer une propriété DependsOn
Identifiez une propriété
DependsOn
prédéfinie dans les cibles communes que vous souhaitez remplacer. Voir le tableau suivant pour une liste des propriétésDependsOn
couramment remplacées.Définissez une autre instance de la ou des propriétés à la fin de votre fichier projet. Incluez la propriété d’origine (par exemple
$(BuildDependsOn)
) dans la nouvelle propriété.Définissez vos cibles personnalisées avant ou après la définition de la propriété.
Générez le fichier projet.
Propriétés DependsOn communément substituées
Nom de la propriété | Les cibles ajoutées s’exécutent avant ce point : |
---|---|
BuildDependsOn |
Le point d'entrée principal de la version. Propriété à remplacer si vous souhaitez insérer des cibles personnalisées avant ou après l’intégralité du processus de génération de la version. |
RebuildDependsOn |
Rebuild . |
RunDependsOn |
Exécution de la sortie de version finale (s’il s’agit d’un .EXE) |
CompileDependsOn |
La compilation (Compile cible). Propriété à remplacer si vous souhaitez insérer des processus personnalisés avant ou après l’étape de compilation. |
CreateSatelliteAssembliesDependsOn |
Création des assemblys satellites |
CleanDependsOn |
La cible Clean (Suppression de toutes les sorties de build intermédiaires et finales). Propriété à remplacer si vous souhaitez nettoyer la sortie de votre processus de génération de la version. |
PostBuildEventDependsOn |
La cible PostBuildEvent |
PublishBuildDependsOn |
Publication de version |
ResolveAssemblyReferencesDependsOn |
La cible ResolveAssemblyReferences (recherche de la fermeture transitive des dépendances pour une dépendance donnée). Consultez l’article ResolveAssemblyReference . |
Exemple : BuildDependsOn et CleanDependsOn
L’exemple suivant est similaire à l’exemple BeforeTargets
et AfterTargets
, mais il montre comment obtenir des fonctionnalités similaires. Il étend la build en utilisant BuildDependsOn
pour ajouter votre propre tâche CustomAfterBuild
qui copie les fichiers de sortie après la build, et ajoute également la tâche CustomClean
correspondante à l’aide de CleanDependsOn
.
Dans cet exemple, il s’agit d’un projet de type SDK. Comme indiqué précédemment dans la remarque sur les projets de type SDK dans cet article, vous devez utiliser la méthode d’importation manuelle au lieu de l’attribut Sdk
utilisé par Visual Studio quand il génère des fichiers projet.
<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’ordre des éléments est important. Les éléments BuildDependsOn
et CleanDependsOn
doivent s’afficher après l’importation du fichier cible de kit SDK standard.
Substituer des cibles prédéfinies
Les fichiers .targets
courants contiennent un ensemble de cibles vides prédéfinies qui sont appelées avant et après certaines cibles majeures du processus de génération de la version. Par exemple, MSBuild appelle la cible BeforeBuild
avant la cible principale CoreBuild
et appelle la cible AfterBuild
après la cible CoreBuild
. Par défaut, les cibles vides dans les cibles communes ne font rien, mais vous pouvez modifier leur comportement par défaut en définissant les cibles que vous souhaitez dans un fichier de projet. Les méthodes décrites plus haut dans cet article sont préférées, mais vous pouvez rencontrer un code plus ancien qui utilise cette méthode.
Si votre projet utilise un SDK (par exemple Microsoft.Net.Sdk
), vous devez passer des importations implicites aux importations explicites, comme indiqué dans les importations explicites et implicites.
Pour substituer une cible prédéfinie
Si le projet utilise l’attribut
Sdk
, remplacez-le par la syntaxe d’importation explicite. Voir Importations explicites et implicites.Identifiez une cible prédéfinie dans les cibles courantes que vous souhaitez écraser. Voir le tableau suivant pour la liste complète des cibles que vous pouvez remplacer en toute sécurité.
Définissez la ou les cibles à la fin de votre fichier de projet, immédiatement avant la balise
</Project>
et après l'importation explicite du SDK. Par exemple :<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>
Notez que l’attribut
Sdk
sur l’élément de niveauProject
supérieur a été supprimé.Générez le fichier projet.
Tableau des cibles prédéfinies
Le tableau suivant montre l’ensemble des cibles dans les cibles courantes que vous pouvez remplacer en toute sécurité.
Nom de la cible | Description |
---|---|
BeforeCompile , AfterCompile |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après la compilation principale. La plupart des personnalisations sont effectuées dans l’une de ces deux cibles. |
BeforeBuild , AfterBuild |
Les tâches insérées dans l’une de ces cibles s’exécutent avant ou après tout le reste lors de la génération. Remarque : les cibles BeforeBuild et AfterBuild sont déjà définies dans les commentaires à la fin de la plupart des fichiers projet. Vous pouvez ainsi ajouter facilement des événements pré-build et post-build à votre fichier projet. |
BeforeRebuild , AfterRebuild |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après l’appel de la fonctionnalité de regénération principale. L’ordre d’exécution des cibles dans Microsoft.Common.targets est le suivant : BeforeRebuild , Clean , Build , puis AfterRebuild . |
BeforeClean , AfterClean |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après l’appel de la fonctionnalité de nettoyage principale. |
BeforePublish , AfterPublish |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après l’appel de la fonctionnalité de publication principale. |
BeforeResolveReferences , AfterResolveReferences |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après la résolution des références d’assembly. |
BeforeResGen , AfterResGen |
Les tâches insérées dans l’une de ces cibles sont exécutées avant ou après la génération des ressources. |
Il existe de nombreuses cibles supplémentaires dans le système de génération et le Kit de développement logiciel (SDK) .NET, consultez les cibles MSBuild - SDK et cibles de version par défaut.
Meilleures pratiques pour les cibles personnalisées
Les propriétés DependsOnTargets
et BeforeTargets
peuvent spécifier qu’une cible doit s’exécuter avant une autre cible, mais elles sont toutes les deux nécessaires dans différents scénarios. Elles diffèrent selon lesquelles la spécification de dépendance est spécifiée. Vous n’avez que le contrôle de vos propres cibles et ne pouvez pas modifier en toute sécurité les cibles système ou d’autres cibles importées, ce qui limite votre choix de méthodes.
Lors de la création d’une cible personnalisée, suivez ces instructions générales pour vous assurer que votre cible est exécutée dans l’ordre prévu.
Utilisez l’attribut
DependsOnTargets
pour spécifier des cibles que vous devez effectuer avant l’exécution de votre cible. Pour une chaîne de cibles que vous contrôlez, chaque cible peut spécifier le membre précédent de la chaîne dansDependsOnTargets
.Utilisez
BeforeTargets
pour toute cible que vous ne contrôlez pas que vous devez exécuter avant (commeBeforeTargets="PrepareForBuild"
pour une cible qui doit s’exécuter tôt dans la version).Utilisez
AfterTargets
pour toute cible que vous ne contrôlez pas qui garantit que les sorties dont vous avez besoin sont disponibles. Par exemple, spécifiezAfterTargets="ResolveReferences"
pour un élément qui modifiera une liste de références.Vous pouvez les utiliser en combinaison. Par exemple :
DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"
.
Importations explicites et implicites
Les projets générés par Visual Studio utilisent généralement l’attribut Sdk
sur l’élément de projet. Ces projets sont appelés projets de style SDK. Voir Utiliser les kits SDK de projet MSBuild. Voici un exemple :
<Project Sdk="Microsoft.Net.Sdk">
Lorsque votre projet utilise l’attribut Sdk
, deux importations sont implicitement ajoutées, une au début de votre fichier projet et une à la fin.
Les importations implicites sont équivalentes à avoir une instruction d’importation comme celle-ci en première ligne du fichier de projet, après l'élément Project
:
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
et l’instruction import suivante comme dernière ligne dans le fichier projet :
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Cette syntaxe est appelée importation explicite du Kit de développement logiciel (SDK). Lorsque vous utilisez cette syntaxe explicite, vous devez omettre l’attribut Sdk
sur l’élément de projet.
L’importation implicite du Kit de développement logiciel (SDK) équivaut à importer les fichiers spécifiques « communs » .props
ou .targets
qui constituent une construction classique dans des fichiers projet plus anciens, tels que :
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
et
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Toutes ces anciennes références doivent être remplacées par la syntaxe explicite du Kit de développement logiciel (SDK) indiquée précédemment dans cette section.
L’utilisation de la syntaxe explicite du Kit de développement logiciel (SDK) signifie que vous pouvez ajouter votre propre code avant la première importation ou après l’importation finale du SDK. Cela signifie que vous pouvez modifier le comportement en définissant des propriétés avant la première importation qui prendra effet dans le fichier importé .props
, et vous pouvez remplacer une cible définie dans l’un des fichiers SDK .targets
après l’importation finale. À l’aide de cette méthode, vous pouvez remplacer BeforeBuild
ou AfterBuild
comme indiqué ci-dessous.
Étapes suivantes
Vous pouvez faire beaucoup plus avec MSBuild pour personnaliser la version. Consultez Personnaliser votre build.