2. Introduction – SolutionInfo.cs
3. Introduction – Partitioned single solution
4. Partitioned single solution and SolutionInfo.cs – The problem
5. Partitioned single solution and SolutionInfo.cs – The Solution
6. Subversion Issue 695 and Release 1.5
7.1. Add a file as link
7.2. Set Build Action to None
In this article I will talk about possible issues when creating a SolutionInfo.cs file
while using the Partitioned single solution development model.
While writing this article I was using the following software:
Visual Studio 2005
Having multiple projects in a solution means having multiple assemblies, and having multiple assemblies could mean that you want to deploy them as separate assemblies.
Each project generated by Visual Studio 2005 contains, by default, an AssemblyInfo.cs file in which you can write assembly attributes.
If you open AssemblyInfo.cs you will find a set of assembly attributes with default values.
The values for some of these attributes could vary from project to project but others (such as AssemblyVersion) should be application wide.
For this purpose a common choice is the SolutionInfo.cs pattern as illustrated by Juval Lowy (you can find his book: Programming .NET Components, 2nd Edition at Amazon).
It consists in grouping all the attributes common to all projects (solution wide attributes) in a separate file named SolutionInfo.cs that will be included as a solution item.
Each project will contain, under the Properties directory, an AssemblyInfo.cs file and a link to the shared SolutionInfo.cs file (see note: Add file as link).
Adding that file as a link allows you to modify always the same file, no matter which link you open so having all the projects’ properties updated at once.
This approach is very useful for incrementing Version Number.
Partitioned Single Solution:
As shown in the article above, when you have a big application and want to allow developers to work on smaller portions of the appliction, you can consider using the ‘partitioned single solution‘ development model.
It consists in creating a top solution containing all the projects in the application and then introducing separate solution files for subsets of related projects.
Each solution file will contain all the projects a develper needs to work on and all the referenced projects.
In this way it is possible to checkout only the projects actually needed on each developer’s workstation, thus obtaining a lighter build process during the development stage.
This approach requires additional work and time to keep all the solution files aligned but it provides a safer way to share work on the same application between different developers.
This article aims to point out that there are a couple of drawbacks in combining the SolutionInfo pattern with the Partitioned single solution model.
Case Scenario 1:
First of all the SolutionInfo approach makes it impossible for a developer to copy the project as is to another location and import it into another solution file.
At compile time he would get the following message:
‘Error 1 Source file ‘pathToSolution\SolutionInfo.cs’ could not be opened …’
He will have to create a SolutionInfo.cs file in the same directory in which the copy of the project is placed in order to make the build succeed.
To understand why this fixes the problem let’s have a look at what happens behind the scenes:
Say that we created projects leaving the Visual Studio default behavior unchanged, that is, they are located in subdirectories of the directory containing the solution file.
When we add a file as link to a project (see note), the following lines will be added to the project file (projectName.csproj) under the ‘ItemGroup‘ tag:
<Compile Include="..\SolutionInfo.cs"> <Link>Properties\SolutionInfo.cs</Link> </Compile>
The ‘Include‘ attribute stores the path to the original file that we’re linking to.
The ‘Link‘ tag stores the path in which the link will be shown in Solution Explorer (including possible solution directory e.g.: ‘Properties‘).
As you can see, the ‘Include‘ attribute stores the relative path to SolutionInfo.cs with respect to the projectName.csproj file location. This will end up in Visual Studio (and MSbuild) looking for a file named SolutionInfo.cs one level above with respect to the projectName.csproj file.
So this is the reason for the error message shown above if copying a project directory anywhere else.
Let’s go further and introduce the SolutionInfo method in a Partitioned Single Solution.
The default Visual Studio behaviour of introducing relative paths (instead of using paths relative to the solution path) makes linking a file (e.g.: SolutionInfo.cs) in a Partitioned Single Solution structure an error prone procedure.
In fact it can easily lead to the build error reported above, if we use nested directory structures.
Let’s have a look at the possible situations:
We could decide to create a directory structure as shown in the following picture:
In fact when a developer checks out the directory containing the solution file he needs, he will also get the SolutionInfo.cs file (remember that with Subversion you can only checkout directories).
Practically speaking we will always checkout the top directory, no matter which solution file we will use.
Case Scenario 3:
What if we want to add a solution file for each project and we want to place it in the project’s directory?
A developer could checkout from the repository only a project’s directory and he would get the solution file too.
But when he will try to build the solution he will get the file missing error!
If he provides a copy of SolutionInfo.cs in the solution’s directory (which is the project’s directory too) it won’t work because MSbuild (as we said before) looks for the file up one level (..\SolutionInfo.cs).
The developer should create a SolutionInfo.cs file outside of its working copy to make the build succeed.If we have a more complicated directory structure with different levels of directories, with project directories in some of this levels, and with solution files in some of these levels too, the situation is even worse.
In fact the SolutionInfo.cs file is located more than one level up with respect to some of this project and solution files as you can see in the following image:
Each time a developer checks out a solution directory that is not the root one he will miss the SolutionInfo.cs file and he’ll need to open a project file in a text editor to know were it is supposed to be.
In order to avoid uncertainty about where each project expects the SolutionInfo.cs to be placed, we need to reference it with a path relative to the solution location instead of the project’s directory. So we can open projectName.csproj in a text editor and modify the entry as follows:
<Compile Include="$(SolutionDir)\SolutionInfo.cs"> <Link>Properties\SolutionInfo.cs</Link> </Compile>
In this way we let the projects be independent of their location in the directory structure.
Let’s go one step further.
If a developer doesn’t want to provide a SolutionInfo.cs file to its custom solution his solution won’t compile unless he sets SolutionInfo.cs ‘Build Action‘ to ‘None‘ (see note: Set Build Action to None).
The problem here is that the ‘Build Action‘ of a file is stored in the versioned projectName.csproj file. Changing this setting will impact on the working copies of all developers using that same project and even on the release build script.
Luckily we can apply a work around to this problem:
the ‘Compile‘ tag in the projectName.csproj file supports another useful attribute: ‘Condition‘.
The condition specified by such attribute will be evaluated by MSBuild during the build process and the file will be compiled only if the condition is satisfied.
We could add it to the row related to SolutionInfo.cs thus obtaining:
<Compile Condition="Exists('$(SolutionDir)\SolutionInfo.cs')" Include="$(SolutionDir)\SolutionInfo.cs"> <Link>Properties\SolutionInfo.cs</Link> </Compile>
MSBuild will evaluate the condition and if it will not find the linked file in the specified path it won’t even try to compile it.
This will allow developers to always have their projects successfully built even if a SolutionInfo.cs file is not present on disk.
Solution Explorer will let the project build succeed but will show a broken link in the GUI to let the user know that something is missing.The assembly resulting from such a working copy build process will lack the solution wide assembly information but the continuous integration tools, as well as developers using solutions including a SolutionInfo.cs file, will have their projects build with the complete assembly information.
If you use Subversions as Version Control System you must be aware of a problem in which you may run into when working with a partitioned single solution model.
If you organize your repository, say, like in the case scenario 2, and you want to checkout a solution (not the global one) and only the projects (and projects’ directories) referenced by that solution you should do it in two separate steps:
first of all keep in mind that with Subversion you can only checkout directories, so you cannot have only the solution file you need but you’re forced to get in your working copy all the solution files defined in the root directory:
– checkout non-recursively the root directory (the one with the solution files),
next you can add to your working copy only the projects’ directories you want:
– call svn update separately specifying the subdirectories you need.
This will seem to work at first but it is not true.
There’s an old implementation issue in Subversion (see: issue 695), related to checking out a directory non-recursively :
the issue consists in the fact that such a directory in the working-copy is not aware of being a partial checkout. So as long as anyone works on identical working copies everything works fine otherwise you will obtain a weird behavior.
If someone commits changes to, say, a directory that you didn’t check out from the repository, the first time you will make a global update of your working copy you’ll get the following error message:
>> Error: Directory ‘pathToWorkingCopy\project3Dir’ is missing
>> Error: Please execute the “Cleanup” command.
If you execute the cleanup and try to update again, the whole root directory will be checked out in your working copy!
The bug has been fixed and sparse directory checkout should be allowed with the upcoming 1.5 version of Subversion.
At the moment if you want to be safe with your working copy you have to checkout the whole root directory.
When you want to add a file as link to a project simply right click on the project and choose Add –> Existing item.
In the choose file dialog box locate and select the file you want (say e.g. SolutionInfo.cs). Don’t click the Add button, you should click the little down arrow on the right edge of the Add button instead. A drop down menu will appear as shown in the picture. Choose Add as link and you’re done (then you can move the link anywhere else in the project directory structure simply dragging it in Solution Explorer).
To set the Build Action to None for a project item simply select the item in Solution Explorer (e.g.: SolutionInfo.cs) and look at the Properties Panel.
A bunch of properties will be shown for the selected item through which you will see: ‘Build Action‘ with the value ‘Compile‘. Just click on the value and a drop down list will appear as shown in the picture. Select ‘None‘ and you’re done.