Tuesday, January 17, 2006

Over the past few months I have noticed a need by many people to place some specific steps into the process that is used when MSBuild is invoked on solution files. When people research this topic they quickly discover that the Visual Studio Solution file is NOT in the MSBuild format. MSBuild is able to consume these files, but you are not able to actually extend the process used for the solution as a whole.
Recently there was an entry at the MSDN MSBuild Forum related to this issue. So I figured I'd put my two cents into the discussion as well.

For some background, when MSBuild needs to build a solution file, it is converted in memory to an MSBuild project file. If you have the envrionment variable msbuildemitsolution set to the value of 1. Then this file will be written out to a file. The file name will be SOLUTION_NAME.sln.proj.

Ok, previously there was another entry on the MSDN Forum about how to set an Envrionment variable using MSBuild. To let you know where I'm going with this, I'd like to have MSBuild be responsible for creating this file and for me to add steps before and after the building of my solution. The task to create the envrionment variable is shown below, this was written by Keith Hill and is at the previous link

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Sedodream.MSBuild
{
    /// <summary>
    /// Taken from http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=73225&SiteID=1
    /// Written by Keith Hill
    /// </summary>
    public class SetEnvVar : Task
    {
        private string _variable;
        private string _value;
        [Required]
        public string Variable
        {
            get { return _variable; }
            set { _variable = value; }
        }
        [Required]
        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }
        public override bool Execute()
        {
            Environment.SetEnvironmentVariable(_variable, _value);
            return true;
        }
    }
}

You can download this file at the bottom of this entry (SetEnvVar.cs).

So we need to take this file and create an assembly from it. Make sure to add Microsoft.Build.Framework and Microsoft.Build.Utilities to your project's references. Oh yeah you could skip this part of the process if you just make sure to set that envrionment variable, but that's no fun.

After you create this assembly place in a known location. I called this assembly Sedodream.MSBuild.dll and placed it in a folder named tasks one folder above the solution I was trying to build.

Now that we are done with that we need to create the msbuild file that will do the rest of the work for us.

The whole file is shown below and can be downloaded at the end of this blog (DreamCatcher_SlnBuild.proj)

<!--============================

MSBuild file which can be used to inject steps into the building of solution files

===============================-->

<Project InitialTargets="SetMSBuildEmit"
   
DefaultTargets="BuildSolution"
   
xmlns=http://schemas.microsoft.com/developer/msbuild/2003>

<PropertyGroup>

<!-- Location that holds all needed assemblies for the shared tasks -->

<SharedTasksDir>..\tasks\</SharedTasksDir>

<!-- Assembly that contains the custom tasks -->

<EnvAssemblyFilename>Sedodream.MSBuild.dll</EnvAssemblyFilename>

<!-- Where is the solution file(s) located -->

<BuildSolutionDir>.\</BuildSolutionDir>

</PropertyGroup>

<ItemGroup>

<!-- Item for solutions file(s) -->

<SlnFiles Include="$(BuildSolutionDir)*.sln"/>

</ItemGroup>

<!--Let MSBuild know where to find our tasks-->

<UsingTask AssemblyFile="$(SharedTasksDir)$(EnvAssemblyFilename)" TaskName="SetEnvVar"/>

<!--

Set the envrionment variable msbuildemit solution to 1 to create the msbuild

project file that represents the solution.

-->

<Target Name="SetMSBuildEmit">

<SetEnvVar Variable="msbuildemitsolution" Value="1"/>

</Target>

<Target Name="BuildSolution" DependsOnTargets="SetMSBuildEmit">

<!-- Have MSBuild emit the solution -->

<MSBuild Projects="@(SlnFiles)" Targets="Build"/>

<!-- Create a new item to pick up the newly generated file -->

<CreateItem Include="*.sln.proj">

<Output TaskParameter="Include" ItemName="SolutionMSBuildFiles"/>

</CreateItem>

<!--

Call MSBuild for each solution file. Pass the property:

theSolution=SOLUTION_FILE_TO_BUILD.

In the DoBuildSolution we use this to determine which file to build.

-->

<MSBuild Projects="$(MSBuildProjectFile)"

Targets="DoBuildSolution"

Properties="@(SolutionMSBuildFiles->'theSolution=%(Filename)%(Extension)')"/>

</Target>

<!--==========================

Content used when this file is invoked above

==============================-->

<!--

Define dependencies for the solution build. This can be extended like other XXXDependsOn Properties

-->

<PropertyGroup>

<DoBuildSolutionDependsOn>

BeforeDoBuildSolution;

CoreBuildSolution;

AfterDoBuildSolution

</DoBuildSolutionDependsOn>

</PropertyGroup>

<Target Name="DoBuildSolution" DependsOnTargets="$(DoBuildSolutionDependsOn)" />

<Target Name="BeforeDoBuildSolution">

<Message Text="*****Before building your solution*****" Importance="high"/>

</Target>

<!--

Here is where we actually build the solution passed to us.

-->

<Target Name="CoreBuildSolution">

<MSBuild Projects="$(theSolution)" Targets="Build"/>

</Target>

<Target Name="AfterDoBuildSolution">

<Message Text="###After building your solution######" Importance="high"/>

</Target>

</Project>

 

Now all you need to do is to place this file into the folder that contains your solution and call msbuild on it with:
    >msbuild.exe DreamCatcher_SlnBuild.proj /t:BuildSolution
The DefaultTargets is set to BuildSolution so you can take that off actually.

The are many advantages to this solution like:
    1) You extend the solution build process in similar ways as extending the project build proces
    2) You can re-use any existing MSBuild tasks
    3) You can add more steps to the build process easily
    4) You can place this file into source control and all team members can use it.

If you need more info please let me know, I would explain further now but I'm not feeling very well currently.

 

Sayed Ibrahim Hashimi


SetEnvVar.cs(.9kb)

DreamCatcher_SlnBuild.proj(3.19kB)

6/5/2006 10:15:12 AM (Eastern Daylight Time, UTC-04:00)
Hi there

I'm getting a 404 when I try to download the SetEnvVar.cs file

the DreamCatcher_SlnBuild.proj works fine

Cheers,
Steve C.
6/5/2006 10:33:15 AM (Eastern Daylight Time, UTC-04:00)
This problem has been fixed, sorry about that.

Sayed Ibrahim Hashimi
7/20/2006 5:36:34 AM (Eastern Daylight Time, UTC-04:00)
Hey Sayed,

since the build process executes within one appdomain (save for tasks explicitly made for running in their own), I store the data in a singleton (static), and use a set of tasks that can set/get this data. Seems to work.

Using this I can also run a task once at the start of the solution build and a task at the end.

Check it out here:
http://mawi.org/ManagingTheSolutionBuildOfMSBuild.aspx

Feedback welcome!

Best regards!
8/9/2007 6:26:12 AM (Eastern Daylight Time, UTC-04:00)
Hi Sayed,

There is under VS an action :
right click on your solution
select "batch build"
click "select all"
click "clean"

this performs a global clean of all projects.
I have no idea of what does VS is doing when you use this function. But i wonder : if there is a button thtat allow you to clean all projects, then there must be a way to do the same thing, but for an other target, like "DoSomething All". Then maybe we can also do :
"DoSomething All" + "Build All"
A kind of "pre solution build event".
The tip you gave is a bit "crapy", maybe not easily intergrated under VS, so i was wondering if you were aware of this "Clean All" button under VS and based in this, if you have any idea of how can this be implemented...
Sorry, maybe my post is not very clear.

Elise
10/9/2007 8:36:05 AM (Eastern Daylight Time, UTC-04:00)
This site is interesting and very informative, nicely interface. Enjoyed browsing through the site
10/9/2007 6:36:20 PM (Eastern Daylight Time, UTC-04:00)
I want simple code and xml to create the .sln file as we did for .proj in MSBuild ,I tried your code but it gives me a lot of errors ,maybe I didn't setup it correctly ,so please try to simplify the process.
Thanks,
Nabeel.
3/22/2008 12:06:59 PM (Eastern Daylight Time, UTC-04:00)
generic butalbital with online consultation
3/22/2008 12:09:51 PM (Eastern Daylight Time, UTC-04:00)
Cheapest propecia prescription drug
3/22/2008 12:19:21 PM (Eastern Daylight Time, UTC-04:00)
Discount prozac no prescription needed
3/22/2008 12:20:34 PM (Eastern Daylight Time, UTC-04:00)
buying on line diflucan
3/22/2008 12:23:35 PM (Eastern Daylight Time, UTC-04:00)
brand acyclovir Saturday delivery
3/22/2008 12:27:40 PM (Eastern Daylight Time, UTC-04:00)
Generic celexa sales
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Theme design by Jelle Druyts