Tuesday 8 October 2013

Structuring Visual Studio Solutions and hooking into your Visual Studio’s build process.


This is the first of a series of posts I will write about a very common problem faced by all sizable projects – how to structure your visual studio projects and resolve inter-dependencies between projects in different visual studio solutions. This is not an issue if you have just one visual studio solution as you can use project references across the board. However, if you have a number of solutions and there a developers working on them simultaneously than the dependencies can sometimes be crippling.

From my experience, the following structure is quite common for most projects.

TypicalPlatform

The Platform Service solution contains common platform specific libraries like configuration, diagnostics, common controls, etc. The Domain Specific services contain libraries specific to the business domain such for example business specific libraries, protocols or schemas. Then there are vertical solutions on top.

In terms of visual studio solutions and project dependences, I would create a solution for each layer and the visual studio projects sitting in top layers are dependent upon projects in the lower layers.

Now that I have explained the context, let’s see different options we have in terms of setting up projects etc. The first and most common method is to set up a “DevBuild” i.e. a build  script that builds all solutions in the order starting from the lower level solutions and culminating with compiling all the “verticals”.

All references within a solution are project references and references across solutions are file references. It’s best practice to reference the assemblies from a common “Assemblies” folder rather than the output path. Typically, you would want to do the copying of all assemblies after a build as a post-build activity. There are different ways of doing it. Below are some of the ones that I have used


1) Adding a custom .targets file in the within the $(MSBuildToolsPath)\Microsoft.Common.Targets\ directory.


This is my favourite and provides a clean “extension” point to add to the Visual Studio Build process. Visual Studio adds two folders within the MSBuild installation directory (C:\Program Files(x86)\MSBuild\12.0 for Visual Studio 2013). Within the directory, there are folders called ImportBefore and ImportAfter. You can write your own MSBuild code and store the file in either of the two folders. If you have extended or hooked into one of the targets run by Visual Studio as part of building a solution, your code will be executed. For example, the following code copies all the assemblies that starts with the name MyCustom and drops them to the common assemblies location for where they can be referenced by other solutions.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="
http://schemas.microsoft.com/developer/msbuild/2003">
    <Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>
    <PropertyGroup>
        <DevelopmentRoot Condition="$(DevelopmentRoot) == ''">C:\Projects\MyCustom\Development\Main</DevelopmentRoot>
        <AssembliesDirectory>$(DevelopmentRoot)\Assemblies</AssembliesDirectory>
    </PropertyGroup>
    <Target Name="CopyAssemblies" AfterTargets="Build" Condition="'$(SkipCopyAssemblies)' != 'true'">            
        <ItemGroup>
            <NetFilesToCopy Include="$(MSBuildProjectDirectory)\**\bin\$(Configuration)\MyCustom*.dll"/>
        </ItemGroup>
        <MSBuild.ExtensionPack.Framework.MsBuildHelper TaskAction="RemoveDuplicateFiles" InputItems1="@(NetFilesToCopy)" Condition="%(NetFilesToCopy.Identity) != ''">
            <Output TaskParameter="OutputItems" ItemName="NetFilesToCopyUnique"/>
        </MSBuild.ExtensionPack.Framework.MsBuildHelper>
        <MakeDir Condition="!Exists($(AssembliesDirectory))" Directories="$(AssembliesDirectory)"/>
        <Microsoft.Build.Tasks.Copy SourceFiles="@(NetFilesToCopyUnique)" DestinationFolder="$(AssembliesDirectory)" Condition="%(NetFilesToCopyUnique.Identity) != ''"/>       
    </Target>
</Project>


 


2) Using Post Build Event command


The post build events have been around since early releases of Visual Studio and allows you to run custom macros at the end of the build process. For me these are rather messy and interfere with your build process especially if you are using Team Build.


3) Visual Studio Add-on


Again a very messy approach but something we have used in the past. You can write your visual studio add-on that traps the compilation event and perform tasks such as copying files to a pre-defined location. I wouldn’t recommend it.


4) Use Visual Studio Solution extension points


When you build a visual studio solutions, behind the scenes, it generates and MSBuild file and uses the generated MSBuild file. In the generated MSBuild file, it includes two files called after.<SolutionName>.sln.targets and before.<SolutionName.sln.targets. If you have files with those names in the same directory and your solution files, it will become part of the build process and provides you an extension point. Sayed Ibrahim Hashimi has detailed it here.

The downside is that the extension points are only used when MSBUILD is called on the solution file (.sln) file from command line. Visual Studio doesn’t support it yet as detailed here.

For me Option 1 is still the best approach if you decide to go down the DevBuld route.

In my next post, I will write about NuGet and how it can be used as an alternative.


Technorati Tags: ,




Wednesday 2 October 2013

Building Visual Studio 2013 solutions with your TFS 2010 / 2012 Build Templates

Visual Studio 2013 is shipped with a new version of MSBuild. The new version or MsBuild 12.0 contains a number of new features as described here. It is installed separately and from now onwards will be following Visual Studio release cycle rather than .Net Framework’s release cycle.

If you are using Visual Studio 2013 to build say a Windows 8.1 app and are still using your TFS 2010 or TSF 2012 build templates, you might get errors because by default the templates use MSBuild 4.0. To resolve the issue please edit your build templates to do the following

1) Find all instances of the MSBuild activity. The activity is named as “Run MSBuild for Project” within the build templates as shown below

Run MSBuild Activity

2) Change the ToolsPath of the activity to point to the MSBuild 12.0 exe file as shown below

Run MSBuild ToolsPath

The TFS2013 version of this activity (Microsoft.TeamFoundation.Build.Workflow.Activities.MSbuild) contains a “ToolsVersion” property that you can use to set the correct version number but in earlier versions (such as TFS2010 or TFS2012) you will have to resort to pointing to the file location.

Implementing Static Code Analysis with StyleCop

In the Oct 2013 edition of MSDN Magazine I have written about details of how to set up StyleCop to run it in Team builds. The article can be found here