Comment avoir une sortie de build avec un dossier par projet ?

Sortie Microsoft Build
Prenons une solution simple: 2 consoles et une bibliothèque. Dans une sortie de build classique tout est au même endroit
Cela n’est pas forcément pratique lorsque l’on doit installer séparément les 2 applications: toutes les dépendances sont mélangées.
Si vous avez le framework 4.5 d’installé, il y a une manipulation très simple à faire pour que chaque projet apparaisse dans un dossier à part: il suffit d’ajouter à la ligne de commande de MSBuild le paramètre “/p:GenerateProjectSpecificOutputFolder=true”. On obtient alors le résultat suivant:
Et par exemple dans ConsoleApplicaton1:
Nous retrouvons bien ici la dépendance à ClassLibrary1. Que faisons nous si nous n’avons pas le framework 4.5 ? La réponse est simple: il va falloir ajouter au moment de la compilation des éléments supplémentaire pour prendre en charge la sortie séparée de la compilation.
La façon la plus simple que j’ai trouvée est d’ajouter une étape de copie de fichiers en me basant sur ce qui est fait pour les projets web. Pour rappel, les projets web se retrouvent compilés dans un dossier _PublishedWebSites\”nom du projet”. Il suffit reprendre le principe est de l’appliquer à tous les projets.
Pour cela je vais utiliser un point d’extension du processus de compilation qui permet d’injecter du code MSBuild. Ils s’activent via des paramètres de compilation:
- CustomBeforeMicrosoftCommonTargets
- CustomAfterMicrosoftCommonTargets
Ces 2 variables prennent comme valeur des scripts MSBuild et le contenu des scripts est injecté dans le processus de compilation. Chaque script a un role particulier. Le premier sert surtout à modifier des données qui viennent du paramétrage projet avant que les scripts de base utilisent les valeurs, par exemple forcer la génération des symboles de debug. Le deuxième sert surtout à modifier le comportement du processus de compilation: nous allons nous servir de celui-là pour ajouter notre code:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <PrepareForRunDependsOn> $(PrepareForRunDependsOn); PackageBinaries; </PrepareForRunDependsOn> </PropertyGroup> <Target Name="PackageBinaries" > <PropertyGroup> <PackageOutDir>$(OutDir)\Outputs\$(ProjectName)</PackageOutDir> </PropertyGroup> <Message Text="Copying files for $(MSBuildProjectName) in '$(PackageOutDir)'" Importance="high"/> <MakeDir Directories="$(PackageOutDir)" /> <Copy SourceFiles="@(IntermediateAssembly)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(AddModules)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="$(IntermediateOutputPath)$(TargetName).pdb" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Condition="'$(_DebugSymbolsProduced)'=='true'" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(DocFileItem)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Condition="'$(_DocumentationFileProduced)'=='true'" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(IntermediateSatelliteAssembliesWithTargetPath)" DestinationFiles="@(IntermediateSatelliteAssembliesWithTargetPath->'$(PackageOutDir)\%(Culture)\$(TargetName).resources.dll')" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(ReferenceComWrappersToCopyLocal); @(ResolvedIsolatedComModules); @(_DeploymentLooseManifestFile); @(NativeReferenceFile)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(ReferenceCopyLocalPaths)" DestinationFiles="@(ReferenceCopyLocalPaths->'$(PackageOutDir)\%(DestinationSubDirectory)%(Filename)%(Extension)')" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(_SourceItemsToCopyToOutputDirectory)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(_SourceItemsToCopyToOutputDirectoryAlways)" DestinationFolder="$(PackageOutDir)" SkipUnchangedFiles="false" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> <Copy SourceFiles="@(_binDeployableAssemblies)" DestinationFolder="$(PackageOutDir)\%(RecursiveDir)" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/> </Target> </Project>
Le code est simple: on crée notre dossier et on ajoute tous les éléments nécessaires à la sortie de compilation.
Ensuite, il suffit ensuite de lancer la compilation de cette façon:
msbuild.exe .\ConsoleApplication37.sln /p:OutDir=c:\temp\ConsoleOutput2 /p:CustomAfterMicrosoftCommonTargets=”c:\temp\CustomOutput.targets”
La sortie ressemble maintenant à cela:
Et dans un des dossiers de Output:
La sortie de build n’est pas la même car je ne change pas la sortie de compilation; je ne fais que recopier ce qui est nécessaire; mais le but d’avoir des dossiers séparés est atteint.