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:

sortie microsoft build classique

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:

 

sortie console build application

Et par exemple dans ConsoleApplicaton1:

MS Build console application

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:

image

Et dans un des dossiers de Output:

image

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.