- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Monday, June 09, 2008

MSBuild Reserved Properties

If you take a look the MSDN documentation for the reserved MSBuild Reserved Properties you will see many properties listed. For one reason or another there are actually several reserved properties that are missing from that list. Here is the list of all the reserved properties, the ones missing from that page are presented here in bold. For those that are listed on that page the description is taken from the MSDN. Also this is the list for MSBuild 3.5.

 

 

Take a look at this project file that prints out these values.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="PrintProperties"
ToolsVersion="3.5">

<Target Name="PrintProperties">
<Message Text="MSBuildNodeCount: $(MSBuildNodeCount)"/>
<Message Text="MSBuildExtensionsPath32: $(MSBuildExtensionsPath32)"/>
<Message Text="MSBuildProjectDirectoryNoRoot: $(MSBuildProjectDirectoryNoRoot)"/>
<Message Text="MSBuildToolsPath: $(MSBuildToolsPath)"/>
<Message Text="MSBuildToolsVersion: $(MSBuildToolsVersion)"/>
<Message Text="MSBuildBinPath: $(MSBuildBinPath)"/>
<Message Text="MSBuildExtensionsPath: $(MSBuildExtensionsPath)"/>
<Message Text="MSBuildProjectDefaultTargets: $(MSBuildProjectDefaultTargets)"/>
<Message Text="MSBuildProjectDirectory: $(MSBuildProjectDirectory)"/>
<Message Text="MSBuildProjectExtension: $(MSBuildProjectExtension)"/>
<Message Text="MSBuildProjectFile: $(MSBuildProjectFile)"/>
<Message Text="MSBuildProjectFullPath: $(MSBuildProjectFullPath)"/>
<Message Text="MSBuildProjectName: $(MSBuildProjectName)"/>
<Message Text="MSBuildStartupDirectory: $(MSBuildStartupDirectory)"/>

</Target>
</Project>

If you execute this you will get something like what's shown here.

 

I'm not sure how important this, but you may need to know one day.

ReservedProperties.proj

Sayed Ibrahim Hashimi

msbuild Monday, June 09, 2008 6:52:31 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, June 04, 2008

DataContract XML Helper

Lately I've been doing some work with WCF and I have had a need to easily convert from object->XML and from XML->object. These XML strings were created using the DataContractSerializer. So I threw together this util class [yeah I know I probably shouldn't have these, but you know you do too J ]. There are really 2 methods GetDataContractXml and BuildFromDataContractXml. The GetDataContractXml returns the XML for the object provided and the BuildFromDataContractXml method will create the object from the XML string. These methods have both a generic and non-generic method. The non-generic versions are particularly useful if you are using this with reflection. Anywayz, the class itself is shown below, and you can download the file at the bottom of this post.

///
/// © Copyright Sayed Ibrahim Hashimi
/// www.sedodream.com
///
using System;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Serialization;
namespace Sedodream.Sample.Serialization01.Util
{
    public static class XmlUtils
    {
        /// 
        /// Builds an object of the specified type from the given
        /// XML representation that can be passed to the DataContractSerializer
        /// 
        public static object BuildFromDataContractXml(string xml, Type type)
        {
            if (string.IsNullOrEmpty(xml)) {throw new ArgumentNullException("xml"); }
            if (type == null) { throw new ArgumentNullException("type"); }

            object result = null;
            DataContractSerializer dcs = new DataContractSerializer(type);
            using (StringReader reader = new StringReader(xml))
            using (XmlReader xmlReader = new XmlTextReader(reader))
            {
                result = dcs.ReadObject(xmlReader);
            }

            return result;
        }
        /// 
        /// Builds an object from its XML 
        /// representation that can be passed to the DataContractSerializer.
        /// 
        public static T BuildFromDataContractXml(string xml)
        {
            if (string.IsNullOrEmpty(xml)) { throw new ArgumentNullException("xml"); }

            T result = default(T);

            object objResult = BuildFromDataContractXml(xml, typeof(T));
            if (objResult != null)
            {
                result = (T)objResult;
            }
            return result;
        }
        /// 
        /// Gets the XML representation of the given object
        /// by using the DataContracSerializer.
        /// 
        public static string GetDataContractXml(T obj)
        {
            if (obj == null) { throw new ArgumentNullException("obj"); }

            return GetDataContractXml(obj.GetType(), obj);
        }
        /// 
        /// Gets the XML representation of the given object
        /// of specfiied type by using the DataContracSerializer.
        /// 
        public static string GetDataContractXml(Type type, object val)
        {
            if (type == null) { throw new ArgumentNullException("type"); }
            if (val == null) { throw new ArgumentNullException("val"); }

            MemoryStream ms = new MemoryStream();
            string xml = null;
            try
            {
                DataContractSerializer dcs = new DataContractSerializer(type);

                using (XmlTextWriter xmlTextWriter = new XmlTextWriter(ms, System.Text.Encoding.Default))
                {
                    xmlTextWriter.Formatting = System.Xml.Formatting.Indented;
                    dcs.WriteObject(xmlTextWriter, val);
                    xmlTextWriter.Flush();
                    ms = (MemoryStream)xmlTextWriter.BaseStream;
                    ms.Flush();
                    xml = UTF8ByteArrayToString(ms.ToArray());
                }
            }
            finally
            {
                if (ms != null)
                {
                    ms.Close();
                    ms = null;
                }
            }
            return xml;

        }
        /// 
        /// Writes the XML representation from the DataContractSerializer
        /// into the specified filename. If a file at filename
        /// already exists then an Exception will be thrown.
        /// 
        public static void WriteDateContractToFile(string filename, T obj)
        {
            WriteDateContractToFile(filename, obj.GetType(), obj);
        }
        /// 
        /// Writes the XML representation from the DataContractSerializer
        /// into the specified filename. If a file at filename
        /// already exists then an Exception will be thrown.
        /// 
        public static void WriteDateContractToFile(string filename, Type type, object val)
        {
            if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("filename"); }
            if (val == null) { throw new ArgumentNullException("val"); }

            if (File.Exists(filename)) { throw new ArgumentException("filname"); }

            //TODO: Stream this into the file instead of this!!!
            File.WriteAllText(filename, GetDataContractXml(type, val));
        }
        private static String UTF8ByteArrayToString(Byte[] characters)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            string constructedString = encoding.GetString(characters);
            return (constructedString);
        }
        private static Byte[] StringToUTF8ByteArray(String pXmlString)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            byte[] byteArray = encoding.GetBytes(pXmlString);
            return byteArray;
        }
    }
}

To show how this can be used here is a quick sample unit test shown below, this will be a part of a later post on a related topic.

using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Sedodream.Sample.Serialization01;
using Sedodream.Sample.Serialization01.Util;

namespace unittest.Sedodream.Sample.Serialization01
{
    [TestFixture]
    public class TestXmlUtils
    {
        [Test]
        public void TestCreateXml()
        {
            string name = "Sayed Ibrahim Hashimi";
            string email = "sayed.hashimi@gmail.com";


            Person person = new Person();
            person.Name = name;
            person.Email = email;

            string xml = XmlUtils.GetDataContractXml(person);
            Assert.IsTrue(!string.IsNullOrEmpty(xml));
            Assert.IsTrue(xml.Contains(name));
            Assert.IsTrue(xml.Contains(email));
        }
        [Test]
        public void TestCreateFromXml()
        {
            const string xml =
@"
  sayed.hashimi@gmail.com
  9891668d-8107-4f3e-85a4-79e941453be2
  Sayed Ibrahim Hashimi
";

            string name = "Sayed Ibrahim Hashimi";
            string email = "sayed.hashimi@gmail.com";
            Guid id = new Guid("9891668d-8107-4f3e-85a4-79e941453be2");

            Person person = XmlUtils.BuildFromDataContractXml(xml);
            Assert.IsNotNull(person);
            Assert.AreEqual(name, person.Name);
            Assert.AreEqual(email, person.Email);
            Assert.AreEqual(id, person.Id);
        }
    }
}

Where the simple Person class is shown here

[DataContract(Namespace="http://schemas.sedodream.com/Serialization/2008/04")]
public class Person
{
    #region Constructors
    public Person()
    {
        Id = Guid.NewGuid();
    }
    #endregion

    [DataMember]
    public string Name
    {
        get;
        set;
    }
    [DataMember]
    public string Email
    {
        get;
        set;
    }
    [DataMember]
    public Guid Id
    {
        get;
        set;
    }
}

From the two simply test you can see how to create the object from the XML string and vice versa. As far as I know these work pretty good, if you find any issues please let me know and I can update them. At some point I will post some more information that is related to this keep an eye for it.

 

XmlUtils.cs

Sayed Ibrahim Hashimi

DataContractSerializer | WCF Wednesday, June 04, 2008 6:54:58 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, May 22, 2008

MSBuild Output Inferral and CreateProperty.ValueSetByTask

Ok there is this hidden feature of MSBuild its called output inferral. This is used when a target is skipped due its Inputs & Outputs (incremental building) but the target creates properties and items. Eventhough the inputs and outputs are up to date the side effects (properties & items) may change the rest of the build process. Because of this output inferral is required. When a target is skipped MSBuild will inspect the target for properties & items that were created and create those. This will ensure that other targets down the chain will not be affected by the target being skipped. In MSBuild 3.5 there is a new property on the CreateProperty task, this property is ValueSetByTask. The whole purpose of this is to only be used when you do not want its value to be inferred. By using this property instead of Value you would be guaranteed that the target was actually executed. Let's clear this up with an example take a look at the project file shown below.

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

<!--
This target will always be skipped .
Neither property One or Two exists but One will be inferred
by MSBuild.
-->
<Target Name="SetupValues"
Inputs="$(MSBuildProjectFullPath)"
Outputs="$(MSBuildProjectFullPath)">

<Message Text="SetuptValues Executed!" Importance="high"/>

<CreateProperty Value="1111">
<Output PropertyName="One" TaskParameter="Value"/>
<!-- Property One is actually inferred by this statement. -->
</CreateProperty>
<CreateProperty Value="2222">
<Output PropertyName="Two" TaskParameter="ValueSetByTask"/>
<!-- Using ValueSetByTask we can ensure bypassing any inferring here -->
</CreateProperty>

<PropertyGroup>
<!-- 3.5 syntax: Property Three is also inferred -->
<Three>3333</Three>
</PropertyGroup>

<!-- Items are inferred too! -->
<ItemGroup>
<File Include="app.config"/>
</ItemGroup>
</Target>

<Target Name="PrintValues" DependsOnTargets="SetupValues">
<Message Text="One: $(One)" />
<Message Text="Two: $(Two)" />
<Message Text="Three: $(Three)"/>
<Message Text="File: @(File)"/>
</Target>

</Project>

In this file (you can download from link at end) there are two targets, SetupValues and PrintValues. PrintValues depends on SetupValues. Because the Inputs and Outputs on SetupValues point to the same file the target will always be skipped. But we declare properties and items so they have to be inferred by the MSBuidl engine so that the remainder of the build will not be hosed because of it. If you execute the PrintValues target the result would be what you see in the image below.

As you can see the values for the properties One & Three were provided by inference but the value for Two was passed over because it uses the ValueSetByTask instead of Value. I would suggest that you continue to use the Value property and not the ValueSetByTask unless you are trying to detect this exact scenario, which most of the time shouldn't matter anywayz.

OutputInferral.proj


Sayed Ibrahim Hashimi

msbuild Thursday, May 22, 2008 6:09:41 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, May 08, 2008

MSBuild: Building the same project multiple times

After some discussion at the MSBuild MSDN Forum I started investigating building the same project with different Compiler constants defined. I was actually pretty surprised by what I found. I created a simple Windows Form Application with a lone button one it. The image below show the simple app.

Here is the entire code behind file.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace WindowsFormsApplication1

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

 

private void buttonExample_Click(object sender, EventArgs e)

{

string message = "default message";

#if CONSTANT_ONE

message = "CONSTANT_ONE";

#endif

#if CONSTANT_TWO

message = "CONSTANT_TWO";

#endif

 

MessageBox.Show(message);

}

}

}

Ddd

The parts to take note are the regions highlighted in yellow. So if the constant CONSTANT_ONE is defined the message results to CONSTANT_ONE and if CONSTANT_TWO is defined it will show the message CONSTANT_TWO.

I then created the simple build file.

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="BuildAll" ToolsVersion="3.5"

xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

 

<PropertyGroup>

<ConstOne>CONSTANT_ONE</ConstOne>

<ConstTwo>CONSTANT_TWO</ConstTwo>

</PropertyGroup>

 

<ItemGroup>

<Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj">

</Projects>

</ItemGroup>

 

<!--

Cleans the project before we start the build process.

The only reason I am calling this is to remove artifacts of

previous builds, to clearly demonstrate what's going on.

-->

<Target Name="CleanAll">

<MSBuild Projects="@(Projects)" Targets="Clean" />

</Target>

 

<Target Name="BuildAll" DependsOnTargets="CleanAll;BuildConstOne;BuildConstTwo" />

 

<Target Name="BuildConstOne">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\;"/>

</Target>

<Target Name="BuildConstTwo">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;"

 

/>

</Target>

 

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

This region rebuilds the projects, so after it is built once it is then cleaned

before building it again.

This produces the assemblies with the desired behavior

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

 

<Target Name="ReBuildAll" DependsOnTargets="CleanAll;ReBuildConstOne;ReBuildConstTwo" />

<Target Name="ReBuildConstOne">

<MSBuild Projects="@(Projects)" Targets="Rebuild"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\;"/>

</Target>

<Target Name="ReBuildConstTwo">

<MSBuild Projects="@(Projects)" Targets="Rebuild"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;"/>

</Target>

 

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

This region calls the default target (Build) but also specifies the

BaseIntermediateOutputPath so it works as well because the projects

are building to different locations on disk.

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

<Target Name="BuildAllBetter" DependsOnTargets="CleanAll;BuildConstOneBetter;BuildConstTwoBetter" />

<Target Name="BuildConstOneBetter">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\BaseIntermediateOutputPath=objOne\"/>

</Target>

<Target Name="BuildConstTwoBetter">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;BaseIntermediateOutputPath=objTwo\;"

 

/>

</Target>

 

</Project>

Ddddd

Let's take a look at what's going on here. The BuildConstOne target passes the CONSTANT_ONE value as a property when invoking the MSBuild task, and builds to the binOne folder. The BuildConstTwo target passes the CONSTANT_TWO value and builds to binTwo. If you execute the target BuildAll, you would expect that the exe in binOne would show the message CONSTANT_ONE and the exe in the binTwo folder has CONSTANT_TWO message showing. What you actually get is that both actually show CONSTANT_ONE. This is because MSBuild doesn't entirely rebuild the project when building it for the second time in BuildConstTwo. The real reason behind this would be because both projects are building to the same BaseIntermediateOutputPath, obj\. There are two ways to of getting around this, one is to call Rebuild instead of Build, and the other is to specify the BaseIntermediateOutputPath as well. In the above I have demonstrated both approaches. The ReBuildAll calls Rebuild, and the BuildAllBetter specifies the BaseIntermediateOutputPath as well as OutputPath.

Questions?!

You can download all the files at:

DefineConst.zip

Sayed Ibrahim Hashimi

msbuild | Visual Studio Thursday, May 08, 2008 12:52:14 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, April 29, 2008

How to debug MSBuild tasks

A question that comes up pretty frequently is "How can I debug an MSBuild task"? It's actually pretty simple. In this post I will describe how to easily an effectively debug MSBuild tasks that you are creating. In this example I will be demonstrating a task from my open source tasks at www.codeplex.com/Sedodream. The task is one that was contributed by Grant Holliday.

First in the project where your tasks are contained create a folder that will be used to contain sample MSBuid files that can be used to debug the tasks. This is also a good idea, because it will show people how to use your tasks. If you are don't want to mix samples & code in the same project then just make sure in your build you copy the files to the correct locations. In the sample project, which you can download at the bottom, the folder is named Samples. When you add MSBuild files to the folder make sure you set the file to be copied to the output folder as well. See the image below.

By setting this, the file will be copied to the output folder when the project is built. Since it will be in the output folder we can use a relative path to get to the assembly that contains the task. Take a look at the sample MSBuild file for this task.

<?xml version="1.0" encoding="utf-8"?>

<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup>

    <TaskLocation Condition="$(TaskLocation)==''">$(MSBuildProjectDirectory)\..\DebugTask.dll</TaskLocation>

  </PropertyGroup>

   <UsingTask TaskName="CreateGuid" AssemblyFile="$(TaskLocation)"/>

 

  <Target Name="Example">

    <Message Text="Starting example" />

    <CreateGuid>

        <Output PropertyName="Guid" TaskParameter="Output"/>

      </CreateGuid>

    <Message Text="Guid: $(Guid)" />

   </Target>

</Project>

 

The line highlighted contains the path to the assembly that contains the task. This path is relative to the location in the output folder, not the source folder. Also another thing to take note, is that we only write to the TaskLocation property if it is empty. This is useful because you can overwrite the location through command line parameters if necessary. But this shouldn't be needed for what we are trying to accomplish here.

After you create the sample, build the solution. And open a command prompt to verify that it works. Here is a sample of the result of this project file.

Once you've verified that that MSBuild file works then you can right click on the project that contains your task, and select properties. From there go to the debug tab. What we want to do is start the msbuild.exe executable on that sample project file. To do this fill in the full path to it in the Start External program text box, i.e. 'C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe'. In the command line arguments you should pass the name of the project file followed by any msbuild parameters. Typically I will attach a file logger to the process. Finally you should set the working directory to the folder containing the sample. The result should look something like the image shown below.

Following this set a break point in your task, set the project as the startup project and hit F5! Another thing to take note of is that these properties are stored in the .user file so it shouldn't affect any other developers on your team.

Below is the link containing a simple solution that was used here.

 

DebugTask.zip

Sayed Ibrahim Hashimi

msbuild | Visual Studio Tuesday, April 29, 2008 6:10:03 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, April 15, 2008

MSBuild + JSLint = Good Javascript

Recently I was introduced to a tool that was to a tool that would analyze Javascript for syntactical errors as well as usage of bad practices. This tool is JSLint. Douglas Crockford from Yahoo! created, and maintains, JSLint. If you haven't heard of him, he is a well known Javascript expert.

Now how can we verify our Javascript with JSLint & MSBuild? In my open source project Sedodream MSBuild I have created a new JSLint task. You can get the installer by going to http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=Sedodream&ReleaseId=12530. After you install the msi you can follow these steps to invoke the JSLint on your projects.

Edit your web project file and include the following snippet before the </Project> tag.

<Import Project="$(MSBuildExtensionsPath)\Sedodream\Sedodream.tasks"/>

<Import Project="$(MSBuildExtensionsPath)\Sedodream\JSLint.targets"/>

 

<PropertyGroup>

<BuildDependsOn>

$(BuildDependsOn);

RunJSLint;

</BuildDependsOn>

</PropertyGroup>

 

The first line includes makes the tasks from my project available, and the second line includes the file that knows how to execute the JSLint tool. Following that I extend the build process to execute the RunJSLint target. If you are not familiar with this take a look at my article Inside MSBuild.

After you do this you may be prompted with a security warning from Visual Studio, you should pick 'Load Project Normally'. You can read more about how to disable it at http://msdn2.microsoft.com/en-us/library/ms228217.aspx. I chose to not have my installer set that registry flag. For the time being I think that users should make that decision themselves, although I would like to think I'm trustworthy J

After you do this and allow Visual Studio to load the project normally, if your create Javascript files that have errors, or JSLint doesn't like you will be warned in the error list as shown below.

 

With that being said keep in mind this is a V1 deal, so this may not work perfect. I am working to make sure that it works well, but it was kinda tricky to get this to work period!

As always I welcome your feedback and I will post more info about this later. Oh yeah by default from JSLint the GoodParts are enforced keep an eye on this blog, or send me a message, to see how to change that.

Sayed Ibrahim Hashimi

msbuild | Visual Studio | Javascript Tuesday, April 15, 2008 6:25:01 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, March 28, 2008

MSBuild Batching Part 3

This post is to supplement the two previous posts that I have focused on batching those are located at:

Batching continues to be confusing and I hope that that post will help to further clarify it a little bit. In the two previous posts I think that I was "hiding" what was really happening by using built in metadata. This time I will expose what's happening by a simple example that only uses custom metadata. The first thing to know about batching, it always occurs over metadata, either Well Known or custom. Take a look at the project file shown below, Batching03.proj.

<!—

© Copyright 2008, Sayed Ibrahim Hashimi (sayed.hashimi@gmail.com)

-->

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

<ItemGroup>

<Server Include="Server1">

<Type>2008</Type>

<Name>SVR01</Name>

</Server>

<Server Include="Server2">

<Type>2003</Type>

<Name>SVR02</Name>

</Server>

<Server Include="Server3">

<Type>2008</Type>

<Name>SVR03</Name>

</Server>

<Server Include="Server4">

<Type>2003</Type>

<Name>SVR04</Name>

</Server>

</ItemGroup>

 

<Target Name="TaskBatching">

<Message Text="%40(Server->'%(Name)'): @(Server->'%(Name)')" />

<Message Text="===========================================" />

<!--

Task Batching Here over the Name metadata.

Notice that Message task is invoked once per unique batch

-->

<Message Text="%25(Server.Name): %(Server.Name)" />

<Message Text="===========================================" />

<Message Text="%25(Server.Type): %(Server.Type)" />

<Message Text="===========================================" />

</Target>

 

<Target Name="TargetBatching01" Outputs="%(Server.Name)">

<Message Text="===== TargetBatching01 ============" />

<Message Text="%25(Server.Name): %(Server.Name)" />

<Message Text="%25(Server.Type): %(Server.Type)" />

<Message Text="Server: @(Server)" />

<Message Text="==================================="/>

</Target>

<Target Name="TargetBatching02" Outputs="%(Server.Type)">

<Message Text="===== TargetBatching01 ============" />

<Message Text="%25(Server.Name): %(Server.Name)" />

<Message Text="%25(Server.Type): %(Server.Type)" />

<Message Text="Server: @(Server)" />

<Message Text="==================================="/>

</Target>

 

 

<Target Name="All" DependsOnTargets="TaskBatching;TargetBatching01;TargetBatching02"/>

</Project>

There are three targets that are important TaskBatching, which demonstrates how you can invoke individual tasks once per batch and then two targets; TargetBatching01 & TargetBatching02, which demonstrate batching an entire target once per batch. When you want to proform batching you will always use the %(…) notation somewhere. In Task batching this will be notated only inside of the tasks' parameters. In target batching this notation will be declared in the targets Outputs attribute.

Zooming in on TaskBatching target here are the results and we will discuss

The comments on the image pretty much describe what's happening. In the first execution there we are not batching. So the item is transformed into a string and passed into the Message task. The transformation is described by what is contained in the ->(). So the Metadata Name for each element in Server is evaluated and a string is created from that by concatenating each of these values. If there were duplicate values then the duplicates would show up in the resulting string.

Moving on to the next one, we batch on the Name Metadata. Since there were 4 Name metadata values the Message task is invoked 4 distinct times. The same applies for the Next invocation of the Message task.

Now let's take a look at the Target Batching. I simply invoked these targets, nothing special. The TargetBatching01 target is batching on the Name Metadata, and the TargetBatching02 on the Type Metadata. The output is shown below.

Because there are 4 distinct Name values TargetBatching is invoked 4 times, and similarily TargetBatching twice because of the distinct Type values. So if you just need to perform a single task over a set of parameters, the use Task batching. But If you need to perform a set of actions on a set of parameters then use Target Batching. More to come later on this topic I'm sure.

As a note I have decided to resume work on the MSBuild book I referenced in earlier posts. You'll be happy to know that I will cover batching in great detail there.

 

Sayed Ibrahim Hashimi

 

 

 

 

 

 

ddd

Friday, March 28, 2008 8:04:53 PM (GMT Standard Time, UTC+00:00)  #     | 
Monday, February 25, 2008

MSBuild Property & Item evaluation

The other day I was talking to a friend of mine and he was experiencing some difficulties understanding some behavior in his build file. To summarize what was confusing him, Properties are evaluated on the fly and as files are imported. Items are gathered as files are imported then evaluated at the end of the project parsing but before any target is executed. To demonstrate this in a simple MSBuild example take a look at the simple build file example below.

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

 

<PropertyGroup>

<PropBefore>[$(Configuration)]</PropBefore>

</PropertyGroup>

<ItemGroup>

<ItemBefore Include="[$(Configuration)]" />

</ItemGroup>

 

 

<PropertyGroup>

<Configuration>Debug</Configuration>

</PropertyGroup>

 

<PropertyGroup>

<PropAfter>[$(Configuration)]</PropAfter>

</PropertyGroup>

<ItemGroup>

<ItemAfter Include="[$(Configuration)]" />

</ItemGroup>

 

<Target Name="PrintInfo">

<Message Text="PropBefore: $(PropBefore)"/>

<Message Text="ItemBefore: @(ItemBefore)"/>

 

<Message Text="==============================================="/>

 

<Message Text="PropAfter: $(PropAfter)"/>

<Message Text="ItemAfter: @(ItemAfter)"/>

 

</Target>

 

</Project>

 

So here the property Configuration is defined in the middle, before and after that I have declared both a Property & an Item that contains its value. Take a look at the output below.

The noticeable difference here is that the ItemBefore actually has the correctly resolved value for the Configuration property where as the PropBefore property does not. If you think about this it makes sense because properties depend on other properties and you can change/extend the value for a property as the project is loaded. Items don't behave in this manner. Anywayz this is something that you should be careful not to trip on.

 

Sayed Ibrahim Hashimi

msbuild Monday, February 25, 2008 7:26:48 AM (GMT Standard Time, UTC+00:00)  #     | 
Wednesday, February 20, 2008

MSBuild: Part 1 - QuickStart

Previously I was thinking of writing some more material on MSBuild. Since then I have written about 60 pages of new material, and have actually decided to focus on some new topics. Because of this I will release a mulit part series that consists of this material that I have written. I think that I've come up with some pretty good content and would like to make it available. It still requires polishing and changes for this new format. So this is the first in this series of post.

These first posts will be from the MSBuild Quick Start chapter

In this post we will discuss the topics highlighted.

MSBuild Quick Start

MSBuild Quick Start

When you are learning a new subject it's exciting to get just dive right into it and get your hands dirty. In this section I will describe all the key elements you will need to know to get started using MSBuild. If you are already familiar with MSBuild feel free to skip this section, all of the material covered here will be covered in later sections as well.

The topics that we will cover in this section include; structure of an MSBuild file, Properties, Targets, Items and invoking MSBuild. With that being said, let's get started.

Project file details

An MSBuild file, typically called an MSBuild project file is really an XML file. These XML files are described by two XMLSchema Documents (XSD). These are located in the %WINDIR%\Microsoft.NET\Framework\v2+\MSBuild folder. Where v2+ is the version folder for .NET 2 and above. In this text I will assume you are using .NET 2.0 unless specified. In those folders you will find Microsoft.Build.Commontypes.xsd, this is the file that describes commonly found element in Visual Studio generated project files. Also this file includes the Microsoft.Build.Core.xsd document. This document describes all the fixed elements in an MSBuild project file. The simplest MSBuild file would contain the following:

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

</Project>

This is the XML fragment that will identify this as an MSBuild file. Inside these tags is where all your content will be placed. Specifically we will be declaring Properties,Items, Targets and a few other items directly under the Project element. When you build you need to know what you are building, typically contained in Items and values that drive the build process, typically stored in Properties. We will now discuss how to declare properties, and we will cover Items later in this chapter.

MSBuild Properties & Targets

MSBuild properties are simply key value pairs, the key for the property is the name that you will use to refer to the property, and the value is, simply, its value. When you declare static properties they are contained in a PropertyGroup element directly under the Project element. Below is an example.

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

<PropertyGroup>

<AppServer>\\sayedApp</AppServer>

<WebServer>\\sayedWeb</WebServer>

</PropertyGroup>

</Project>

In the snippet above I have declared two properties; AppServer and WebServer with the values '\\sayedApp' and '\\sayedWeb' respectively. You can create as many PropertyGroups under the Project tag as you desire. The above fragment could have been defined as the one shown below.

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

<PropertyGroup>

<AppServer>\\sayedApp</AppServer>

</PropertyGroup>

<PropertyGroup>

<WebServer>\\sayedWeb</WebServer>

</PropertyGroup>

</Project>

If you create a sample project in Visual Studio, you will notice that there are many properties declared. These properties contain values that will be used throughout the build process. Below is a fragment from a Windows Application project that I created using Visual Studio.

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

<PropertyGroup>

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

<ProductVersion>8.0.50727</ProductVersion>

<SchemaVersion>2.0</SchemaVersion>

<ProjectGuid>{A71540FD-9949-4AC4-9927-A66B84F97769}</ProjectGuid>

<OutputType>WinExe</OutputType>

<AppDesignerFolder>Properties</AppDesignerFolder>

<RootNamespace>WindowsApplication1</RootNamespace>

<AssemblyName>WindowsApplication1</AssemblyName>

</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

<DebugSymbols>true</DebugSymbols>

<DebugType>full</DebugType>

<Optimize>false</Optimize>

<OutputPath>bin\Debug\</OutputPath>

<DefineConstants>DEBUG;TRACE</DefineConstants>

<ErrorReport>prompt</ErrorReport>

<WarningLevel>4</WarningLevel>

</PropertyGroup>

....

</Project>

You can see that things such as the output type, name of the assembly and many other values are defined in properties. Defining properties is great, but completely useless without being able to use them. So we will move on to Target declarations.

MSBuild has execution elements, those are Tasks and Targets. A Task is the smallest unit of work in and MSBuild file, and a Target is simply a sequential set of tasks. A Task must always be contained within target. Below is a sample that shows you the simplest MSBuild file which contains a target.

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

<Target Name="HelloWorld">

</Target>

</Project>

In this sample we have created a new Target named HelloWorld, but it actually doesn't perform any work at this point because it is empty. When MSBuild is installed you are given many tasks out of the box. You can find a list of these tasks at MSBuild Task Reference (http://msdn2.microsoft.com/en-us/library/7z253716.aspx). We will now use the Message task. The message task is used to send a message to the logger(s) that are listening the build process. In many cases this means that a message is sent to the console that is executing the build process. When you invoke a task in an MSBuild file you can pass it input parameters by inserting XML attributes with values. These attributes will vary from task to task depending on what inputs the task is expecting. From the documentation for the Message task (http://msdn2.microsoft.com/en-us/library/6yy0yx8d.aspx) you can see that is accepts a string parameter named Text. The snippet below shows you how to use the Message task to send the value 'Hello World' to the logger(s).

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

<Target Name="HelloWorld">

<Message Text="Hello world!"/>

</Target>

</Project>

Now we will verify that this works as expected. To do this place the above text into a file named HelloWorld.proj. Now open a Visual Studio Command Prompt and go to the directory that contains the newly created file. The shortcut for this prompt is in the Visual Studio Tools folder under the Visual Studio folder in the Start menu. The command that you'll use is the msbuild.exe command. The basic usage for the command is

msbuild [INPUT_FILE] /t:[TARGETS_TO_EXECUTE]

So our command would be

msbuild HelloWorld.proj /t:HelloWorld

In the above we are saying, build HelloWorld.proj by executing the HelloWorld target. The result of this command is shown in the image below.

 

In the image above we can see that the HelloWorld target is executed and that a message 'Hello World!' was displayed on the console. Now let's see how we can use properties within other properties. Have a look at the simple project file shown below.

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

<PropertyGroup>

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

<DropLocation>\\sayedData\MSBuildExamples\Drops\$(Configuration)\$(Platform)\</DropLocation>

</PropertyGroup>

<Target Name="PrepareFilesForDrop">

<Message Text="DropLocation : $(DropLocation)"/>

</Target>

</Project>

In the snippet above we can see that there are three properties that have been declared. On both the Configuration & Platform properties there is a Condition attribute. We will discuss these attributes later in this chapter. The remaining property, DropLocation, is defined using the values of the two previously declared items. Let's take a close look at that. The DropLocation Property has three components; a constant value and two values that are derived from the Configuration & Platform properties. When the MSBuild engine sees the $() notation it will replace that with the value of the specified properties. So the qualified DropLocation value would be: '\\sayedData\MSBuildExamples\Drops\Debug\AnyCPU\'. You can verify that by executing the PrepareFilesForDrop target from the Visual Studio Command Prompt. The reference for properties can be found at http://msdn2.microsoft.com/en-us/library/ms171458(VS.80).aspx.

When you use MSBuild there are a handful of properties that are available to you out of the box, that cannot be modified. These are known as Reserved properties. In the table below you will find all the Reserved properties.

Name

Description

Example

MSBuildProjectDirectory

Full path to the location of project file.

C:\MSBuild\MSBuild1\MSBuild1

MSBuildProjectFile

Filename of the project file, including extension.

MSBuild1.csproj

MSBuildProjectExtension

Extension of the project filename, including the initial dot.

.csproj

MSBuildProjectFullPath

Full path to the project file including the filename.

C:\MSBuild\MSBuild1\MSBuild1\MSBuild1.csproj

MSBuildProjectName

Name of the MSBuild project file excluding the file extension.

MSBuild1

MSBuildBinPath

Full path of the .NET Framework MSBuild bin directory.

%WINDIR%\Microsoft.NET\Framework\v2.0.50727

MSBuildProjectDefaultTargets

The value for the DefaultTargets attribute that is in the Project element.    

Build

MSBuildExtensionsPath

Full path to the MSBuild folder located in the Program Files directory. This is a great place to keep commonly used items.

C:\Program Files\MSBuild

 

You would use these properties just as you would properties that you have declared in your own project file. To see an example of this you can look at Visual Studio generated project files. When you initially create a new C# project you will find the import statement <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> towards the bottom. This import declaration is using the MSBuildBinPath reserved property to resolve the full path to the Microsoft.CSharp.targets file. This is the file that drives the build process for C# projects. We will discuss its contents in more detail later in the book. Now that we have an idea how to use properties we will move on to Items.

Items

Building applications usually means dealing with many files. Because of this there is a specific construct that is used when dealing with files, and that is what's called Items. Items are usually file references, but they actually can be used for other purposes as well. If you create a project using Visual Studio you may notice that you see many PropertyGroup elements as well as ItemGroup elements. The ItemGroup elements contain all the defined items. When you define a Property you are declaring a key/value pair, but when you declare an Item you declare Items that can potentially have many elements inside of them. Let's see the syntax in action.

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

<ItemGroup>

<SolutionFile Include="..\MSBuildExamples.sln"/>

</ItemGroup>

<Target Name="PrintSolutionInfo">

<Message Text="SolutionFile: @(SolutionFile)"/>

</Target>

</Project>

In this file, ItemsSimple.proj, there is an ItemGroup that has a sub element SoultionFile. The ItemGroup is the element type that all statically declared Items must be placed. The name of the sub elements are the names of the actual Items. In the above sample we have declared an Item names SolutionFile. The SolutionFile element has an attribute attached to it, the Include attribute. This attribute determines what actual entries are contained within the Item. This is because an Item can contain multiple entries. The Include attribute can contain a single value or a comma separated list of values. We will see this demonstrated later in this chapter.

In the target, PrintSolutionInfo, we can see the Item at use. Item evaluation syntax varies from Property syntax. To get the value of an item you will use the @() notation. The result of the build file, ItemsSimple.proj, is shown in the image below.

In this case the Item, SolutionFile, contains a single entry. Now let's see the result if there were multiple files included in the item. The snippet below shows the modified version of the ItemsSimple.proj file.

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

<ItemGroup>

<SolutionFile Include="..\MSBuildExamples.sln"/>

</ItemGroup>

<Target Name="PrintSolutionInfo">

<Message Text="SolutionFile: @(SolutionFile)"/>

</Target>

 

<ItemGroup>

<Compile Include="Form1.cs;Form1.Designer.cs;Program.cs;Properties\AssemblyInfo.cs" />

</ItemGroup>

<Target Name="PrintCompileInfo">

<Message Text="Compile: @(Compile)"/>

</Target>

</Project>

When you invoke the PrintCompileInfo target you will get the result 'Compile: Form1.cs;Form1.Designer.cs;Program.cs;Properties\AssemblyInfo.cs'. The Message tasks' Text parameter is a string value so the item is automatically converted, by MSBuild, into a string then passed to the task. This is done by concatenating all the entries separated by a ;. Later in the book I will demonstrate how you can customize this conversion process. Since the Include attribute can accept either one or many entries we could have actually defined the Compile Item as follows:

<ItemGroup>

<Compile Include="Form1.cs"/>

<Compile Include="Form1.Designer.cs"/>

<Compile Include="Program.cs"/>

<Compile Include="Properties\AssemblyInfo.cs"/>

</ItemGroup>

When a Property declaration appears after a previous declaration, this results in the value of the Property to be overridden. Items act differently from this; you cannot remove entries or overwrite values contained in an Item. You can only append to them. This is why both of the above statements are equivalent. Typically Items refer to existing files, if this is the case you can use wild cards to automatically include whatever files meet the conditions contained in the include statement. You can use three wildcard elements with MSBuild: ?, *, and **. You can use ? to replace a single character with any character. For example, the include declaration Include="c?r.cs" would include the files car.cs, cbr.cs, ccr.cs, and so on. The * element can replace any location with zero or more characters. To change the previous example, Include="c*r.cs" would include car.cs, caar.cs, cr.cs, colorer.cs, and so on. The ** notation tells MSBuild to search the directories recursively for the pattern. For example, Include="src\**\*.cs" would include all the files under the src directory with .cs extensions.

Another difference between Properties and Items is that Items can have metadata. Actually when you declare an Item each entry has a set of metadata associated with it out of the box. The table below outlines the well known metadata.

Name

Description

FullPath

Full path of this item including the filename.

RootDir

Root directory to which this item belongs.

Filename

Filename for this item, not including the extension.

Extension

File extension for this item.

RelativeDir

Path to this item relative to the current working directory.

Directory

Directory of the item without the root directory.

RecursiveDir

Used for items that were created using wildcards. This would be the directory that replaces the wildcard(s) statements that determine the directory.

Identity

Value for the item specified in the Include attribute.

ModifiedTime

Time this item was last modified.

CreatedTime

Time the item was created.

AccessedTime

Last time this item was accessed.

 

The basic usage for accessing metadata values is described by the template shown below.

@(ITEM_NAME->'%(METADATA_NAME)')

Where ITEM_NAME is the name of the item and METADATA_NAME is the name of the metadata that you would like to retrieve. Consider the following example. Assume that you have the following files in a directory; Class1.cs,Class2.cs,Class3.cs and Class4.cs. The following project file, MetadataExample01.proj, shows how to access the Compile Items' Fullpath metadata.

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

<ItemGroup>

<Compile Include="*.cs"/>

</ItemGroup>

<Target Name="PrintCompileInfo">

<Message Text="Compile fullpath: @(Compile->'%(Fullpath)')"/>

</Target>

</Project>

If you execute the PrintCompileInfo target you will see the following result.

From the output shown in the figure above we can see that the full path for all the Compile items was sent to the console. Accessing the metadata in this manner is given the term Item Transformation. We will discuss transformations in much more detail in a later chapter. Before we move on to discuss MSBuild Conditions take a look at a simple Windows Application project file that was generated by Visual Studio. You should see many things that you recognize.

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

<PropertyGroup>

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

<ProductVersion>8.0.50727</ProductVersion>

<SchemaVersion>2.0</SchemaVersion>

<ProjectGuid>{0F34CE5D-2AB0-49A9-8254-B21D1D2EFFA1}</ProjectGuid>

<OutputType>WinExe</OutputType>

<AppDesignerFolder>Properties</AppDesignerFolder>

<RootNamespace>WindowsApplication1</RootNamespace>

<AssemblyName>WindowsApplication1</AssemblyName>

</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

<DebugSymbols>true</DebugSymbols>

<DebugType>full</DebugType>

<Optimize>false</Optimize>

<OutputPath>bin\Debug\</OutputPath>

<DefineConstants>DEBUG;TRACE</DefineConstants>

<ErrorReport>prompt</ErrorReport>

<WarningLevel>4</WarningLevel>

</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

<DebugType>pdbonly</DebugType>

<Optimize>true</Optimize>

<OutputPath>bin\Release\</OutputPath>

<DefineConstants>TRACE</DefineConstants>

<ErrorReport>prompt</ErrorReport>

<WarningLevel>4</WarningLevel>

</PropertyGroup>

<ItemGroup>

<Reference Include="System" />

<Reference Include="System.Data" />

<Reference Include="System.Deployment" />

<Reference Include="System.Drawing" />

<Reference Include="System.Windows.Forms" />

<Reference Include="System.Xml" />

</ItemGroup>

<ItemGroup>

<Compile Include="Form1.cs">

<SubType>Form</SubType>

</Compile>

<Compile Include="Form1.Designer.cs">

<DependentUpon>Form1.cs</DependentUpon>

</Compile>

<Compile Include="Program.cs" />

<Compile Include="Properties\AssemblyInfo.cs" />

<EmbeddedResource Include="Properties\Resources.resx">

<Generator>ResXFileCodeGenerator</Generator>

<LastGenOutput>Resources.Designer.cs</LastGenOutput>

<SubType>Designer</SubType>

</EmbeddedResource>

<Compile Include="Properties\Resources.Designer.cs">

<AutoGen>True</AutoGen>

<DependentUpon>Resources.resx</DependentUpon>

</Compile>

<None Include="Properties\Settings.settings">

<Generator>SettingsSingleFileGenerator</Generator>

<LastGenOutput>Settings.Designer.cs</LastGenOutput>

</None>

<Compile Include="Properties\Settings.Designer.cs">

<AutoGen>True</AutoGen>

<DependentUpon>Settings.settings</DependentUpon>

<DesignTimeSharedInput>True</DesignTimeSharedInput>

</Compile>

</ItemGroup>

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

Other similar extension points exist, see Microsoft.Common.targets.

<Target Name="BeforeBuild">

</Target>

<Target Name="AfterBuild">

</Target>

-->

</Project>

msbuild | Visual Studio Wednesday, February 20, 2008 12:40:11 AM (GMT Standard Time, UTC+00:00)  #     | 
Friday, February 15, 2008

Blog test from Word 2007

This is a test from creating a post using Word 2007. Thanks to dasBloggingWithWord2007.aspx">Colin Neller for the detailed information for making Word 2007 work with dasBlog. Here is an image

 

Sayed Ibrahim Hashimi

msbuild Friday, February 15, 2008 7:10:42 PM (GMT Standard Time, UTC+00:00)  #     |