"로컬 복사"및 프로젝트 참조에 대한 모범 사례는 무엇입니까?
큰 C # 솔루션 파일 (~ 100 프로젝트)이 있으며 빌드 시간을 개선하려고합니다. "로컬 복사"는 많은 경우에 낭비 적이라고 생각하지만 모범 사례가 궁금합니다.
.sln에는 어셈블리 C에 따라 어셈블리 B에 따라 응용 프로그램 A가 있습니다.이 경우 수십 개의 "B"와 소수의 "C"가 있습니다. 이들은 모두 .sln에 포함되어 있으므로 프로젝트 참조를 사용하고 있습니다. 모든 어셈블리는 현재 $ (SolutionDir) / Debug (또는 Release)에 빌드됩니다.
기본적으로 Visual Studio는 이러한 프로젝트 참조를 "로컬 복사"로 표시하므로 모든 "C"가 빌드되는 모든 "B"에 대해 $ (SolutionDir) / Debug에 한 번 복사됩니다. 이것은 낭비되는 것 같습니다. "로컬 복사"를 끄면 무엇이 잘못 될 수 있습니까? 대규모 시스템을 가진 다른 사람들은 무엇을합니까?
후속 조치 :
많은 응답을 통해 빌드를 더 작은 .sln 파일로 분할 할 것을 제안합니다 ... 위의 예에서는 먼저 기본 클래스 "C"를 빌드 한 다음 대량의 모듈 "B"를 빌드 한 다음 몇 가지 애플리케이션 " ㅏ". 이 모델에서는 B에서 C에 대한 프로젝트가 아닌 참조가 필요합니다. 여기서 발생하는 문제는 "디버그"또는 "릴리스"가 힌트 경로에 구워지고 "B"의 릴리스 빌드를 빌드하는 것입니다. "C"의 디버그 빌드에 대해
빌드를 여러 .sln 파일로 분할 한 사용자는이 문제를 어떻게 관리합니까?
이전 프로젝트에서 프로젝트 참조가있는 하나의 큰 솔루션으로 작업했으며 성능 문제도 발생했습니다. 해결책은 세 가지였습니다.
항상 로컬 복사 속성을 false로 설정하고 사용자 지정 msbuild 단계를 통해이를 적용하십시오.
각 프로젝트의 출력 디렉토리를 동일한 디렉토리로 설정하십시오 (바람직하게는 $ (SolutionDir)
프레임 워크와 함께 제공되는 기본 cs 대상은 현재 빌드중인 프로젝트의 출력 디렉토리에 복사 될 참조 세트를 계산합니다. 이것이 '참조'와 관련하여 아래의 전이 폐쇄를 계산이 필요하므로이 될 수있는 매우 비용이 많이 드는. 이 문제를 해결하려면을 가져온 후 모든 프로젝트에서 가져온
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>
이 해결 방법을 사용하면 하나의 솔루션에 120 개가 넘는 프로젝트를 보유 할 수 있다는 것이 나타났습니다. 이는 솔루션을 나눠서 VS 대신 직접 프로젝트의 빌드 순서를 결정할 수 있다는 주요 이점이 있습니다. .
그 주제에 대한 Patric Smacchia의 기사를 읽으라고 제안합니다.
- .NET 어셈블리 및 Visual Studio 프로젝트를 통해 코드베이스 파티션 -> 모든 Visual Studio 프로젝트가 실제로 자체 어셈블리에 있어야합니까? 'Copy Local = True'는 실제로 무엇을 의미합니까?
- NUnit 코드베이스에서 배운 교훈 -> VisualStudio Project Reference + Copy Local true 옵션은 나쁘다! )
- Analyzing the code base of CruiseControl.NET --> Bad usage of Copy Local Reference Assembly option set to True)
CC.Net VS projects rely on the copy local reference assembly option set to true. [...] Not only this increase significantly the compilation time (x3 in the case of NUnit), but also it messes up your working environment. Last but not least, doing so introduces the risk for versioning potential problems. Btw, NDepend will emit a warning if it founds 2 assemblies in 2 different directories with the same name, but not the same content or version.
The right thing to do is to define 2 directories $RootDir$\bin\Debug and $RootDir$\bin\Release, and configure your VisualStudio projects to emit assemblies in these directories. All project references should reference assemblies in the Debug directory.
You could also read this article to help you reduce your projects number and improve your compilation time.
I suggest having copy local = false for almost all projects except the one that is at the top of the dependency tree. And for all the references in the one at the top set copy local = true. I see many people suggesting sharing an output directory; I think this is a horrible idea based on experience. If your startup project holds references to a dll that any other project holds a reference to you will at some point experience an access\sharing violation even if copy local = false on everything and your build will fail. This issue is very annoying and hard to track down. I completely suggest staying away from a shard output directory and instead of having the project at the top of the dependency chain write the needed assemblies to the corresponding folder. If you don't have a project at the "top," then I would suggest a post-build copy to get everything in the right place. Also, I would try and keep in mind the ease of debugging. Any exe projects I still leave copy local=true so the F5 debugging experience will work.
You are correct. CopyLocal will absolutely kill your build times. If you have a large source tree then you should disable CopyLocal. Unfortunately it not as easy as it should be to disable it cleanly. I have answered this exact question about disabling CopyLocal at How do I override CopyLocal (Private) setting for references in .NET from MSBUILD. Check it out. As well as Best practices for large solutions in Visual Studio (2008).
Here is some more info on CopyLocal as I see it.
CopyLocal was implemented really to support local debugging. When you prepare your application for packaging and deployment you should build your projects to the same output folder and make sure you have all the references you need there.
I have written about how to deal with building large source trees in the article MSBuild: Best Practices For Creating Reliable Builds, Part 2.
In my opinion, having a solution with 100 projects is a BIG mistake. You could probably split your solution in valid logical small units, thus simplifying both maintenance and builds.
I am surprised no one has mentioned using hardlinks. Instead of copying the files, it creates a hardlink to the original file. This saves disk space as well as greatly speeding up build. This can enabled on the command line with the following properties:
/p:CreateHardLinksForAdditionalFilesIfPossible=true;CreateHardLinksForCopyAdditionalFilesIfPossible=true;CreateHardLinksForCopyFilesToOutputDirectoryIfPossible=true;CreateHardLinksForCopyLocalIfPossible=true;CreateHardLinksForPublishFilesIfPossible=true
You can also add this to a central import file so that all your projects can also get this benefit.
If you got the dependency structure defined via project references or via solution level dependencies it's safe to turn of "Copy Local" I would even say that it's a best practice todo so since that will let you use MSBuild 3.5 to run your build in parallel (via /maxcpucount) without diffrent processes tripping over each other when trying to copy referenced assemblies.
our "best practise" is to avoid solutions with many projects. We have a directory named "matrix" with current versions of assemblies, and all references are from this directory. If you change some project and you can say "now the change is complete" you can copy the assembly into the "matrix" directory. So all projects that depends on this assembly will have the current(=latest) version.
If you have few projects in solution, the build process is much faster.
You can automate the "copy assembly to matrix directory" step using visual studio macros or with "menu -> tools -> external tools...".
You don't need to change CopyLocal values. All you need to do is predefine a common $(OutputPath) for all projects in the solution and preset $(UseCommonOutputDirectory) to true. See this: http://blogs.msdn.com/b/kirillosenkov/archive/2015/04/04/using-a-common-intermediate-and-output-directory-for-your-solution.aspx
Set CopyLocal=false will reduce build time, but can cause different issues during deployment.
There are many scenarios, when you need to have Copy Local’ left to True, e.g.
- Top-level projects,
- Second-level dependencies,
- DLLs called by reflection
The possible issues described in SO questions
"When should copy-local be set to true and when should it not?",
"Error message 'Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.'"
and aaron-stainback's answer for this question.
My experience with setting CopyLocal=false was NOT successful. See my blog post "Do NOT Change "Copy Local” project references to false, unless understand subsequences."
The time to solve the issues overweight the benefits of setting copyLocal=false.
I tend to build to a common directory (e.g. ..\bin), so I can create small test solutions.
You can try to use a folder where all assemblies that are shared between projects will be copied, then make an DEVPATH environment variable and set
<developmentMode developerInstallation="true" />
in machine.config file on each developer's workstation. The only thing you need to do is to copy any new version in your folder where DEVPATH variable points.
Also divide your solution into few smaller solutions if possible.
This may not be best pratice, but this is how I work.
I noticed that Managed C++ dumps all of its binaries into $(SolutionDir)/'DebugOrRelease'. So I dumped all my C# projects there too. I also turned off the "Copy Local" of all references to projects in the solution. I had noticable build time improvement in my small 10 project solution. This solution is a mixture of C#, managed C++, native C++, C# webservice, and installer projects.
Maybe something is broken, but since this is the only way I work, I do not notice it.
It would be interesting to find out what I am breaking.
Usually, you only need to Copy Local if you want your project using the DLL that is in your Bin vs. what is somewhere else (the GAC, other projects, etc.)
I would tend to agree with the other folks that you should also try, if at all possible, to break up that solution.
You can also use Configuration Manager to make yourself different build configurations within that one solution that will only build given sets of projects.
It would seem odd if all 100 projects relied on one another, so you should be able to either break it up or use Configuration Manager to help yourself out.
You can have your projects references pointing to the debug versions of the dlls. Than on your msbuild script, you can set the /p:Configuration=Release
, thus you will have a release version of your application and all satellite assemblies.
If you want to have a central place to reference a DLL using copy local false will fail without the GAC unless you do this.
http://nbaked.wordpress.com/2010/03/28/gac-alternative/
If the reference is not contained within the GAC, we must set the Copy Local to true so that the application will work, if we are sure that the reference will be preinstalled in the GAC then it can be set to false.
Well, I certainly don't know how the problems works out, but i had contact with a build solution that helped itself in such that all created files where put on an ramdisk with the help of symbolic links.
- c:\solution folder\bin -> ramdisk r:\solution folder\bin\
c:\solution folder\obj -> ramdisk r:\solution folder\obj\
You can also tell additionally the visual studio which temp directory it can use for the build.
Actually that wasn't all what it did. But it really hit my understanding of performance.
100% processor use and a huge project in under 3 Minute with all dependencies.
'Programing' 카테고리의 다른 글
입력 유효성 검사가 실패 할 때 Angularjs가 양식 제출을 방지 (0) | 2020.06.09 |
---|---|
JavaScript에서 많은 수의 과학적 표기법을 피하는 방법은 무엇입니까? (0) | 2020.06.08 |
.py 파일을 구문 분석하고 AST를 읽고 수정 한 다음 수정 된 소스 코드를 다시 작성하십시오. (0) | 2020.06.08 |
"단위 테스트 작성"은 어디에 있습니까? (0) | 2020.06.08 |
여러 열에서 INNER JOIN을 수행하는 방법 (0) | 2020.06.08 |