Currently our .net code is not processor specific, but it depends on libraries (Oracle/ODP.Net) which are. We've found a solution where we edit the csproj file directly, and put the references in to item groups with a Condition clause based on our selected build configuration. We have 32 bit debug/release and 64bit debug/release, and the correct assemblies are references when you build that config.
This works more or less at build time, but it causes all kinds of wackiness in Visual Studio (2008). The end result is that the same assembly shows up four times under references, and three have the yellow exclamation mark. It also generates some 76 warnings that I can't get rid of. We try to aim for 0 warnings because we want to know when new ones show up so this is a bit of a problem.
Is anybody aware of a solution to conditional references that allow it to look like a single reference (which it really is) and doesn't fill up my warnings at build time?
The only thing that leaps to mind is having 4 separate project files... but before you panic about having to maintain 4 files when ever you add a class, you can use another csproj trick here:
<Compile Include="**\*.cs" />
which (IIRC) says "include all cs files at any level in the folder structure".
We found an answer that was a bit different than what we were looking for, but I kindof like it. If you add this to your config file under runtime->AssemblyBinding
<dependentAssembly>
<assemblyIdentity name="Oracle.DataAccess" publicKeyToken="89b483f429c47342" />
<bindingRedirect oldVersion="2.111.6.20" newVersion="2.111.6.0" />
</dependentAssembly>
Then the 64bit and 32bit versions work with the same build. All we have to do is not copy Oracle.DataAccess.dll locally when we deploy and let it pull it from the GAC.
Thanks!
Related
My app uses Nuget packages including Spring.rest and spring.social.twitter. Two library oddities crop up when deployed (deploying is pretty much just installing the .net45 framework and then everything in my release folder onto another machine via WIX).
First oddity is that Nuget has common.logging 2.1.2 installed on my dev box (used by spring.rest), but when spring.rest loads on the prod box, it's looking for 2.0.0. No idea why, as I don't even have 2.0.0 on my dev box, but I can download that version and copy it onto prod, and get past that one.
The second one is the spring.social - nuget decided to use the net20 libs (it's using the net40-client for spring.rest), which works fine on dev, but they (the spring.social libs) decide they need the net20 version of the spring.rest.dll on the prod box. If I change my csproj to use the net40 spring.social libs, I can't compile, because apparently they are not identical even in the same version (link failures on two function calls that work with the net20 dll). If I change spring.rest down to net20, again I can't compile, so that isn't an option.
They aren't strongly named (of course), and they are really the same version and same architecture, so I'm not sure that I could even put them in the GAC if I wanted to. I did try putting both versions in the GAC, but although I got no installer errors from my MSI, it also just did not put the libs with the same names in (I had put almost all of them there for giggles, and the rest worked). The app will run on a dedicated box, so I "own" everything about it and can do all kinds of evil things to it that would be verboten in a "normal" user app, but this little circle of fun has me baffled.
Trying to track down what is running on the dev box (to see if perhaps it is reaching out to some other location to find libs), I tried DepencyWalker (depends), but since everything is delay-loaded, it doesn't do much, even with profiling, since that stops when it hits the entry point. I tried fuslogvw, but common.logging and spring.rest don't even show up? The only nuget packages I see in fuslogvw are spring.social.twitter and newtonsoft.json, and they are both loaded from my bin folder as expected. As I'm out of ideas, I'm turning to the wonderful SO community. What are some good next steps?
Thanks,
Greg
Most likely the .config file in production and on your development machine differs. If the
dev .config file contains a bindingRedirect and your production .config does not this could be the cause for the observed behavior.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="32ab4ba45e0a69a1"
culture="en-us" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
http://msdn.microsoft.com/en-us/library/7wd6ex19(v=vs.71).aspx
I am having little trouble with the GAC/my application. I have been previously using Version 9.0.2.3951 for iAnywhere.Data.AsaClient assembly reference but now I have uninstalled that by going into C:\Windows\Assembly. Currently there is version 9.0.2.3924 in the GAC but when I run application it still looking for version 9.0.2.3951 instead of 3924.
I tried going into Assembly.cs file in my project to see if it has any entry for 3951 but it doesn't even mention that, then I tried to go into App.Config file to see if it has any entry but it doesn't. I have also removed the references from the project and re-added the reference to 3924 file version.
How can I make my app to look for 3924 instead of 3951 file version.
You need to add assemblyBinding information to your configuration, to coerce the proper binding. Something like this:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="" />
<dependentAssembly>
<assemblyIdentity name="iAnywhere.Data.AsaClient" publicKeyToken="f222fc4333e0d400" culture="neutral" />
<bindingRedirect oldVersion="9.0.2.3951-9.0.2.3951" newVersion="9.0.2.3924" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Second option: unload the project in question, and then choose to edit it (via context menu). Look for the project reference, which should look like this:
<Reference Include="iAnywhere.Data.AsaClient, Version=9.0.2.3951, Culture=neutral, PublicKeyToken=f222fc4333e0d400">
You can either delete it and regenerate it manually, or simply modify the XML entry to:
<Reference Include="iAnywhere.Data.AsaClient, Version=9.0.2.3924, Culture=neutral, PublicKeyToken=f222fc4333e0d400">
Cheers!
code4life gave a good solution which would work in most cases. However in my case a different solution worked for me. Just to help everyone else I am adding that solution here with screen shots.
In VS2010 click on Tools and then click on options then Project and Solutions link and then click on Build and Run and then under MSBuild project build output verbosity: select Diagnostic
Under MSBuild project build log file verbosity: select Diagnostic and then press OK
"3." Press F5 to run your project again. This time look at the OUTPUT window to look for Calling assembly section. In my case it showed me following.
The highlighted text in above image shows me that there is a policy file that is still present in the GAC which is redirecting the 3924 version call from my application to 3951. After looking at this I went in to C:\Windows\Assembly** and starting looking for policy file which is causing the redirection to **uninstall it. The screenshot is as follows,
"4." I simply right clicked on the file and uninstalled the version I didn't want and ran my program again and it is fixed.
Search for all your iAnywhere.Data.AsaClient dll's and figure out what dll's are where keep
open cmd.exe
cd\
dir iAnywhere.Data.AsaClient.dll /s
Check all the dll's for the version you are looking for and remove the rest from the app finding those dll's
if you find the dll that is the wrong version in the gac uninstall it from the gac if you need help knowing how to do that let me know and I can tell you
The problem is that your application was compiled against a version of the assembly that it can no longer find. Your options are:
Update your assembly reference and recompile your code
Create a binding redirect as #code4life mentions in his response
First off, I'm using Visual Studio 2010, Measurement Studio 2010 plugin, C# and .NET 4.0.
My application receives data from a USB device and graphs the data using a WaveformPlot() [which is a part of Measurement Studio]. I'm able to run the application fine in debug and release modes and even run it fine directly using the EXE file in the bin folders, on the development computer.
However, I am simply unable to run it in a different computer. I'm using ClickOnce to create an install file and folders, and I copy the publish folder from my development computer to the client computer. I then run the install file, ClickOnce downloads .NET (if it's not already installed) and then opens the application. The application shows my initial dialog asking me to choose a COM port from the many available. Once I do this, my code goes through the InitializeComponent() after which the main form is supposed to show up.
The main form doesn't open up after the initial box. Using Debug.WriteLine statements, I've been able to narrow it down to
this.waveformPlot = new NationalInstruments.UI.WaveformPlot();
It crashes here. It doesn't show me any error message or anything. It works fine on my development computer, just not on other computers. I included the National Instruments DLL files and other National Instruments components are initialized fine. It's just this one that doesn't. The application shows up in the Windows Task Manager for a while and then just vanishes after like 10 seconds.
What should I do?
Update 1
After reading another unrelated question on Stack Overflow, I realized that I could put the Application.run and the form1 mainform = new form1() in a try-catch block.
System.TypeInitializationException: The type initializer for 'NationalInstruments.UI.Internal.WaveformPlotElement' threw and exception. --> System.IO.FileNotFoundException: Could not load file or assembly 'NationalInstruments.Common.Native.dll' or one of its dependencies. The specified module could not be found.
Since I at least know it's an exception now, I'll work on it, try to figure out which DLL is missing and why, and update this question.
Update 2
I checked the application files that are in the publish folder, and it does include the 'NationalInstruments.Common.Native.dll'. I have no idea why it can't load it.
Update 3
I ran Fusion Log Viewer on the client computer and saw that the NationalInstruments.Common.Native.dll was loaded succesfully. But still, the debug exception message shows up as shown in the OP,
Could not load file or assembly 'NationalInstruments.Common.Native.dll' or one of its dependencies"
Screenshot of what the Fuslogvw.exe shows
Fuslog Viewer shows that all the assemblies have been loaded successfully. I checked on the client computer. Although, the National Instruments DLL files have a line which says "GAC lookup unsuccessful" in them while the other assemblies don't.
DebugViewer displays the exception that I print out using Debug.writeLine. This shows that the NationalInstruments.Common.Native.dll or one of its dependencies could not be loaded.
I am very confused.
I tried adding references to the project, using a decompiler to check references, using other install programs (other than ClickOnce) and none of them seem to be getting me anywhere. :(
Update 4
I just found out yesterday that the application was installing and running fine on 64-bit systems. The two computers I tried it on before were 32-bit Windows 7 systems.
I am looking to see if that could help me somehow.
The application was compiled on my 64-bit Windows 7 development laptop. The 'Platform' drop down menu in 'Build' under project properties shows 'Active (x86) and I have 'Any CPU' as the platform target.
After spending lots of time on this issue, I spoke to someone from National Instruments, and he helped me solve the issue I was having. I had previously noticed, by checking the module dependencies of mstudiocommon.2010.msm, that it (mstudiocommon.2010.msm) was looking for the vs100_crt_x86.msm file, but the installer had detected (and added) a vs90_crt_x86.msm (in the 'Detected Dependencies' of the installer project). Right-clicking the installer project and adding the VS100 .msm file manually fixed the issue that I was having.
Below, is a screenshot of the module dependencies that I could see for the mstudiocommon and mstudioui merge modules:
Although, I didn't quite understand why Visual Studio was detecting VS90 instead of VS100, I am happy that I finally fixed this problem, and I'll leave this for another day.
Try Fusion Log Viewer from SDK to identify which library causes the problem.
Without logs and error messages it's too difficult to find what is wrong. You should put a try catch in your code where you try to access the library components waveformPlot and print the error message and the stacktrace. After you can see what is missing.
You can use use either Reflector or JustDecompile to get what references 'NationalInstruments.Common.Native.dll' needs. From the sounds of it though with the word Native in the name of the DLL it maybe a wrapper around someother native Win32 C dll. Do you have those in the same folder? It may also be a wrapper around a COM dll which maybe is not registered?
I think what happen is that someone have installed application on target system that is using only subset of latest NI components. To fix this issue I have added bindingRedirect to app.config. It worked.
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NationalInstruments.Common" publicKeyToken="DC6AD606294FC298" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-13.0.40.190" newVersion="9.1.40.159"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NationalInstruments.Common.Native" publicKeyToken="DC6AD606294FC298" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-13.0.40.190" newVersion="9.1.40.159"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
I had the same problem and when having tons of references in your project it's really hard to find what assembly is actually missing. Especially if you're having this problem on a clients computer without Visual Studio.
After an hour or two messing around with fuslogvw.exe and not being able to get a clear answer I just googled "detect missing assemblies .net application" and found http://www.amberfish.net/
It works like charm, there is a free trial and the developer only asks a very democratic price for it... Very Cool !!
PS. I'm in NO WAY affiliated with amberfish, today is the first time I found out about this tool. the tool this guy built should be in the default toolkit of Windows. Just what i needed.
I have a GlobalAssemblyInfo.cs file in the root of my solution, and I have something like the following entry in it to enable strong naming of my output assemblies.
#pragma warning disable 1699
[assembly : AssemblyKeyFile("..\\keyfile.snk")]
#pragma warning restore 1699
This approach has two drawbacks. Firstly, AssemblyKeyFileAttribute is deprecated, and so to avoid compilation warnings I need the pragma lines you see above. Secondly, I either need to keep all my projects at the same depth relative to the root to use the relative path, or use an absolute path, which dictates a checkout location on other users' machines (and on continuous integration servers/build agents).
Does anyone have a better solution than this, other than going through each project setting strong naming in the project file?
Well, to avoid the path problem you can use [assembly:AssemblyKeyName(...)] instead (although IIRC this is also deprecated); use sn -i to install a named key. Each machine (that does builds) would need this key adding.
Other than that; yes, you'd probably need to edit the project files.
Those attributes for key signing were deprecated for good reason (information leakage), which is another reason to go the project route.
If you have a lot of projects it might be possible to set them via a recorded macro, or even directly manipulating the .csproj files (ensure they are unloaded from VS first).
Richard makes a good point about information leakage - I've now found posts from Microsoft's .NET team where they describe this. So I've gone for his suggestion and come up with the following NAnt target:
<target name="strongName" description="Strong names the output DLLs">
<foreach item="File" property="filename">
<in>
<items>
<include name="**/*.csproj"></include>
<exclude name="**/*.Test.csproj"></include>
</items>
</in>
<do>
<echo message="${filename}" />
<xmlpoke file="${filename}" xpath="/m:Project/m:PropertyGroup/m:SignAssembly" value="false">
<namespaces>
<namespace prefix="m" uri="http://schemas.microsoft.com/developer/msbuild/2003" />
</namespaces>
</xmlpoke>
<xmlpoke file="${filename}" xpath="/m:Project/m:PropertyGroup/m:AssemblyOriginatorKeyFile" value="..\keyfile.snk">
<namespaces>
<namespace prefix="m" uri="http://schemas.microsoft.com/developer/msbuild/2003" />
</namespaces>
</xmlpoke>
</do>
</foreach>
</target>
The <namespaces> element is necessary for the XPath to be resolved in the csproj file - note that this is for VS2008, and something slightly different may be needed in VS2005.
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?
In a previous project I worked with one big solution with project references and bumped into a performance problem as well. The solution was three fold:
Always set the Copy Local property to false and enforce this via a custom msbuild step
Set the output directory for each project to the same directory (preferably relative to $(SolutionDir)
The default cs targets that get shipped with the framework calculate the set of references to be copied to the output directory of the project currently being built. Since this requires calculating a transitive closure under the 'References' relation this can become VERY costly. My workaround for this was to redefine the GetCopyToOutputDirectoryItems target in a common targets file (eg. Common.targets ) that's imported in every project after the import of the Microsoft.CSharp.targets. Resulting in every project file to look like the following:
<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>
This reduced our build time at a given time from a couple of hours (mostly due to memory constraints), to a couple of minutes.
The redefined GetCopyToOutputDirectoryItems can be created by copying the lines 2,438–2,450 and 2,474–2,524 from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets into Common.targets.
For completeness the resulting target definition then becomes:
<!-- 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>
With this workaround in place I found it workable to have as much as > 120 projects in one solution, this has the main benefit that the build order of the projects can still be determined by VS instead of doing that by hand by splitting up your solution.
I'll suggest you to read Patric Smacchia's articles on that subject :
Partitioning Your Code Base Through .NET Assemblies and Visual Studio Projects --> Should every Visual Studio project really be in its own assembly? And what does 'Copy Local=True' really mean?
Lessons learned from the NUnit code base --> The VisualStudio Project Reference + Copy Local true option is evil!)
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.