154 votes

Quelle est la meilleure pratique pour « Copie locale » et avec les références de projet ?

J'ai un grand c# fichier de solution (~100 projets), et je suis en train d'essayer d'améliorer les temps de construire. Je pense que la "Copie Locale", c'est du gaspillage dans de nombreux cas, pour nous, mais je me pose des questions sur les meilleures pratiques.

Dans notre .la sln, nous avons l'application d'Une fonction de montage B qui dépend de l'assemblée C. Dans notre cas, il y a des dizaines de "B" et une poignée de "C". Puisque ce sont tous inclus dans le .la sln, nous sommes à l'aide de références de projet. Toutes les assemblées, actuellement construire en $(SolutionDir)/Debug (ou de sortie).

Par défaut, Visual Studio marque ces références de projet en tant que "Copie Locale", qui se traduit dans tous les "C" soit copiée dans $(SolutionDir)/Debug une fois pour toutes "B" qui construit. Cela semble inutile. Ce qui peut aller mal si je viens de tourner "Copie Locale"? Ce que font d'autres personnes avec de grands systèmes?

SUIVI:

Beaucoup de réponses suggèrent que la rupture de la construire en de plus petites .la sln fichiers... Dans l'exemple ci-dessus, je voudrais construire la fondation de classes "C" en premier, suivi par la majorité des modules "B", et puis quelques applications, "Un". Dans ce modèle, j'ai besoin d'avoir des références de projet C de B. Le problème que je rencontre c'est que "Debug" ou "Release" sont ancrées dans l'indicateur de chemin et je me retrouve à la construction de ma Release de "B" à l'encontre de debug des "C".

Pour ceux d'entre vous qui a divisé le construire en de multiples .la sln fichiers, comment gérez-vous ce problème?

85voto

Bas Bossink Points 4124

Dans un précédent projet, j'ai travaillé avec une grande solution avec les références de projet et heurtés à un problème de performances. La solution a été de trois ordres:

  1. Réglez toujours la Copie Locale valeur false à la propriété et de faire respecter cette via un custom msbuild étape

  2. Définir le répertoire de sortie pour chaque projet dans le même répertoire (de préférence par rapport à $(SolutionDir)

  3. La valeur par défaut cs cibles être livré avec le cadre de calculer l'ensemble des références à être copié dans le répertoire de sortie du projet actuellement en cours de construction. Étant donné que cela nécessite calcul de la fermeture transitive en vertu de la "Références" cela peut devenir TRÈS coûteux. Ma solution était de redéfinir l' GetCopyToOutputDirectoryItems de la cible dans une commune des objectifs de fichier (par exemple. Common.targets ) qui ont été importées dans chaque projet, après l'importation de la Microsoft.CSharp.targets. Résultant dans chaque fichier de projet pour l'aspect suivant:

    <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        ... snip ...
      </ItemGroup>
      <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
      <Import Project="[relative path to Common.targets]" />
      <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
           Other similar extension points exist, see Microsoft.Common.targets.
      <Target Name="BeforeBuild">
      </Target>
      <Target Name="AfterBuild">
      </Target>
      -->
    </Project>
    

Cela réduit notre temps de construction à un moment donné à partir d'un couple d'heures (surtout à cause des contraintes de mémoire), pour un couple de minutes.

La redéfinition GetCopyToOutputDirectoryItems peut être créé en copiant les lignes 2,438–2.450 et 2.474 les–2,524 de C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets en Common.targets.

Pour l'intégralité de la résultant de la définition de l'objectif devient alors:

<!-- This is a modified version of the Microsoft.Common.targets
     version of this target it does not include transitively
     referenced projects. Since this leads to enormous memory
     consumption and is not needed since we use the single
     output directory strategy.
============================================================
                    GetCopyToOutputDirectoryItems

Get all project items that may need to be transferred to the
output directory.
============================================================ -->
<Target
    Name="GetCopyToOutputDirectoryItems"
    Outputs="@(AllItemsFullPathWithTargetPath)"
    DependsOnTargets="AssignTargetPaths;_SplitProjectReferencesByFileExistence">

    <!-- Get items from this project last so that they will be copied last. -->
    <CreateItem
        Include="@(ContentWithTargetPath->'%(FullPath)')"
        Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(ContentWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"
            >
        <Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways"
                Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='Always'"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory"
                Condition="'%(ContentWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/>
    </CreateItem>

    <CreateItem
        Include="@(_EmbeddedResourceWithTargetPath->'%(FullPath)')"
        Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"
            >
        <Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways"
                Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='Always'"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory"
                Condition="'%(_EmbeddedResourceWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/>
    </CreateItem>

    <CreateItem
        Include="@(Compile->'%(FullPath)')"
        Condition="'%(Compile.CopyToOutputDirectory)'=='Always' or '%(Compile.CopyToOutputDirectory)'=='PreserveNewest'">
        <Output TaskParameter="Include" ItemName="_CompileItemsToCopy"/>
    </CreateItem>
    <AssignTargetPath Files="@(_CompileItemsToCopy)" RootFolder="$(MSBuildProjectDirectory)">
        <Output TaskParameter="AssignedFiles" ItemName="_CompileItemsToCopyWithTargetPath" />
    </AssignTargetPath>
    <CreateItem Include="@(_CompileItemsToCopyWithTargetPath)">
        <Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways"
                Condition="'%(_CompileItemsToCopyWithTargetPath.CopyToOutputDirectory)'=='Always'"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory"
                Condition="'%(_CompileItemsToCopyWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/>
    </CreateItem>

    <CreateItem
        Include="@(_NoneWithTargetPath->'%(FullPath)')"
        Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_NoneWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"
            >
        <Output TaskParameter="Include" ItemName="AllItemsFullPathWithTargetPath"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectoryAlways"
                Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='Always'"/>
        <Output TaskParameter="Include" ItemName="_SourceItemsToCopyToOutputDirectory"
                Condition="'%(_NoneWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'"/>
    </CreateItem>
</Target>

Avec cette solution de contournement en place, j'ai trouvé qu'il réalisable d'avoir plus de 120 projets dans une solution, c'est le principal avantage que l'ordre de compilation des projets peut encore être déterminé par VS au lieu de le faire à la main en divisant votre solution.

32voto

Julien Hoarau Points 23987

Je vous suggère de lire Patrick Smacchia, des articles sur le sujet :

CC.Net VS les projets s'appuient sur la copie locale de référence de l'assemblée option est définie à true. [...] Non seulement cette augmentation de manière significative le temps de compilation (x3 dans le cas de NUnit), mais aussi il bousille votre environnement de travail. Dernier mais non le moins, cela présente le risque pour la gestion des versions d'éventuels problèmes. Btw, NDepend émet un avertissement si il fonde 2 assemblées dans 2 répertoires différents avec le même nom, mais pas le même contenu ou la version.

La bonne chose à faire est de définir les 2 répertoires de $RootDir$\bin\Debug et $RootDir$\bin\Release, et configurer votre VisualStudio projets à émettre des assemblées dans ces répertoires. Toutes les références de projet devrait faire référence à des assemblées dans le répertoire de Débogage.

Vous pouvez aussi lire cet article pour vous aider à réduire votre nombre de projets et d'améliorer votre temps de compilation.

23voto

Aaron Stainback Points 974

Je suggère d'avoir copie locale = false pour presque tous les projets, sauf celui qui est en haut de l'arbre des dépendances. Et pour toutes les références, dans l'un au-dessus de définir copie locale = true. Je vois beaucoup de gens ce qui suggère le partage d'un répertoire de sortie, je pense que c'est une idée horrible basé sur l'expérience passée. Si votre projet de démarrage contient des références à une dll que tout autre projet de maintenir une référence à vous en ferez l'expérience d'un accès\violation de partage, même si la copie locale = false sur tout et votre compilation échouera. Cette question est très annonying et difficiles à dépister. Je suis entièrement d'suggérons de rester à l'écart à partir d'un fragment d'répertoire de sortie et au lieu d'avoir le projet en haut de la dépendance de la chaîne d'écrire le besoin assemblées dans le dossier correspondant. Si vous n'avez pas de projet en "haut", alors je vous suggère un post construire copie pour que tout soit dans le bon endroit. Aussi je voudrais essayer et garder à l'esprit la facilité de débogage. Tout exe projets que j'ai toujours laisser une copie locale=vrai, alors le débogage F5 expérience fonctionne.

10voto

Vous avez raison CopyLocal sera absolument tuer votre temps de construire. Si vous avez une grande source d'arbre, alors vous devez désactiver CopyLocal. Unfortunatley il n'est pas aussi facile qu'il devrait l'être pour le désactiver proprement. J'ai répondu à cette même question à propos de la désactivation de la copie locale à Comment puis-je remplacer CopyLocal (Privé) de références .NET de MSBUILD. Check it out. Ainsi que les Meilleures pratiques pour les grandes solutions de Visual Studio (2008).

Voici quelques infos sur CopyLocal que je le vois.

Copie locale a été mise en oeuvre vraiment pour le débogage. Lorsque vous perpare votre demande d'empaquetage et déploiement, vous devez construire vos projets pour le même dossier de sortie et assurez-vous d'avoir toutes les références dont vous avez besoin là.

J'ai écrit sur la façon de traiter avec la construction de grands arbres source dans l'article MSBuild: les Meilleures Pratiques Pour la Création d'Fiable Construit, Partie 2.

8voto

Bruno Shine Points 944

À mon avis, avoir une solution avec 100 projets est une grosse erreur. Vous pourriez probablement fractionner votre solution en valides petites unités logiques, ce qui simplifie la maintenance et des générations.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X