1. ホーム
  2. c#

[解決済み] コピーローカル」のベストプラクティスと、プロジェクトリファレンスを使った方法とは?

2022-04-26 09:17:14

質問

私は大規模なC#ソリューションファイル(~100プロジェクト)を持っており、ビルド時間を改善しようとしています。 私は、"Copy Local"が私たちにとって多くの場合無駄であると思いますが、ベストプラクティスについて疑問に思っています。

私たちの .sln では、アプリケーション A がアセンブリ B に依存し、そのアセンブリ B がアセンブリ C に依存しています。私たちの場合、数十の "B" と少数の "C" があります。 これらはすべて.slnに含まれているので、プロジェクト参照を使用しています。 現在、すべてのアセンブリは $(SolutionDir)/Debug (または Release) にビルドされています。

デフォルトでは、Visual Studio はこれらのプロジェクト参照を "Copy Local" としてマークし、すべての "C" が "B" をビルドするたびに $(SolutionDir)/Debug に 1 回コピーされることになります。 これは無駄のように思えます。 ローカルへのコピー機能をオフにすると、どのような問題が発生するのでしょうか? 大規模なシステムを持つ他の人たちはどうしているのでしょうか?

FOLLOWUP

多くの回答が、ビルドをより小さな.slnファイルに分割することを提案しています... 上記の例では、まず基礎クラス "C" を構築し、次にモジュールの大部分 "B" 、そしていくつかのアプリケーション "A" を構築する予定です。 このモデルでは、BからCへの非プロジェクト参照が必要です。そこで直面する問題は、"Debug" または "Release" がヒントパスに組み込まれてしまい、"B" の Release ビルドを "C" の debug ビルドに対して行うことになることです。

ビルドを複数の.slnファイルに分割している方は、この問題をどのように管理しているのでしょうか?

解決方法は?

以前のプロジェクトで、私はプロジェクト参照を含む1つの大きなソリューションに取り組み、パフォーマンスの問題にもぶつかりました。解決策は3つありました。

  1. 常にCopy Localプロパティをfalseに設定し、msbuildのカスタムステップでこれを強制する。

  2. 各プロジェクトの出力ディレクトリを同じディレクトリに設定します($(SolutionDir)からの相対パスが望ましい)。

  3. フレームワークとともに出荷されるデフォルトのcsターゲットは、現在ビルドされているプロジェクトの出力ディレクトリにコピーされる参照のセットを計算します。これは'References'関係の下で推移的クロージャを計算することを必要とするので、これは次のようになります。 VERY のコストがかかる。この問題を解決するために、私は GetCopyToOutputDirectoryItems ターゲットを共通のターゲットファイル (例. Common.targets をインポートした後に、すべてのプロジェクトでインポートされます。 Microsoft.CSharp.targets . その結果、すべてのプロジェクトファイルは次のようになります。

    <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>
    
    

これにより、ある時間でのビルド時間が、(主にメモリの制約により)数時間から数分に短縮されました。

再定義された GetCopyToOutputDirectoryItems の2,438-2,450行目と2,474-2,524行目をコピーして作成することができます。 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targetsCommon.targets .

その結果、ターゲット定義は次のようになる。

<!-- 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>

この回避策により、1つのソリューションで120ものプロジェクトを持つことができるようになりました。これは、プロジェクトのビルド順序を、ソリューションを分割して手作業で行う代わりに、VSで決定できるという主な利点があります。