views:

5055

answers:

12

I have a large c# solution file (~100 projects), and I am trying to improve build times. I think that "Copy Local" is wasteful in many cases for us, but I am wondering about best practices.

In our .sln, we have application A depending on assembly B which depends on assembly C. In our case, there are dozens of "B" and a handful of "C". Since these are all included in the .sln, we're using project references. All assemblies currently build into $(SolutionDir)/Debug (or Release).

By default, Visual Studio marks these project references as "Copy Local", which results in every "C" being copied into $(SolutionDir)/Debug once for every "B" that builds. This seems wasteful. What can go wrong if I just turn "Copy Local" off? What do other people with large systems do?

FOLLOWUP:

Lots of responses suggest breaking up the build into smaller .sln files... In the example above, I would build the foundation classes "C" first, followed by the bulk of the modules "B", and then a few applications, "A". In this model, I need to have non-project references to C from B. The problem I run into there is that "Debug" or "Release" gets baked into the hint path and I wind up building my Release builds of "B" against debug builds of "C".

For those of you that split the build up into multiple .sln files, how do you manage this problem?

+4  A: 

our "best practise" is avoid solutions with many projects. We have a directory named "matrix" with current version of assemblies, and all references are from this directory. If you change some project and you can say "now is the change complete" you copy the assembly into the "matrix" directory. So all project, which depends on this assembly has current(=latest) version.

If you have few projects in solution, the build process is much faster.

The step "copy assembly to matrix directory" you can automatize with visual studio macros or with "menu -> tools -> external tools...".

TcKs
+1  A: 

I tend to build to a common directory (e.g. ..\bin), so I can create small test solutions.

kenny
+4  A: 

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.

Bruno Shine
Why this post got negative point?
TcKs
Bruno, please see my followup question above - if we break into smaller .sln files, how do you manage the Debug vs. Release aspect that is then baked into the hint path of my references?
Dave Moore
I agree with this point, the solution I'm working with has ~100 projects, only a handful of which have more than 3 classes, build times are shocking, and as a result my predecessors split the solution into 3 which completely breaks 'find all references' and refactoring. The whole thing could fit in a handful of projects which would build in seconds!
Jon M
Dave,Good question. Where I work, we have build scripts that do things like build dependencies for a given solution and put the binaries somewhere where the solution-in-question can get them. These scripts are parametrized for both debug and release builds. The downside is extra time up front to build said scripts, but they can be reused across apps. This solution has worked well by my standards.
apollodude217
A: 

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.

CubanX
+1  A: 

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.

Aleksandar
Interesting... How would this work with debug vs. release builds ?
Dave Moore
I'm not sure whether any suitable solution exists for loading debug/release assemblies through a DEVPATH, it's intended to be used for shared assemblies only, I wouldn't recommend it for making regular builds.Also be aware that assembly version and GAC are overridden when using this technique.
Aleksandar
+1  A: 

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.

jyoung
A: 

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.

Bruno Shine
Bruno - yes, this works with Project References, which is one of the reasons we wound up with a 100 project solution in the first place. It does not work on references where I browse to the pre-built Debug releases - I wind up with a Release app built against Debug assemblies, which is a problem
Dave Moore
Edit your project file in a text editor and use $(Configuration) in your HintPath, e.g.<HintPath>..\output\$(Configuration)\test.dll</HintPath>.
chaiwalla
+3  A: 

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.

Torbjörn Gyllebring
+10  A: 
Bas Bossink
Can you describe the changes you made and why? My eyeballs are too tired after a long day of coding to try to reverse engineer it myself :)
Charlie Flowers
ZXX
+4  A: 

I'll suggest you to read Patric Smacchia article on that subject :

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.

madgnome
+1  A: 

You are correct CopyLocal will absolutely kill your build times. If you have a large source tree then you should disable CopyLocal. Unfortunatley it not as easy as it should be to disable it cleanly. I have answered this exact question about disabling copy local 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.

Copy local was implemented really to support local debugging. When you perpare your application for package 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.

Sayed Ibrahim Hashimi
A: 

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/