- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Wednesday, May 10, 2006

MSBuild: Build several projects

I received this email from a reader of my MSBuild MSDN Article, here is his question:

I have multiple projects in multiple directories on my machine, I need to compile all of those projects and copy the resulting DLLs into my application folder which id different from the projects. Can i do all this in one MSBuild file? Most the MSBuild file samples i find work only on a single project and assume that the build file is in the same directory as the .CS files.

To answer this question you can create an MSBuild project file which uses the MSBuild task to build other project files. By using this you can extract out the files that were built during the process and place them into an item to be copied to the location of your choice. To demonstrate this I have created the following directory structure:

├BuildSeveralExample

   ├───App1

      └───WindowsApplication1

          └───Properties

   ├───App2

      ├───ClassLibrary1

         └───Properties

      └───ConsoleApplication1

          └───Properties

   └───Deploy

 

The App1 and App2 folders each represent a product that is to be built. App1 contains a C# Win Forms project and App2 contains a C# Class Library and a C# Console App. The Deploy folder is the location that we would like to copy all of the output files to. In the BuildSeveralExample folder I have created a projects file, BuildAll.proj, which can be used to fulfill this need.

The first thing we need to do is to have a way to locate all of the project files, in my solution I created an Item and included all of the project files as such:
  <ItemGroup>

    <ProjectsToBuild Include="**\*proj" Exclude="$(MSBuildProjectFile)"/>

  </ItemGroup>

I include all project files in the current directory or any subdirectory, with the exception of the current project file. The MSBuildProjectFile is an MSBuild Reserved property, for a complete list see:  http://msdn2.microsoft.com/en-us/library/ms164309.aspx.

In your situation you may need to specify what files to include and exclude, to do this you can simply enter their locations separated with a semi-colon. Now that we have all the project files we’d like to build we need to actually build them. Do this by:

  <PropertyGroup>

    <Configuration>Release</Configuration>

  </PropertyGroup>

 

  <Target Name="CoreBuild">

    <MSBuild Projects ="@(ProjectsToBuild)"

             ContinueOnError ="false"

             Properties="Configuration=$(Configuration)">

        <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>

    </MSBuild>

  </Target>

 

This is using the MSBuild Task to each of the project files. Also note the use of the Output element here, the MSBuild Task has an output of TargetOutputs which contains a list of files which were produced by the project files built. These files are placed in the OutputFiles item. Since we didn’t specify a target to be executed on each project file, the default target will be executed. This is how Visual Studio builds your project files as well. By using the MSBuild task we can also specify properties to be passed to the project file(s) in the Properties attribute. If you need to pass different properties for different project files, then you may have to call the MSBuild Task more than once.

Now all we have to do is copy these files over to another location. This is achieved like:

 

  <PropertyGroup>

    <DestFolder>Deploy\</DestFolder>

  </PropertyGroup>

 

  <Target Name="CopyFiles">

    <Copy SourceFiles="@(OutputFiles)"

          DestinationFiles="@(OutputFiles->'$(DestFolder)%(RecursiveDir)%(Filename)%(Extension)')" />

  </Target>

The location to copy the files is specified in the DestFolder property shown above.

As well as performing these actions, we’d also like to create a fresh build when this process is invoked. To do this we can either call the Rebuild target on the project files or call Clean before we build them. I prefer the second approach, but both are equally good. It also serves for a better demonstration for me to implement the second approach.

To clean the projects all we have to do is call the Clean target on each of the project files. I have written about this in more detail in my previous entry Clean many projects and Extending the Clean. As well as that we have to remove any files that have been created by a previous invocation of this process, have a look at my clean target:

  <!--

  This target can be used to clean all of the projects before you build them.

  It will also delete any dll & exe files located in the the Deploy folder. This could be accomplished

  much better than this, but this is quick and easy.

  -->

  <Target Name="CleanAll">

    <!-- Delete any files this process may have created from a previous execution -->

    <CreateItem Include="$(DestFolder)\**\*exe;$(DestFolder)\**\*dll">

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

    </CreateItem>

    <Delete Files="@(GeneratedFiles)"/>

    <MSBuild Projects="@(ProjectsToBuild)" Targets="Clean" Properties="Configuration=$(Configuration);"/>

  </Target>

To tie it all together I’ve created a BuildAll target which is responsible for managing all of this. Here it is:

  <PropertyGroup>

    <BuildAllDependsOn>CleanAll;CoreBuild;CopyFiles</BuildAllDependsOn>

  </PropertyGroup>

  <Target Name="BuildAll" DependsOnTargets="$(BuildAllDependsOn)"/>

I took the Visual Studio approach and defined targets to perform individual steps, created one target to call them in the correct order by using a depends list derived from a property. This is a good approach because this depends on list can be modified outside of this project file to inject other required steps. For more detailed info on that see my other entry Extending the clean.

To see this in action you have to execute the BuildAll target on the BuildAll.proj file. I’ve made the project file available as well as my sample applications that go along with it. The BuildSeveralExample.zip file contains all the necessary files.

 

BuildAll.proj (Link fixed)

BuildSeveralExample.zip (25.17 KB)

 

Sayed Ibrahim Hashimi

 

msbuild | Visual Studio | Windows Forms Wednesday, May 10, 2006 6:39:05 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, January 26, 2006

Inherited Form with Visual Studio Designer

Ok,

I thought this was pretty straight forward but I had two people ask me about this recently so I thought I'd post a blog discussing it. There are many occasions where you'd like to create a Form that and use it as the base for inheritance by other forms. At the same time you'd like to still be able to use the Visual Studio Designer to design your Forms that inherit from that. In order to achieve this you'll have to create what's called an Inherited Form. You can use this form many things, including Visual Inheritance.

For this entry I have created a very simple example, you can download the files at the bottom of this entry if you are interested. To re-create the sample follow along. I created a new C# Windows Form Application. Then renamed the Form1.cs file to MyBaseForm.cs. This is the class that I would like to use as the base class for other forms. At this point I added a docked label to the form and add some specific stylistic changes to it. The resultant form is shown below.

As you can see from the image above there's not much to this form, but this is just a sample :-).
Now I'd like to provide an easy way to customize the description in my new label. To do this I'll create a new property and give it some attributes to be used by the Designer. That property is shown below.

[Description("Description")]
[Category("Form Base")]
public string Description
{
    get
    {
        return this.labelDescription.Text;
    }
    set
    {
        this.labelDescription.Text = value;
    }
}

This is just a normal property with the exception that It has the System.ComponentModel.Description attribute as well as the System.ComponentModel.Category attribute. The Description the name of this attribute that will be shown in the properties grid of the designer, and the category is the category that it will be in. If the Category is not present then it will default to the Misc category. Let's see how this makes a difference. Keep in mind that this is the simplest type of integration with the designer that you can achieve. The possibilities are endless.

At this point we should build the project. Before we can create the Inherited Form we must build the project, otherwise you'll get an error stating that no assemblies are present which contain a Form. So after the build has completed we want to add a new item to the project. One method for this is Right Click on the Project->Add->New Item. At this point you'll be presented a dialog to select the template type that you want to create. From this list select "Inherited Form". Have a look at the image below.

I'll call the new form MyInheritedForm.cs. After that you're given a dialog to select which Form class it should inherit from, then your form is created. For clarity about the designer integration here is a view of the properties grid from the MyInheritedForm designer view.

As you can see we have the Description property available in the Form Base category, just what we wanted! Now if the developer changes the value on this property then the text in the description label will be updated. Like I said earlier the designer integration is very capable, and this is the simplest form. Using these Inherited Forms for simple things like Visual Inheritance is a very clean way of mainting visual changes in a single form. But don't view this as the only use of Inherited Forms.

InheritedFormEx.zip (19.8 KB)

 

Sayed Ibrahim Hashimi

Visual Studio | Windows Forms Thursday, January 26, 2006 5:26:38 AM (GMT Standard Time, UTC+00:00)  #     |