How to break a large VS solution into smaller solutions - visual-studio-2010

We have a VS2008 solution file that contains around 80 projects (yes i know it sucks).
The projects are arranged in various folders, and some may depend on other 3rd party DLLs in a top level "Libs" folder.
We'd like to refactor this into several smaller .sln files, each containing a reasonable number of projects.
The problem is, when moving the project files around, the relative paths stored within them will break and so we will have to do lots of manual "patch ups" to fix that.
Is there any tool or any proven technique for doing something like this ?

We did something similar, and to fix the references, we wrote a quick utility that would parse the .csproj or .vbproj files (which are basically xml files) and fix the affected paths based on where the .proj file itself was located after the refactoring. this was better than manually changing the project xml or adding removing references to avoid human errors.
Once you know where the proj file is and where common files (or other files) will be, you modify the Reference node in the project file with the relative path. So for e.g., you might have to change the original
<Reference Include="NHibernate">
<HintPath>..\..\ServicesShared\Library\NHibernate.dll</HintPath>
</Reference>
in the .proj file to
<Reference Include="NHibernate">
<HintPath>..\Common\ServicesShared\Library\NHibernate.dll</HintPath>
</Reference>
if that's where the NHibernate.dll lives now.
Hopefully this will work for you guys.

Related

Can Visual Studio use an .editorconfig not in the directory hierarchy?

We have a very large number of solutions spread across a wide number of repositories which do not always share a directory hierarchy in a way that makes it easy for us to update an .editorconfig such that it applies to all projects/solutions in the organization. We currently apply all of our code analysis configuration via an internal NuGet package and I was hoping we could include our organization-wide .editorconfig settings in this way as well?
I tried a quick experiment adding the following to a project to see if linked files would be honored (since we could simply add this to a props file we already have in the NuGet package), but it does not appear to be honored currently.
<ItemGroup>
<None Include="C:\SomeAlternatePath\ECTest\.editorconfig" Link=".editorconfig" />
</ItemGroup>
Is there some other MSBuild property or mechanism we could use to better facilitate this without literally writing a duplicate file to every solution/project/repo?
Is there some other MSBuild property or mechanism we could use to
better facilitate this without literally writing a duplicate file to
every solution/project/repo?
I'm afraid the answer is negative. Cause the .editorconfig file have nothing to do with msbuild or xx.csproj. Only file hierarchy can affect the behavior how the config file works. More details please check this document.
Some tests:
When I right-click project=>add .editorconfig to add this file in current project, there's one line added to the xx.csproj: <None Include=".editorconfig"/>.
If we set the indent_size = 32, it works for current project. Now we can right-click that file=>Exclude from Project to remove that file from current project system. (This action will remove the <None Include=".editorconfig"/> in xx.csproj, but the file is still in the same folder where xx.csproj exists)
Now reload the project, the settings(indent_size=32) still works. So it's obvious if we place this file in project directory, then it will take effect, no matter if we have definitions about it in project file(xx.csproj).
Suggestions:
According to your description, all your projects use the same .editorconfig file. Since this file's working scope is affected by file hierarchy, you can reduce some meaningless work by:
1.Place that file in Solution folder, it will work for all projects under that solution folder
2.Place that file in repos(C:\Users\xxx\source\repos) folder, it will work for all solutions and projects under this folder.
3.So if most of your solutions are under path C:\somepath, place that file here, all projects under that path will benefit from that. And about precedence in file hierarchy please see this one.
Hope all above makes some help :)

Change binding root in VS2010 using Perforce source control

I have read this post thoroughly: How does Visual Studio's source control integration work with Perforce? and found it very informative. However, I have a specific issue that is blocking my use of Perforce in VS.
For the most part, I have no complaints about the plug-in (I'm still using the P4VSCC plug-in because the new plug-in requires conversion by the entire team which can't happen at this time). Once I understood the idiosyncracies, I've had only one problem working with the plug-in.
Our solutions contains many projects that are built into a single deployment package. As such, each assembly is versioned the same. To accomodate this, and other common aspects, we have defined a common "SharedVersionInfo.cs" file which contains the AssemblyVersion and AssemblyFileVersion attributes typically found in the AssemblyInfo.cs file. This file is stored in an Assets folder beneath the solution folder and added to each project's Properties folder as a linked file. This works great from a version management perspective as we only have to change the version in one place and all assemblies are updated. However, Perforce has a problem with this when a new developer first opens the solution or when a new project is added. The only remedy we have currently is to remove all of the linked files (there are 3 per project in this solution), bind the project to source control, then re-add the linked files.
This isn't such a big deal when we add a new project but the solution contains 80 projects (and counting), so this isn't a viable remedy for a new developer!
My understanding is that the problem has to do with where VS thinks the binding root for each project is. After some research, I was led to find where the MSSCCPRJ.SCC files are for the projects. I found there are numerous SCC files scattered throughout the solution structure. So...
First question: Why are there multiple MSSCCPRJ.SCC files in my solution structure?
We also have several shared/common projects that we use in our solutions. This leads to the following folder structure:
/Source
/CommonTools
/ProjectA
ProjectA.csproj
/ProjectB
ProjectB.csproj
/MySolution
/Assets
SharedVersionInfo.cs
/Project1
Project1.csproj
/Project2
Project2.csproj
:
/ProjectZ
ProjectZ.csproj
MySolution.sln
Where both ProjectA and ProjectB are part of MySolution.sln
Second Question: How can I setup the bindings so the /Source folder is considered the root? This would ensure that all projects included in the solution are under the same binding root. Perforce considers this folder to be the root, how do I get VS and the plug-in to do the same?
Since no one else has offered up a solution, I thought I'd follow-up with my own findings for anyone else that comes across the thread.
First, I still have no idea why Visual Studio creates multiple MSSCCPRJ.SCC files but these are the key to establishing the "binding root" for a solution. It is critical that this file exist at the highest level necessary so that ALL of the projects in the solution are in sub-folders relative to the location of this file. In my example above, the MSSCCPRJ.SCC needed to be located in the /Source folder. Having it in the /MySolution folder caused the original problem when adding projects from /CommonTools into the solution.
That said, resolving the issue was no easy task. I had to manually edit the .sln and all of the .csproj files in Notepad. What I found was that some of the .csproj files had the following elements identifying the source control settings:
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
I don't know what SAK stands for, but my understanding is this tells Visual Studio to use the binding information contained in the .sln file.
I had to change these to:
<SccProjectName>Perforce Project</SccProjectName>
<SccLocalPath>..\..</SccLocalPath>
<SccAuxPath />
<SccProvider>MSSCCI:Perforce SCM</SccProvider>
where the SccLocalPath value is the relative path from the .csproj file to the MSSCCPRJ.SCC file.
I also had to change the SccLocalPathX and SccProjectFilePathRelativizedFromConnectionX statements for each project in the .sln file. The SccLocalPathX value should be the relative path from the .sln file to the MSSCCPRJ.SCC file - a dot (.) if in the same folder. SccProjectFilePathRelativizedFromConnectionX should be the relative path from the binding root to the .csproj file.
I wish I could say that having that in place, I never had to repeat these steps. Unfortunately, I still have to go in and make corrections every time I added a new project to the solution. Visual Studio still wants to use SAK for the elements in the .csproj file and sometimes the values in the .sln file aren't quite right.
But, at least I know what to look for and what needs to be done to achieve my goals. If anyone else has a better solution or a way to configure VS and/or Perforce so these settings are created correctly up-front, I'll gladly give credit.
Hope that helps...

What is the best way to determine how a missing reference in a Visual Studio project can be found?

I'm using Visual Studio 2008 and I'm trying to get a project that I didn't create to build.
In the references folder of this project there are four DLLs with the exclamation icon whose Path property is empty. The in the .csproj file points to an output directory which I don't think is the initial location of these DLLs.
I believe that I have the most recent copies of the DLLs in question (they are used in several related projects). I could place them in this project's directory but I want to determine their original location so I don't have to modify the project and solution files (which are used by other developers in other remote locations).
I would ask the original developers but they're located in a time zone where it's 1 a.m. so I though someone here could help me out.
Thanks in advance.
Open the .csproj file in a text editor an locate the missing references.
If the entry is like this:
<Reference Include="AssemblyName"/>
then it's located in the default folder for that application and somewhere on the search path.
If the entry is like this:
<Reference Include="AssemblyName">
<SpecificVersion>False</SpecificVersion>
<HintPath>Path\AssemblyName.dll</HintPath>
</Reference>
Then the file should be put in the folder referred to by Path.

Visual Studio Linked Files Directory Structure

I have two versions of a project. One for Silverlight and one for .NET. The SL project has the vast majority of the code base in it. I want to globally add all files from the SL project into the .NET version as linked files. I've managed to do so successfully like this in the csproj file for the .NET version:
<Compile Include="..\MyProj.Common.SL\**\*.cs" Exclude="..\MyProj.Common.SL\Properties\**">
Unfortunately, this adds all the files right to the root of my project... so I end up with a long unreadable list of linked files in the .NET project. I really really really don't want to have to maintain an entire duplicate directory structure by hand and deal with directory name changes and file name changes and whatnot.
So, is there any way to have Visual Studio preserve the directory structure when adding linked files in the wildcard manner above? Or is there at least a way of making it group all the linked files together under a directory in the .NET project like MyProj.Common.SL.Links?
The very closest I've come is to set the <Visible>false</Visible> under the <Compile> tag, which effectively removes the long unreadable list of 300+ files....but unfortunately this screws up Resharper, which no longer sees those files as valid and it goes crazy on all the projects that reference the .NET project. If I could figure out a way of making Resharper not get all messed up, that would be an acceptable solution too...
Any suggestions?
Thanks.
I think I found a way of getting this to work:
<Compile Include="..\MyProj.Common.SL\**\*.cs" Exclude="..\MyProj.Common.SL\Properties\**">
<Link>MyProj.Common.SL.LinkedFiles\MyProj.Common.SL.LinkedFiles</Link>
</Compile>
It will create a MyProj.Common.SL.LinkedFiles folder and group all the linked files under that folder.
I think I would do this:
Copy the existing project's <Compile> items, which presumably have e.g. Include="foo.cs" and Include="Folder\bar.cs"
Paste them into the new project
Search and replace <Compile Include="(.*?)" /> with <Compile Include="..\Other\$1" ><Link>$1</Link></Compile>
I don't know if I got the regular expression search and replace syntax exactly right, but the point is, you already have a good project, you should be able to cut, paste, regex-replace it to get the same set of files, only referenced from a different folder, and with the same directory structure.
You'll still have two .csproj's to maintain at this point, but this is also easily fixed. Now take this new list of compile items, and put it in a file named e.g. "Common.csproj" that just contains the property group with those compile items, and then have both projects <Import Include="..\Common.csproj" /> and not include any Compile items of their own.
Basically, a little manual labor to refactor the .csproj file for sharing once, and then I think you'll be set. I am not sure if this is the 'easiest' way to unblock you, but I think this sounds approximately like what you may want for an 'ideal' structure.

Set "Copy Local" to False by default?

Can I set the default-option of "Copy Local" in Visual Studio to False? In most times, when I add a dll as dependency of a project, I want the Copy Local property set to False. Per default, it is True. Is there a way to change the default behaviour of Visual Studio? (2008)
No - Visual Studio uses an internal set of rules to determine what to set Copy Local to.
From MSDN:
If the reference is another project, called a project-to-project reference, then the value is true.
If the assembly is found in the global assembly cache, the value is false.
As a special case, the value for the mscorlib.dll reference is false.
If the assembly is found in the Framework SDK folder, then the value is false.
Otherwise, the value is true.
Actually, you can. You need a couple things:
Create .targets file that makes copylocal (<Private> tag, to be precise) false by default.
Import the target in .csproj files. You can add it in the very last line, before closing </Project> tag, it'll look like <Import Project="..\Build\yourtarget.targets" />.
Now each project with this target has copylocal disabled by default.
The drawback is that you need to modify each and every csproj file, including new ones. You can work around the new project issue by modifying the VS project template. Instead of Class.cs described in the blog article, you need to modify Class.vstemplate (in the same zip file).
With that approach, there's one more problem - the path itself. If you use hardcoded relative path in newly-generated csproj files, they may be wrong (unless you have flat project structure).
You can:
Make VS generate correct relative path. Not sure how to do that and if that's even possible.
Ignore it and change the path manually for each new csproj (depending on the number of new project you have, while not ideal, that may be tolerable).
Use the environment variable instead of relative path. In that case every developer will need the same variable set.
There must be better solution for that, but haven't found it yet.
Starting with msbuild v 15 you can copy a single file called Directory.Build.props in the root folder that contains your source:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
</Project>
Nothing more to do! This works well with Visual Studio 2017 and also the vNext Build. You might have to close Visual Studio and than open your solution again to take the file effect.
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets
We don't use a .targets files (as suggested in the answer by ya23), so we just edit the .csproj project file manually in a text editor and add the <Private> element to the reference, like this:
<Reference Include="[...]">
<Private>False</Private>
[...]
</Reference>
The value of the <Private> element matches the value of the "Copy local" property. For instance, if <Private> is set to False, then "Copy local" is also false..
Bumping this because it seems there's now a nuget package allowing exactly this...
https://nuget.org/packages/CopyLocalFalse
Haven't tried yet, just hoping it helps.
Regarding the solution posted by #herzbube, if you want to turn off "Copy Local" for all (or most) of the references in your .csproj file, you don't need to set <Private>False</Private> individually on each Reference, you can just put the following directly in the .csproj:
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
</ItemDefinitionGroup>
This doesn't affect projects referenced with <ProjectReference>, but you can do the same thing--either instead or as well--for those:
<ItemDefinitionGroup>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
If you want both of these, you can merge them into a single group:
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
Make sure you put these overrides prior to the first actual <Reference … > or <ProjectReference … > that you want to affect because these blocks will only apply to those references that appear below them. Then, if there are a few that you do actually want to be locally copied, you can just override those back individually (i.e., within the individual tag itself), this time using True.
For more advanced cases you can switch the overriding value back and forth between True and False multiple times in the same .csproj file. Another advanced technique would be to strategically place some of your references below these blocks, and others above, so the latter won't be affected.
All of this should make the XML in your .csproj much cleaner and easier to read. But there's even more good news, so read on...
As for selecting which projects should be be marked <Private>False</Private> this will usually depend on the specific situation, but there is something fundamental everyone can and should do for starters. It's a step so basic, simple and effective and it delivers such huge MSBuild reliability improvements1. and build-time speedup--and with little downside--that every large solution that uses the default (i.e. local per-project) C# output locations should almost always make this adjustment:
In any and every Visual Studio solution which builds multiple C# class libraries with any non-trivial number of <ProjectReference> inter-dependencies, and which culminates in building one more applications (i.e. executables):
Near the top the .csproj for every class library, insert the <ProjectReference> block shown above.
REASON: There is no need for any .dll to gather any of the libraries it references into a sub-directory of its own, since no executable is ever run from that location. Such rampant copying is useless busywork and may be unnecessarily slowing down your build, possibly quite dramatically.
On the other hand, do not modify the .csproj for any of your solution's applications.
REASON: Executables need to have all the privately-built libraries they need in their respective sub-directories, but the build for each app alone should be responsible for individually gathering each dependency, directly from its respective sub-directory, into the app's sub-directory.
This works perfectly because the .csproj for a class library may reference multiple other class libraries, but the .csproj for an executable usually never references another executable. Thus, for every locally-built library, the only .dll in its bin folder will be itself, whereas every locally-built application will contain the full set of locally-built libraries it references.
Conveniently, nothing changes for the referenced libraries that are not built by your solution, since these usually use <Reference> instead of <ProjectReference>, and we didn't modify the former tag at all. But do note the assumption just mentioned; if it is violated by some of your projects, you may need to make some adjustments.
[1.] Reliability improvements could be related to file collisions that may occur when gathering the same library from multiple disjoint paths in a dependency graph, especially in concurrent builds.

Resources