- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Saturday, October 27, 2012

MSBuild: how to set the configuration property

When you are building a .sln file or a .csproj/.vbproj/etc you need to be careful how you set the Configuration property. Once this property is set you do not want to change the value of it. Because of this it is best to specify this as a global property. To do that you can pass it in on the command line. If you are using the MSBuild task then you can pass it in via Properties or AdditionalProperties. What you do not want to do is to set this value inside your .csproj/.vbproj or any imported file. The only time when this is OK if there is a condition which will not set the property if it’s already been set. This is exactly how the default .csproj/.vbproj files are authored:

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

If you do set this property w/o the conditional not to overwrite an existing value you may experience really strange behavior during your builds. And it may be difficult to diagnose the issue as well.

Now let me explain why you cannot do this. When you build a .csproj/.vbproj MSBuild will start creating an in-memory representation of the entire project. It will start with the .csproj/.vbproj file itself. It will read the file from top to bottom. When a property is encountered it is evaluated. If a property is encountered that relies on another one, for example

<IntermediateOutputPath Condition=" '$(PlatformName)' == 'AnyCPU' ">$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>

The properties inside the expression will be evaluated on whatever values exist for the properties at the time. In this case ItermediateOutputPath will be the value of BaseIntermediateOutputPath and Configuration.

If a property is encountered which specifies a value for a property which has been previously declared, the previous value will be discarded.

The implications of this are subtle but very simple; once a property has been set which has dependent properties you must not overwrite that property. This is because when a dependent property is encountered it is evaluated immediately. You cannot re-evaluate that property. So if you set a property during your build, some existing dependent properties which were evaluated before the value change will continue to use the old value. There is no way to re-evaluate those properties.

Now lets see how this relates to the Configuration property specifically.

The contents of the .csproj/.vbproj are properties/items along with an import to Microsoft.csharp.targets (Microsoft.VisualBasic.targets for .vbproj) which then imports Microsoft.Common.targets. If you look in Microsoft.common.targets you will see many different properties which are declared using $(Configuration). So this means that you must treat the Configuration property with care and abide by the rule that I have outlined above.

FYI If you want more details on this I have explained this entire process in great detail in my book Inside MSBuild and Team Build.

Configuration for web publishing

If you have used the new web publish experience in Visual Studio 2012 (also available for VS2010 from the Azure SDK) you may have noticed that the web publish profiles are MSBuild files. They are stored under Properties\PublishProfiles (My Project\PublishProfiles for VB) and have extensions of .pubxml. In that file (or in a .pubxml.user file associated with it) you will find a property defined as

<LastUsedBuildConfiguration>Release</LastUsedBulidConfiguration>

This value corresponds to the value for the Configuration drop down in the VS web publish dialog. When you kick off a publish in VS we use this value and kick off a build and specify Configuration as a global property which is passed in. The reason why we did not name this property Configuration is because the web publish profile is imported into the web project itself during the publish process. By the file being imported you can natively access all the properties/items of the .csproj/.vbproj and also easily extend the build process. Since that is the case we cannot set the Configuration property because we know it’s value has already been set. Due to this when publishing from the command line you must specify the Configuration property.

Sayed Ibrahim Hashimi | @SayedIHashimi

msbuild | MSDeploy | Visual Studio | Visual Studio 2008 | Visual Studio 2010 Saturday, October 27, 2012 3:49:02 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, August 13, 2009

MSBuild: Executing MSTest Unit Tests

I have seen a couple blog entries about executing MSTest unit tests from MSBuild. Most recently I saw the entry by Scott A. Lawrence. So I decided to share how I execute MSTest unit tests from MSBuild

I created a file named Build.Common.UnitTest.targets which contains all the behavior that will execute the test cases. This file can then be imported into whatever scripts that need to execute test cases. The entire file is shown below. We will discuss afterwards.

Build.Common.UnitTest.targets

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

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

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

    TESTING

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

 

  <!-- Default build settings go here -->

  <PropertyGroup>

    <RunMSTest Condition="'$(RunMSTest)'==''">true</RunMSTest>

    <BuildInParallel Condition="'$(BuildInParallel)'==''">true</BuildInParallel>

  </PropertyGroup>

 

  <Target Name="MSTestValidateSettings">

    <!-- Cleare out these items -->

    <ItemGroup>

      <_RequiredProperties Remove="@(_RequiredProperties)"/>

      <_RequiredItems Remove="@(_RequiredItems)"/>

    </ItemGroup>

   

    <ItemGroup>

      <_RequiredProperties Include="BuildContribRoot">

        <Value>$(BuildContribRoot)</Value>

      </_RequiredProperties>

      <_RequiredProperties Include="OutputRoot">

        <Value>$(OutputRoot)</Value>

      </_RequiredProperties>

 

      <_RequiredItems Include="MSTestProjects">

        <RequiredValue>@(MSTestProjects)</RequiredValue>

        <RequiredFilePath>%(MSTestProjects.FullPath)</RequiredFilePath>

      </_RequiredItems>

      <_RequiredItems Include="AllConfigurations">

        <RequiredValue>@(AllConfigurations)</RequiredValue>

      </_RequiredItems>

      <_RequiredItems Include="AllConfigurations.Configuration">

        <RequiredValue>%(AllConfigurations.Configuration)</RequiredValue>

      </_RequiredItems>

    </ItemGroup>

   

    <!-- Raise an error if any value in _RequiredProperties is missing -->

    <Error Condition="'%(_RequiredProperties.Value)'==''"

           Text="Missing required property [%(_RequiredProperties.Identity)]"/>

 

    <!-- Raise an error if any value in _RequiredItems is empty -->

    <Error Condition="'%(_RequiredItems.RequiredValue)'==''"

           Text="Missing required item value [%(_RequiredItems.Identity)]" />

 

    <!-- Validate any file/directory that should exist -->

    <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"

           Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />

  </Target>

 

  <UsingTask

      TaskName="TestToolsTask"

      AssemblyFile="$(BuildContribRoot)TestToolsTask-1.3\Microsoft.VisualStudio.QualityTools.MSBuildTasks.dll"/>

 

  <!-- TODO: Create a ValidateTestSettings target and put it on this list -->

  <PropertyGroup>

    <MSTestDependsOn>

      BuildMSTestProjects;

      BeforeMSTest;

      CoreMSTest;

      AfterMSTest

      $(MSTestDependsOn);

    </MSTestDependsOn>

  </PropertyGroup>

  <Target Name="MSTest" DependsOnTargets="$(MSTestDependsOn)"/>

  <Target Name="BeforeMSTest"/>

  <Target Name="AfterMSTest"/>

  <Target Name="CoreMSTest" Outputs="%(MSTestProjects.Identity)">

    <Message Text="Running MSTest for project [%(MSTestProjects.Identity)]"/>

    <Message Text="MSTestProjects.Directory: %(MSTestProjects.RootDir)%(MSTestProjects.Directory)" />

 

    <PropertyGroup>

      <_CurrentConfig>Debug</_CurrentConfig>

    </PropertyGroup>

 

    <PropertyGroup>

      <_SearchPath>$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\</_SearchPath>

      <_TestContainers></_TestContainers>

    </PropertyGroup>

 

    <TestToolsTask

      SearchPathRoot="%(MSTestProjects.RootDir)%(MSTestProjects.Directory)"

      TestContainers="$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\bin\%(MSTestProjects.Filename).dll"/>

 

    <!-- TODO: Read in the results of the tests and get all failures and report them here -->

  </Target>

 

  <PropertyGroup>

    <BuildMSTestProjectsDependsOn>

      BeforeBuildMSTestProjects;

      CoreBuildMSTestProjects;

      AfterBuildMSTestProjects;

      $(BuildMSTestProjectsDependsOn);

    </BuildMSTestProjectsDependsOn>

  </PropertyGroup>

  <Target Name="BuildMSTestProjects" DependsOnTargets="$(BuildMSTestProjectsDependsOn)"/>

  <Target Name="BeforeBuildMSTestProjects"/>

  <Target Name="AfterBuildMSTestProjects"/>

  <Target Name="CoreBuildMSTestProjects" Outputs="%(AllConfigurations.Configuration)"

          DependsOnTargets="CleanMSTest">   

    <!-- Make sure to do clean build in case test cases were added -->

   

    <PropertyGroup>

      <_CurrentConfig>%(AllConfigurations.Configuration)</_CurrentConfig>

    </PropertyGroup>

 

    <Message Text="Building (MSTestProjects.Identity) %(MSTestProjects.Identity)" Importance="high"/>

   

    <!-- Build the projects here. -->

    <MSBuild Projects="%(MSTestProjects.Identity)"

             Properties="Configuration=$(_CurrentConfig);

                          OutputPath=$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\bin\;

                          BaseIntermediateOutputPath=$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\obj\;

                          GenerateResourceNeverLockTypeAssemblies=true;

                          %(ProjectsToBuild.Properties);

                          $(AllProjectProperties);"

             BuildInParallel="$(BuildInParallel)"

             >

    </MSBuild>

  </Target>

 

 

  <Target Name="CleanMSTest">

 

    <MSBuild Projects="@(MSTestProjects)"

         Targets="Clean"

         Properties="Configuration=$(_CurrentConfig);

                          OutputPath=$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\bin\;

                          BaseIntermediateOutputPath=$(OutputRoot)$(_CurrentConfig)\%(MSTestProjects.Filename)\obj\;

                          GenerateResourceNeverLockTypeAssemblies=true;"

         BuildInParallel="$(BuildInParallel)"

             />

   

  </Target>

 

</Project>

There are five important targets which are described below.

Name

Description

MSTestValidateSettings

This validates that the file was provided the needed data values to perform its task, to run the unit tests. For more info on this technique see my previous entry Elements of Reusable MSBuild Scripts: Validation.

MSTest

This is the target that you would execute to run the test cases. The target itself is empty but it sets up the chain of dependent targets.

CoreMSTest

This is the target which executes the test cases. This is preformed using the TestToolsTask.

BuildMSTestProjects

This target is responsible for building (i.e. compiling) the projects which contain the test cases. You don't have to call this it is called automagically.

CleanMSTest

This target will execute the Clean target for all the test projects defined.

 

If you take a look at the CoreBuildMSTestProjects target you can see that I am batching (Target batching to be specific) it on each defined configuration. This is achieved with the attribute Outputs="%(AllConfigurations.Configuration)". If you are not familiar with batching, see the links at the end of this post for more details, and you can always grab my book for even more detailed info J. Then inside that target I build each project by batching (Task batching) the MSBuild task on each project defined in the MSTestProjects item list.

Then inside the CoreMSTest target I execute the test cases. This target is batched for every value in the MSTestProjects item. As I'm writing this I have noticed that I've hard-coded the value for the configuration used in that target to be Debug with the statement

<PropertyGroup>

  <_CurrentConfig>Debug</_CurrentConfig>

</PropertyGroup>

This shouldn't be hard coded, but passed in. I will leave it as is for now though. Then the TestToolsTask is invoked to execute the test cases.

Now that we have written the re-usable .targets file to execute the test cases we need to create a file which will "feed" it the necessary data values and let it do its magic. I created a sample solution, which you can download at the end of this post, which demonstrates its usage. The solution is named MSTestExample and you can see the files it contains in the screen shot below.

Here I've highlighted the two MSTest projects as well as a couple build files. I've already shown the contents of the Build.Common.UnitTest.targets file. Here is the contents of the Build.MSTestExample.proj file.

Build.MSTestExample.proj

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

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

 

  <!--

  Some properties you may be interested in setting:

    Name              Description

    *****************************************************

    RunMSTest         true/false to run unit tests or not

  -->

 

  <PropertyGroup>

    <Root Condition="'$(Root)'==''">$(MSBuildProjectDirectory)\..\</Root>

 

    <BuildRoot Condition="'$(BuildRoot)'==''">$(Root)Build\</BuildRoot>

    <BuildContribRoot Condition="'$(BuildContribRoot)'==''">$(BuildRoot)Contrib\</BuildContribRoot>

 

    <SourceRoot Condition="'$(SourceRoot)'==''">$(Root)</SourceRoot>

    <BuildArtifactsRoot Condition="'$(BuildArtifactsRoot)'==''">$(BuildRoot)BuildAftifacts\</BuildArtifactsRoot>

    <OutputRoot Condition="'$(OutputRoot)'==''">$(BuildArtifactsRoot)Output\</OutputRoot>

    <TreatWarningsAsErrors Condition=" '$(TreatWarningsAsErrors)'=='' ">true</TreatWarningsAsErrors>

  </PropertyGroup>

 

  <PropertyGroup>

    <CodeAnalysisTreatWarningsAsErrors Condition="'$(CodeAnalysisTreatWarningsAsErrors)'==''">true</CodeAnalysisTreatWarningsAsErrors>

    <BuildInParallel Condition="'$(BuildInParallel)'==''">true</BuildInParallel>

   

    <RunCodeAnalysis Condition="'$(RunCodeAnalysis)'==''">true</RunCodeAnalysis>

    <RunMSTest Condition="'$(RunMSTest)'==''">false</RunMSTest>

    <RunStyleCop Condition="''=='$(RunStyleCop)'">true</RunStyleCop>

  </PropertyGroup>

 

  <!-- Configurations that we want to build for -->

  <ItemGroup>

    <AllConfigurations Include="Debug">

      <Configuration>Debug</Configuration>

    </AllConfigurations>

  </ItemGroup>

 

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

    MSTest

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

 

  <ItemGroup>

    <MSTestProjects Include="$(SourceRoot)TestProject1\TestProject1.csproj">

    </MSTestProjects>

    <MSTestProjects Include="$(SourceRoot)TestProject2\TestProject2.csproj">

    </MSTestProjects>

  </ItemGroup>

 

  <Import Project="$(BuildRoot)Build.Common.UnitTest.targets"/>

 

  <!-- Inject the MSTest targets into the build -->

  <!--<PropertyGroup Condition="'$(RunMSTest)'=='true'">

    <BuildDependsOn>

      $(BuildDependsOn);

      MSTest;

    </BuildDependsOn>

    <BuildMSTestProjectsDependsOn>

      CoreBuild;

      $(BuildMSTestProjectsDependsOn)

    </BuildMSTestProjectsDependsOn>

  </PropertyGroup>-->

 

</Project>

This file is pretty simple. It just creates some properties, and items and then just imports the Build.Common.UnitTest.targets file to do all the heavy lifting. You will probably notice that there are some properties defined that don't make sense here, like CodeAnalysisTreatWarningsAsErrors, this is because this was taken from a build script which does some other tasks. You can ignore those. To see what properties/items are required for the MSTest just look at the MSTestValidateSettings target. Also in the previous code snippet I showed how you could inject the MSTest target into the build process but it is commented out since there is no real build process in this case.

One thing about this approach that may not be ideal is that it will execute the test cases in one assembly at a time and if there is a failure it will not go on to the other assemblies. In my case this is OK because this is for local builds, public builds are executing test cases by Team Build, but this can be looked at.

Questions? Comments?

 

MSTestExamples.zip

MSBuild Batching Links


Sayed Ibrahim Hashim

batching | msbuild | Visual Studio | Visual Studio 2008 Thursday, August 13, 2009 5:11:52 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, June 02, 2009

Speaking at Jaxdug Wednesday June 3, 2009

This coming Wednesday I will be at the Jacksonville Developer User Group. The topic title is "Leveraging Web Deployment Projects". I am still thinking about a new title, I'm not extremely happy about that one. Here is the description about the talk.

In this session we will take a look at how Web Deployment Projects can be used to assist in the deployment of web sites and web applications; including ASP.NET Web Applications and ASP.NET MVC Web Applications. We will give an overview of what Web Deployment Projects are and the functionality that is available out of the box. A Web Deployment Project is a wrapper for the aspnet_compiler.exe tool in the form of an MSBuild project and adds value to using the tool itself. Because they are MSBuild files we are able to customize and extend the process. We will discuss how we can customize the process to perform common steps such as

  1. Creating Virtual Directories
  2. Updating values in the web.config file
  3. Encrypting the web.config file
  4. Minimizing JavaScript files
  5. Versioning the Assemblies

In this session we will not be covering MSBuild itself, so I will not go into too much detail about MSBuild specifics. More to be discussed is how you can take advantage of Web Deployment Projects and how that build process can be extended and customized.

 

Sayed Ibrahim Hashimi

ASP.NET MVC | msbuild | Visual Studio 2008 | speaking Tuesday, June 02, 2009 4:25:06 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, May 26, 2009

Visual Studio 2010: When *Might* it be Available

Before I begin this post I must say that I do not have any inside information about the release of Visual Studio 2010 or .NET 4. The post is composed of 100 % speculation and based on information that has already been made publicly available.

I am pretty interested to know the release date for Visual Studio 2010, and .NET Framework 4.0, so I asked around but no one could tell me anything. So I decided to do some research as to the release dates for Visual Studio 2005 and Visual Studio 2008 based on the release of their Beta releases. I came up with the following table.

 

Beta 1

Beta 2

RTM

Beta 1 - Beta 2

Beta 2 - RTM

Visual Studio 2005

06/29/2004

04/18/2005

11/07/2005

43 Weeks

29 Weeks

Visual Studio 2008

04/19/2007

07/26/2007

11/19/2007

14 Weeks

17 Weeks

Visual Studio 2010

05/18/2009

11/30/2009

05/09/2010

28 Weeks

20 Weeks

Yellow cells are 100% based just on the averages of the two previous versions

 

When Visual Studio 2005 came out there were huge changes. I think this is why the amount of time between Beta 1 and Beta 2, and between Beta 2 and RTM were so much longer then their corresponding periods in Visual Studio 2008. I think for the release of Visual Studio 2010 we are going to be somewhere between the two time periods. So maybe a decent indicator would simply be the average of the two. This is what I used to come up with the cells that are shaded in yellow. I'm sure this will be off but hopefully by weeks and not months!

   

Sayed Ibrahim Hashimi

msbuild | Visual Studio | Visual Studio 2008 Tuesday, May 26, 2009 4:24:49 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, April 29, 2009

MSBuild: Properties and AdditionalProperties Known Metadata

With MSBuild 3.5 two new known metadata names were introduced for items passed to the MSBuild Task. Those known metadata names are Properties and AdditionalProperties. The idea is that you can now specify properties on the item itself instead of having to pass all the properties using the Properties attribute on the MSBuild Task. For example you could define an item, Projects, to be:

<ItemGroup>

    <Projects Include="Project01.proj">

        <Properties>Configuration=Release</Properties>

    </Projects>

</ItemGroup>
Then you can build the values contained in the Projects item using the MSBuild task:

<Target Name="BuildProjects">

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

</Target>

 

Like I said previously there are two new ways to pass properties in item metadata, Properties and AdditionalProperties. The difference can be confusing and very problematic if used incorrectly. Admittedly I didn't know the difference until about 6 months ago (but soon enough to include in my book J ). The difference is that if you specify properties using the Properties metadata then any properties defined using the Properties attribute on the MSBuild Task will be ignored. In contrast to that if you use the AdditionalProperties metadata then both values will be used, with a preference going to the AdditionalProperties values.

Now let's take a look at an example to make this clear. The following MSBuild file is named Properties.proj and is shown below.

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

 

<PropertyGroup>

    <ProjectPath>Project01.proj</ProjectPath>

</PropertyGroup>

 

<ItemGroup>

     <_ProjectToBuild_Properties Include="$(ProjectPath)">

         <Properties>Configuration=Debug</Properties>

    </_ProjectToBuild_Properties>

</ItemGroup>

<ItemGroup>

    <_ProjectToBuild_AdditionalProperties Include="$(ProjectPath)">

        <AdditionalProperties>Configuration=Debug</AdditionalProperties>

    </_ProjectToBuild_AdditionalProperties>

</ItemGroup>

 

<Target Name="Build_Properties">

    <Message Text="Building project with Properties metadata" Importance="high"/>

    <MSBuild Projects="@(_ProjectToBuild_Properties)" Properties="DebugSymbols=true" />

</Target>

 

<Target Name="Build_AProperties">

    <Message Text="Building project with AdditionalProperties metadata" Importance="high"/>

    <MSBuild Projects="@(_ProjectToBuild_AdditionalProperties)" Properties="DebugSymbols=true" />

</Target>

 

</Project>

I've highlighted a few important elements contained in this file. Firstly if you do not specify your ToolsVersion to be 3.5 then none of this functionality will work. Make sure to always provide this value, or you may be baffled by the un-expected behavior.

Inside of both targets you can see that I and providing the value Properties="DebugSymbols=true". Below is the definition of the file that it is building, the Project01.proj file.

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

 

<Target Name="Build">

    <Message Text="Building $(MSBuildProjectFile)" Importance="high"/>

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

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

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

</Target>

 

</Project>

This file just prints out the values for a couple of properties that are not defined inside the file itself. Now let's see the behavior when we execute both of these targets. The result is shown in the image below.

As I've highlighted here the result of executing the Build_Properties target results in the attribute Properties="DebugSymbols=true" being completely ignored. The result from the Build_AProperties target takes the Properties attribute into consideration as well as the AdditionalProperties metadata. Don't use both of these together!

I personally prefer using the AdditionalProperties because I think that ignoring the Properties attribute can be confusing and hard to diagnose. This is why I tend to use that approach instead of the Properties metadata unless I have a specific reason.

Sayed Ibrahim Hashimi

 

 

 

msbuild | Visual Studio 2008 Wednesday, April 29, 2009 5:04:17 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, February 04, 2009

Comments about reusable MSBuild files

A reader of this blog asked me to review some reusable MSBuild file that he created. I won't reveal the details of his files here but here are my comments which are relevant to all reusable MSBuild files.

Properties in targets files

For targets file (MSBuild files designed to be imported by other files) if possible you should always define properties using conditions to ensure that the consumer hasn't alredy defined that value. See my new article MSBuild Best Practices (http://msdn.microsoft.com/en-us/magazine/dd419659.aspx) specifically the section titled "Defining Dynamic Items and Properties".

Assumptions in targets files

Your ProjectOutputHelper.targets file overrides the BuildDependsOn property. This is good and I am sure that it works great, but I don't agree 100% with this approach. You are assuming that the consumers of the file will be importing the Microsoft.Common.targets file which is most likely true, but in my opinion is a bad practice. Better would be to allow consumers of the file to extend the BuildDependsOn. See my new book Inside the Microsoft Build Engine in Chapter 7 you will find a section ‘Creating Reusable Build Elements’ which define some rules for targets files, this is explained more there.

Extendable targets should declare dependencies in a property

All non-internal targets contained inside of targets files should declare the target dependencies inside of a property. This allows consumers to inject steps into specific areas of the build process. For instance from Microsoft.Common.targets:

<PropertyGroup>

    <BuildDependsOn>

        BeforeBuild;

        CoreBuild;

        AfterBuild

    </BuildDependsOn>

</PropertyGroup>

<Target

    Name="Build"

    Condition=" '$(_InvalidConfigurationWarning)' != 'true' "

    DependsOnTargets="$(BuildDependsOn)"

    Outputs="$(TargetPath)"/>

Design incremental targets

If possible you should design your targets to build incrementally ( Chapter 6 of my book explains this) so that the targets that are already up-to-date don’t have to be rebuilt. It is good to get into the habit of creating targets that build incrementally.

Use MSBuild 3.5 Syntax

If you are building with MSBuild 3.5 (.NET 3.5) you should use the new ItemGroup and PropertyGroup elements inside of the targets instead of the CreateProperty and CreateItem task.

msbuild | Visual Studio 2008 Wednesday, February 04, 2009 5:06:59 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, February 02, 2009

Microsoft Press Blog Posting

The The Microsoft Press team has posted a blog about my book & upcoming articles, you can read it at All Sayed all the time (and MSBuild). These guys make it seem like I'm actually doing something, when in reality I've been on vacation for the past 5 weeks!

Sayed Ibrahim Hashimi

msbuild | Visual Studio 2008 Monday, February 02, 2009 4:17:35 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, November 10, 2008

Team Build - Creating Delta Builds

Out of the box Team Build does not support Delta Builds. Thomas Janssen has a detailed post Realizing Delta Builds with VSTS Team Build for Delta Deployments which describes how this can be achieved. I have not had the opportunity to try this myself, but the blog post looks very through and informative. You can also download his DeltaBuild.targets file from his blog. I have the following comments relating to this; perhaps he will upgrade this file to reflect these comments.

This guidance is appropriate for target files which are designed to be consumed by others.

Don't override targets like AfterCompileSolution

Don't override targets like AfterCompileSolution. Instead extend the CompileSolutionDependsOn property in the manner:

<PropertyGroup>

<CompileSolutionDependsOn>

$(CompileSolutionDependsOn);

DeltaAfterCompileSolution

</CompileSolutionDependsOn>

</PropertyGroup>

This is because some consumers of this file may have already defined the AfterCompileSolution target. In which case one of them will be overridden.

Notice that I named the new target DeltaAfterCompileSolution. It is a best practice to prefix your targets with a value that should be unique. This will minimize the chances of your targets colliding with those defined by others. For example this could have been named MyAfterCompileSolution, but I bet there are a bunch of these already defined.

DependsOnTargets should always be taken from a property

You should define the DependsOnTargets value inside of a property, just like the MSFT targets files do. For example your DeltaAfterCompileSolution (after being renamed) would look like this:

<PropertyGroup>

<DeltaAfterCompileSolution>

$(DeltaAfterCompileSolution);

SafeCleanBuildResult;

GetLatestVersion;

CollectIncrementalBuildResult;

CreateDeltaBuildResult

</DeltaAfterCompileSolution>

</PropertyGroup>

<Target Name="DeltaAfterCompileSolution"

DependsOnTargets="$(DeltaAfterCompileSolution)">

</Target>

This is because you want users to be able to extend this process similar to how users extend the built-in build process. Without this they may need to modify your file, which is ill-advised because you may make a new release at some point.
Also notice the usage of the $(DeltaAfterCompileSolution) in the declaration of the DeltaAfterCompileSolution property. This is to
preserve any previous values that the user may have defined.

 

On a side note, if you are looking for more detailed information regarding Team Build, there will be three chapters of my new book Inside the Microsoft® Build Engine: Mastering MSBuild and Team Build which will be published in January 2009. Sample chapters will be available at that link shortly.

Thanks,

Sayed Ibrahim Hashimi

Team Build | Team Build | Visual Studio 2008 Monday, November 10, 2008 4:43:46 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, September 08, 2008

MSBuild: Exec Task

I was looking at the documentation for the Exec task (yes I'm writing a new MSBuild book!) and discovered that it is lacking. So I decided to post the complete list of properties here. So here it is for those of you using it.

Name

Description 

Command 

The command which is to be executed. This is the only required parameter.

CustomErrorRegularExpression *

If provided this will be the regex used to determine if an error occurred.

CustomWarningRegularExpression *

If provided this will be the regex used to determine that a warning occurred.

ExitCode

Output property containing the exit code provided by the executed command.

IgnoreExitCode

If true then the Exec task will not fail the build based on the exit code. Otherwise the build is failed for any non-zero exit code.

IgnoreStandardErrorWarningFormat *

If true the output is not examined for errors and warnings.

Outputs

An input/output parameter that contains the output items from the task. This is not set by the Exec task itself but made available to be set by the consumer.

StdErrEncoding

An input/output parameter that specifies the encoding that is used for the standard error stream.

StdOutEncoding

An input/output parameter that specifies the encoding that is used for the standard output stream.

Timeout

Specifies the timout, in milliseconds for the command. After the specified amount of time has passed the command will be terminated. By default there is no timeout

ToolPath

Specifies the location of the tool.

WorkingDirectory 

Specifies the working directory.

* Denotes new properties in MSBuild 3.5

There is a slightly new behavior in this version of the task relating to detecting errors & warnings from the output of the command. Take a look at the forum entry Exec task and "error :" in output.

More to come on the new book later keep an eye here soon. If you have ideas about specific examples that should be demonstrated let me know, for example; how to zip a set of file, how to ftp files to a server, your example here, etc.

Sayed Ibrahim Hashimi

msbuild | Visual Studio 2008 Monday, September 08, 2008 5:23:19 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, July 30, 2008

IList Randomize Extension Method

I looked around for an extension method that would just randomize a list. I found a couple but they either didn't seem to work right or the modified the collection itself instead of creating a new collection and returning that. So I created one, it is pretty simple anywayz. The definition for it is shown below.

public static class IListExtensions
{
    public static IList<T> Randomize<T>(this IList<T> input)
    {
        if (input == null)
        { throw new ArgumentException("input"); }

        var test = (from p in input
                    select new { Id = rand.Next(), ListObject = p }).OrderBy(t => t.Id);

        IList<T> randomList = new List<T>();
        foreach (var item in test)
        {
            randomList.Add(item.ListObject);
        }

        return randomList;
    }

    static Random rand = new Random();
}

From here we use this method just like any other IList method, an example is shown below.

public void SampleRandomize()
{
    int numElemnets = 10;
    IList<int> numList = new List<int>();
    for (int i = 0; i < numElemnets; i++)
    {
        numList.Add(i);
    }

    IList<int> radnomList = numList.Randomize();

    for (int i = 0; i < randomList.Count; i++)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("i: {0}", randomList[i]));
    }
}

I think it's pretty cool how you can create methods that can be soo easily used thanks to extension methods.


Sayed Ibrahim Hashimi

Visual Studio 2008 | CSharp3 Wednesday, July 30, 2008 6:31:15 AM (GMT Daylight Time, UTC+01:00)  #     | 
Monday, February 11, 2008

Silverlight: ErrorCode 1001

Recently I ran into an error when creating a simple Silverlight, Silverfish as I like to call it. The error was in occurring in the constructor of the Silverlight control that I created. Specifically it occurred when the content was read from the resource with the following statement

this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());

Once this was executed an Exception was thrown with the following message

Error HRESULT E_FAIL has been returned from a call to a COM component.

And the dialog shown below was displayed

I Googled this error for a bit and it seemed like there were various errors that could cause this. So I started playing with the XAML that it was attempting to load. I discovered that the issue was caused by an Element with the same Name attribute as a previously declared one. So if you get this error make sure that all your Elements have unique names attached to them.

 

Sayed Ibrahim Hashimi

Silverlight | Visual Studio 2008 Monday, February 11, 2008 5:05:26 AM (GMT Standard Time, UTC+00:00)  #     |