Détermination des sorties d'un ProjectReference dans MSBuild sans déclencher reconstructions redondants

StackOverflow https://stackoverflow.com/questions/2325598

Question

Dans le cadre d'une solution contenant de nombreux projets, j'ai un projet que les références (via un <ProjectReference> trois autres projets dans la solution, ainsi que quelques autres). Dans le AfterBuild, je dois copier les résultats de 3 projets dépendants spécifiques à un autre endroit.

Via diverses réponses SO, etc. la façon dont je me suis installé à accomplir qui était:

    <MSBuild 
        Projects="@(ProjectReference)" 
        Targets="Build" 
        BuildInParallel="true" 
        Condition="'%(Name)'=='ProjectA' OR '%(Name)'=='ProjectB' OR '%(Name)'=='ProjectC'">
        <Output TaskParameter="TargetOutputs" ItemName="DependentAssemblies" />
    </MSBuild>
    <Copy SourceFiles="@(DependentAssemblies)" DestinationFolder="XX" SkipUnchangedFiles="true" />

Cependant, je suis tombé sur des problèmes avec cela. La tâche de <MSBuild étape de IncrementalClean finit par la suppression d'un certain nombre de sorties de ProjectC. Lors de l'exécution de cette sous VS2008, un fichier build.force étant déposé dans le dossier obj/Debug de ProjectC qui déclenche alors ProjectC se reconstruit si je compilons sur toute solution si le projet contenant cet objectif de AfterBuild, alors que si l'on exclut ce projet de la construction, il [correctement] ne déclenche pas une recréation de ProjectC (et critique une reconstruction de toutes les personnes à charge de ProjectC). Cela peut être supercherie VS spécifique dans ce cas qui ne se produira pas dans le contexte d'un TeamBuild ou une autre invocation commandline MSBuild (mais sera par VS l'usage le plus commun, alors je dois résoudre ce soit ainsi)

Les projets dépendants (et le reste de la solution en général) ont tous été créés de manière interactive avec VS, et donc les ProjectRefences contiennent des chemins relatifs, etc. Je l'ai vu mention de ce qui est susceptible de causer des problèmes - mais sans plein explication des raisons pour lesquelles, ou quand il sera fixé ou comment travailler autour d'elle. En d'autres termes, je ne suis pas vraiment intéressé par exemple convertir les chemins de ProjectReference à des chemins absolus par la main modification du .csproj.

Bien qu'il soit tout à fait possible que je fais quelque chose de stupide et quelqu'un va immédiatement signaler ce qu'il est (ce qui serait formidable), soyez assurés que j'ai passé beaucoup de temps penché sur les sorties /v:diag etc. (bien que je n'ai pas essayé de construire une repro à partir du sol - ce qui est dans le contexte d'une construction globale relativement complexe)

Était-ce utile?

La solution

Comme indiqué dans mon commentaire, appelant GetTargetPath sur le projet référencé renvoie uniquement l'ensemble de sortie primaire de ce projet. Pour obtenir tous les ensembles de la copie locale référencée du projet référencé, il est un peu messier.

Ajoutez ce qui suit à chaque projet que vous faites référence que vous souhaitez obtenir les CopyLocals de:

    <Target
    Name="ComputeCopyLocalAssemblies"
    DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences"
    Returns="@(ReferenceCopyLocalPaths)" /> 

Ma situation est que je devais recréer la structure du dossier Pipeline pour System.AddIn dans le dossier bin de mon projet de haut niveau de l'hôte. C'est un peu en désordre et je n'étais pas satisfait de la MSDN propose des solutions de déblayage avec le OutputPath - comme qui casse sur notre serveur de build et empêche la création de la structure du dossier dans un autre projet (par exemple un SystemTest)

ainsi que l'ajout de la cible ci-dessus (à l'aide d'une importation .targets), j'ajouté ce qui suit dans un fichier .targets importé par chaque « hôte » qui a besoin du dossier de pipeline créé:

<Target
    Name="ComputePipelineAssemblies"
    BeforeTargets="_CopyFilesMarkedCopyLocal"
    Outputs="%(ProjectReference.Identity)">

    <ItemGroup>
        <_PrimaryAssembly Remove="@(_PrimaryAssembly)" />
        <_DependentAssemblies Remove="@(_DependentAssemblies)" />
    </ItemGroup>

    <!--The Primary Output of the Pipeline project-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="GetTargetPath"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_PrimaryAssembly" />
    </MSBuild>

    <!--Output of any Referenced Projects-->
    <MSBuild Projects="%(ProjectReference.Identity)"
             Targets="ComputeCopyLocalAssemblies"
             Properties="Configuration=$(Configuration)"
             Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
        <Output TaskParameter="TargetOutputs"
                ItemName="_DependentAssemblies" />
    </MSBuild>

    <ItemGroup>
        <ReferenceCopyLocalPaths Include="@(_PrimaryAssembly)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
        <ReferenceCopyLocalPaths Include="@(_DependentAssemblies)"
                                 Condition=" '%(ProjectReference.PipelineFolder)' != '' ">
            <DestinationSubDirectory>%(ProjectReference.PipelineFolder)</DestinationSubDirectory>
        </ReferenceCopyLocalPaths>
    </ItemGroup>
</Target>

J'ai aussi besoin d'ajouter le PipelineFolder nécessaire des méta-données aux références réelles du projet. Par exemple:

    <ProjectReference Include="..\Dogs.Pipeline.AddInSideAdapter\Dogs.Pipeline.AddInSideAdapter.csproj">
        <Project>{FFCD0BFC-5A7B-4E13-9E1B-8D01E86975EA}</Project>
        <Name>Dogs.Pipeline.AddInSideAdapter</Name>
        <Private>False</Private>
        <PipelineFolder>Pipeline\AddInSideAdapter\</PipelineFolder>
    </ProjectReference>

Autres conseils

Votre solution originale devrait fonctionner en changeant simplement

Targets="Build"

à

Targets="GetTargetPath"

La cible GetTargetPath retourne simplement la propriété TargetPath et ne nécessite pas la construction.

Vous pouvez protéger vos fichiers ProjectC si vous appelez une cible comme cette première:

  <Target Name="ProtectFiles">
    <ReadLinesFromFile File="obj\ProjectC.csproj.FileListAbsolute.txt">
        <Output TaskParameter="Lines" ItemName="_FileList"/>
    </ReadLinesFromFile>
    <CreateItem Include="@(_DllFileList)" Exclude="File1.sample; File2.sample">
        <Output TaskParameter="Include" ItemName="_FileListWitoutProtectedFiles"/>
    </CreateItem>      
      <WriteLinesToFile 
        File="obj\ProjectC.csproj.FileListAbsolute.txt"
        Lines="@(_FileListWitoutProtectedFiles)"
        Overwrite="true"/>
  </Target>

Mon solution actuelle est basée sur cette question SO , à savoir, je:

    <ItemGroup>
        <DependentAssemblies Include="
            ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
            ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
            ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>

Cependant, cela va se rompre sous TeamBuild (où toutes les sorties se retrouvent dans un répertoire), et même si le nom de l'un des résultats des projets dépendants changent.

EDIT: Aussi la recherche des commentaires sur s'il y a une réponse plus propre pour savoir comment faire le hardcoding un peu plus propre que:

    <PropertyGroup>
        <_TeamBuildingToSingleOutDir Condition="'$(TeamBuildOutDir)'!='' AND '$(CustomizableOutDir)'!='true'">true</_TeamBuildingToSingleOutDir>
    </PropertyGroup>

et

    <ItemGroup>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'!='true'"
            Include="
                ..\ProjectA\bin\$(Configuration)\ProjectA.dll;
                ..\ProjectB\bin\$(Configuration)\ProjectB.dll;
                ..\ProjectC\bin\$(Configuration)\ProjectC.dll">
        </DependentAssemblies>
        <DependentAssemblies 
            Condition="'$(_TeamBuildingToSingleOutDir)'=='true'"
            Include="
                $(OutDir)\ProjectA.dll;
                $(OutDir)\ProjectB.dll;
                $(OutDir)\ProjectC.dll">
        </DependentAssemblies>
    </ItemGroup>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top