Conditional AppendTargetFrameworkToOutputPath based on TargetFramework - c#

I'm trying to set AppendTargetFrameworkToOutputPath for a large number of projects, but depending on the TargetFramework.
So basically I have
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
and have to figure out where to put it so I don't have to duplicate code in all projects.
What I've figured out so far:
Directory.Build.props: If I don't specify a condition (Condition="'$(TargetFramework)'=='net48'") it works reliably. If I limit it to only a specific TargetFramework then it only works if the project is multi-targeting. I.e. <TargetFrameworks>net48;net5.0</TargetFrameworks> works fine, but for <TargetFramework>net48</TargetFramework> the condition evaluates to false - not too surprising since the Build.props file is evaluated before the TargetFramework is set in the csproj (also not too surprising that multi-targeting works, given how this is implemented).
Directory.Build.targets: While this would solve the problem with the TargetFramework not being set in the csproj, it seems like this is too late in the evaluation process and the output path is already set.
Is there any way around this apart from requiring all projects to use <TargetFrameworks>? (which would be an incredibly fragile solution).
Note that this is NOT a duplicate, since the user is importing the common file manually in the csproj which gives you much finer control over when the definitions are imported.

You can use the <BeforeTargetFrameworkInferenceTargets> property to specify the path to a target file that is executed at the right time during the build.

Related

How can I ignore obsolete type errors when running Live Unit Testing in VS 2022 enterprise?

I have a solution with about 58 projects, including 10 test projects. There's only one I'm interested in running, that relates to my team's work.
I can run the test project manually from the Test Explorer, and it builds with all tests passing fine.
However, when I set up Live Unit Testing, the build fails due to obsolete types being used in some another projects. I don't want to replace all the type references since that code is managed by other teams.
Here are my VS2022 LUT settings.
I have only added the test project I'm interested in to the LUT playlist. I also tried replacing some of the obsolete types (I only had 3 errors initially) but the compiler then found dozens more...
I increased the logging level on LUT to verbose, but that didn't give anything useful. There aren't any other settings I can see for LUT to ignore certain projects or errors.
I have looked up the MS documentation on the [obsolete] attribute, but I'm not sure why I get the error on LUT and not on regular test or debug builds.
The .csproj of a project that fails has this line: <TreatWarningsAsErrors>True</TreatWarningsAsErrors> and setting it to False allows LUT to build it. But I don't want to have to change dozens of .csprojs just for running some tests.
Is there any other way to mark errors as warnings for live unit testing?
Instead of setting <TreatWarningsAsErrors>false</TreatWarningsAsErrors> you can add <NoWarn>CS0618</NoWarn> to the project files. That's probably going to help everybody, because others will have the same problem as well.
You can also add a section such as the following to your Directory.Build.props file (should be in the root of your project structure):
<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('UnitTest')) AND '$(MSBuildProjectExtension)' == '.csproj'">
<NoWarn>$(NoWarn);CS0618</NoWarn>
</PropertyGroup>
This will disable the warning for all projects whose name ends with "UnitTest".

MSBuild on NET 5 Core projects produces different bin/x64 and bin/Debug folder structures

I am having an awful time understanding how MSBuild works with NET Core project files (csproj) on Windows 11. I have 71 NET Core C# project files (executables, libraries, test projects). They all compile and run properly under Visual Studio or with msbuild batch jobs that build and PUBLISH to a publish folder on the command line.
BUILDING LOCALLY PRODUCES DIFFERENT BIN/* FOLDERS
But when I run MSBuild (with the same command-line arguments) on the 71 projects to build locally (not publish locally), some of the projects produce different output folders.
Some of them create bin/Debug/* folders, some create bin/x64/Debug/ folders, and some projects produce both types of folders. The “*” part is framework/runtime, which is net5.0-windows/win-x64 on my system.
Here is what my typical NET 5.0 project file looks like:
<PropertyGroup>
<Platforms>AnyCPU</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net5.0-windows7.0</TargetFramework>
<SelfContained>false</SelfContained>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
// The IsPublishable setting makes no difference.
// The build operation is not a publishable operation.
</PropertyGroup>
Here is what my MSBuild command looks like for all 71 projects
MSBuild /t:Restore;Build /p:Configuration=Debug /p:Platform=x64 /p:RuntimeIdentifier=win-x64 csproj-file-pathname
I have done many experiments, including completely deleting the obj and bin trees before running the MSBuild command lines, but nothing works. I cannot see any discernible relationships among project files, types of projects (lib or exe or app), and MSBuild command line arguments that would explain the different local build output folders.
Does anyone know what would make some projects create different output folder structures (and contents) if the project files are the same and the MSBuild arguments are the same?
Does anyone know for sure exactly what bin/* output folders should be produced by the msbuild arguments shown above? I have spent hours fighting the issue without success. Thank you.
MSBuild Structured Log Viewer is going to be your friend here. Whenever I need to know exactly what MSBuild is doing, I break this tool out.
I made a couple of library projects to show you what's going on
Here's the solution configuration for x64
I build the solution with the same properties you are using.
I'm pretty familiar with MSBuild and I know what property to look for, for the final copy. So I search for $property OutDir. This means I'm looking for an MSBuild property with the name that contains OutDir.
As you can see, we're pretty much getting the same situation you described. Some have bin\x64\Debug while some are just bin\Debug. They all have their target framework appended but they also have their runtime identifier appended.
If we want to see the project exactly as MSBuild sees it. We can expand the evaluation folder and click the "Preprocess" from the context menu. This will be a monster xml. This is actually your csproj in its full expanded glory after all the .props and .targets imported from the .NET SDK and from your repo.
You can get this file from the cli as well with MSBuild -preprocess[:file] option.
There's a lot of stuff going on but really we're only looking for stuff that directs the output. I know OutputPath is used in OutDir so I search for OutputPath and jump around. I find that the special .targets file that controls default outputs is called Microsoft.NET.DefaultOutputPaths.targets. Here's the relevant portion:
<Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
<Platform Condition="'$(Platform)'==''">AnyCPU</Platform>
<PlatformName Condition="'$(PlatformName)' == ''">$(Platform)</PlatformName>
<BaseOutputPath Condition="'$(BaseOutputPath)' == ''">bin\</BaseOutputPath>
<BaseOutputPath Condition="!HasTrailingSlash('$(BaseOutputPath)')">$(BaseOutputPath)\</BaseOutputPath>
<OutputPath Condition="'$(OutputPath)' == '' and '$(PlatformName)' == 'AnyCPU'">$(BaseOutputPath)$(Configuration)\</OutputPath>
<OutputPath Condition="'$(OutputPath)' == '' and '$(PlatformName)' != 'AnyCPU'">$(BaseOutputPath)$(PlatformName)\$(Configuration)\</OutputPath>
<OutputPath Condition="!HasTrailingSlash('$(OutputPath)')">$(OutputPath)\</OutputPath>
The first part of your question can finally be answered. For projects that build under the AnyCPU solution (even when you specify x64), you don't get the $(Platform) in the output. You can see that in my example: only net6lib has x64 in the OutDir.
So what about the runtime identifier, I don't see it in this .targets file. It's actually expanded on a little later in a different file: Microsoft.NET.RuntimeIdentifierInference.targets.
Relevant portion with comment
<!--
Append $(RuntimeIdentifier) directory to output and intermediate paths to prevent bin clashes between
targets.
But do not append the implicit default runtime identifier for .NET Framework apps as that would
append a RID the user never mentioned in the path and do so even in the AnyCPU case.
-->
<PropertyGroup Condition="'$(AppendRuntimeIdentifierToOutputPath)' == 'true' and '$(RuntimeIdentifier)' != '' and '$(_UsingDefaultRuntimeIdentifier)' != 'true'">
<IntermediateOutputPath>$(IntermediateOutputPath)$(RuntimeIdentifier)\</IntermediateOutputPath>
<OutputPath>$(OutputPath)$(RuntimeIdentifier)\</OutputPath>
</PropertyGroup>
And there's the last part of your output portion. $(TargetFramework) gets inserted in there as well between the previous target I mentioned and this target but you already knew that.
There is no madness here at all. MSBuild is following exactly what it's been told to do.
Hopefully this helps some!

Loading a .NET project with items included as wildcards

I've worked out that in my C#/F# code I can load information about any .NET project using
collection.LoadProject(path_to_my_proj_file)
where collection is of type ProjectCollection. I can then get the access to all the properties and items defined in the project and all of its dependencies. As an example I can get an access to all the files included via Compile in the following way
project.GetItems "Compile"
Let's assume I want to define a custom ItemGroup in my fsproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ItemGroup>
<MyGroup Include="Test.txt" />
<MyGroup Include="TestFiles\**\*" />
</ItemGroup>
</Project>
Some comments:
MyGroup is my custom collection identifier. I've found out things like this are allowed
The "TestFiles" folder contains 4 files all together that exist in some subfolders, but they all match the pattern "TestFiles\**\*"
When I load the project using the method I had mentioned at the beginning and run
project.GetItems "MyGroup"
I get only one item, that is "Test.txt". The other files don't get discovered, unless I define them explicitly (i.e. without the wildcard) in the fsproj.
Is there a way for me to discover the files included using wildcards as well ? I'm even happy to get them in the unresolved form. So getting "TestFiles\**\*" instead of specific files that match the pattern is fine as well.
Found the solution.
First, let me say that the problem was on my side. In fact in general when you try to load a project, it DOES load also all the files. Even those defined with a wildcard, as long as the ofc match the pattern.
And here's the deal: in my application I don't use the plain collection.LoadProject, but rather a more sophisticated library. And yes - the library does some magic stuff that caused the problem.
In this situation the problem was simple: all the *.[cs|fs]proj files were copied to C:\temp\<random_folder_name>. Only *.[cs|fs]proj files - nothing less, nothing more. As a result, when the project loader attempted to evaluate the solution, there were simply no files to pattern-match, resulting in zero elements.
The lesson here is: writing a proper MCVE is important - would save me (and probably you as well) some time. Apologies for problems, guys!

Multidex Xamarin Maps android issues

After adding the Xamarin maps nuget package to my app, I was getting the Java.exe has exited with code 2 error. I enabled multi-dex in the android options and I am now getting the following error:
Expecting class path separator ';' before '\Android\android-sdk\build-tools\26.0.0-preview\lib\shrinkedAndroid.jar'
I re-installed the Android SDK and made sure that I had the latest version, however I am still getting the error. I've spent a good while googling this issue and trying different solutions, however I have not found one to work
There are some issues with multi-dex at the current build. Updating to Visual Studio 2017 13.2 may fix the issue you mentioned. If it doesn´t, you can try two things:
To fix the class path separator error, put your Android SDK in a path without spaces. i.e: C:\android-sdk\ and change the path in Tools>Options>Xamarin>Android SDK Location.
If you don´t want to move the SDK you can create a link with a command line:
mklink /D "C:\android-sdk" "C:\Program Files (x86)\Android\android-sdk"
If even with Multi-dex enabled, it doesn´t run:
When you tick "Enable Multi-Dex" option in the Android project properties, you´ll see the change reflected in the csproj as you would expect
<AndroidEnableMultipleDex>true</AndroidEnableMultipleDex>
But if you take a closer look, there is another similar xml node that keeps its value to false:
<AndroidEnableMultiDex>false</AndroidEnableMultiDex>
Notice the difference between "MultiDex" and "MultipleDex"
Changing the second one to true will make the trick.
I have same problem while adding map with nuget package to my app.
just change the latest proguard.jar file from link
and replce from your android sdk-> tools->proguard->lib->progaurd.jar
and
right click on your android project->select option->build->android build-> enable proguard and enable multidex as well go to Advance ->java heap size put 1G.
I had this same issue running Visual Studio 2017 v 15.2.
As has been mentioned before, this results when you've added enough nugets and packages to your solution that you exceed the 65k method limit in a standard dex file, requiring you to enable multi-dex.
Like others have noted, enabling multi-dex doesn't always fix the problem, because it turns on a flag called "AndroidEnableMutipleDex" in the .csproj file. So, as suggested above, I added the node
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
but, since I wasn't too familiar with the format of the .csproj file, I forgot to add it under both
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
...
<AndroidEnableMultipleDex>true</AndroidEnableMultipleDex>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
</PropertyGroup>
and
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
...
<AndroidEnableMultipleDex>true</AndroidEnableMultipleDex>
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
</PropertyGroup>
Yes, this a a basic mistake, but it can be easy to make if you're not used to manually editing your .csproj file, especially if you're just wading into this for the first time and not even sure where exactly the bug is coming from.
Also, as others have noted, you will need to move the location of your Android sdk to a path with no spaces.

What is the best practice for "Copy Local" and with project references?

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.

Categories

Resources