[解決済み] コピーローカル」のベストプラクティスと、プロジェクトリファレンスを使った方法とは?
質問
私は大規模な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つありました。
-
常にCopy Localプロパティをfalseに設定し、msbuildのカスタムステップでこれを強制する。
-
各プロジェクトの出力ディレクトリを同じディレクトリに設定します($(SolutionDir)からの相対パスが望ましい)。
-
フレームワークとともに出荷されるデフォルトの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.targets
を
Common.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で決定できるという主な利点があります。
関連
-
[解決済み】「未割り当てのローカル変数を使用」とはどういう意味ですか?
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み] C#のStringとstringの違いは何ですか?
-
[解決済み] C#の正しいバージョン番号を教えてください。
-
[解決済み] C#のオートプロパティに初期値を与える最良の方法は何ですか?
-
[解決済み] C#のconstとreadonlyの違いは何ですか?
-
[解決済み] .NET Coreと.NET Standard Class Libraryのプロジェクトタイプの違いは何ですか?
-
[解決済み】ソリューションでプロジェクトの依存関係を使用する場合、MSBuildは参照(DLLファイル)をコピーしない
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】「未割り当てのローカル変数を使用」とはどういう意味ですか?
-
[解決済み] エンティティタイプ ApplicationUser は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み】C# ASP.NET使用時に「WebClientのリクエスト中に例外が発生しました。
-
[解決済み】値をNULLにすることはできません。パラメータ名:source
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】画像のペイントにTextureBrushを使用する方法
-
[解決済み] エラーメッセージ 'Unable to load one or more of requested types. 詳細については、LoaderExceptionsプロパティを取得してください'。