Welcome to MSDN Blogs Sign in | Join | Help

I've mentioned the CompilationOutputs item group we added in TFS 2008 before in passing (see this post, for example), but never given it the attention it deserves...  This item group is built up over the course of the Compile / CompileConfiguration / CompileSolution targets, and by the end of the build it contains the full list of outputs generated by all the items in your SolutionToBuild item group.  Furthermore, it includes metadata that allows you to distinguish the outputs for each platform/configuration pair and for each individual solution.  For example, given solutions Foo.sln and Bar.sln and configurations x86|Debug and x86|Retail, the following target:

  <Target Name="AfterCompile">
    <Message Text="%(CompilationOutputs.Solution) built %(Identity) for %(Platform)|%(Configuration)." /> 
  </Target>

...might produce the following output:

  C:\BuildLocation\TeamProject\Definition\Sources\Foo.sln built C:\BuildLocation\TeamProject\Definition\Binaries\x86\Debug\Foo.exe for x86|Debug.
  C:\BuildLocation\TeamProject\Definition\Sources\Bar.sln built C:\BuildLocation\TeamProject\Definition\Binaries\x86\Debug\Bar.exe for x86|Debug.
  C:\BuildLocation\TeamProject\Definition\Sources\Foo.sln built C:\BuildLocation\TeamProject\Definition\Binaries\x86\Release\Foo.exe for x86|Release.
  C:\BuildLocation\TeamProject\Definition\Sources\Bar.sln built C:\BuildLocation\TeamProject\Definition\Binaries\x86\Release\Bar.exe for x86|Release.

Of course, producing that output is not particularly exciting.  It is easy to imagine lots of other cool things that can be done with this information, however, including copying all build outputs to the drop location without putting them in the default build location (where they all end up in a single folder).  I'm sure users of Team Build will come up with lots of other creative uses for this item group as well.

A fair number of people seem to want to use Team Build to kick off their own pre-existing build scripts that have nothing in common with the process defined in Microsoft.TeamFoundation.Build.targets.  While we don't typically encourage this, enough people want to do it that I figured I should finally post an example.  So - the minimal tfsbuild.proj file that will kick off your custom build script would look something like this:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
  <Target Name="EndToEndIteration">
    <Exec Command="SomeScript.cmd" />
  </Target>
</Project>

Of course, most of the Team Build goodness comes from executing the standard targets while having our logger attached, so this approach will not generate a lot of information - no build steps will show up in the build report within VS, it won't keep track of errors/warnings, no data will be pushed to the warehouse (and thus reporting across builds will not work), and so forth.

If you can abstract your custom build script down to the compilation portion, and let the standard build process handle the Get, the Label, etc. you'll be in better shape.  To do this, just override the BeforeCompile target and execute your custom script there, leaving the rest of the TfsBuild.proj file (especially the import of Microsoft.TeamFoundation.Build.targets) alone.

We got a forum post the other day on whether changesets and work items could be associated since the last Successful build.  Some of you may be thinking "Isn't that how it already works?"  Actually, it's not quite how it works.  Changesets and Work Items are, by default, associated since the last "good" build, where "good" means compilation and tests succeeded, but something else may have gone wrong.  That is, the last "good" build may well be marked Partially Succeeded and not Succeeded.

I started responding to the forum post, and then figured it would make a good blog post instead.  (Of course, I've posted a link to this post in the forums...)

So - to modify the default behavior and associate changesets and work items only since the last successful build, you'll need to do three things:

  1. Skip the default logic to associate changesets and work items.  This is pretty simple - just set the SkipGetChangesetsUpdateWorkItems property to true.
  2. Write a custom task to find the last successful build and return its label.
  3. Call this custom task and then call the GenCheckinNotesUpdateWorkItems task with its output.

Lucky for you, I've already written the custom task (or at least a simple version of it).  Here is the source for it:

using System;
using Microsoft.Build.Framework;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Client;

namespace BlogProjects
{
    public class GetLastSuccessfulBuildLabel : ITask
    {
        #region Properties

        [Required]
        public String BuildDefinitionName
        {
            get
            {
                return m_buildDefinitionName;
            }
            set
            {
                m_buildDefinitionName = value;
            }
        }

        [Output]
        public String LastSuccessfulBuildLabel
        {
            get
            {
                return m_lastSuccessfulBuildLabel;
            }
        }

        [Required]
        public String TeamFoundationServerUrl
        {
            get
            {
                return m_teamFoundationServerUrl;
            }
            set
            {
                m_teamFoundationServerUrl = value;
            }
        }

        [Required]
        public String TeamProject
        {
            get
            {
                return m_teamProject;
            }
            set
            {
                m_teamProject = value;
            }
        }

        #endregion

        #region ITask Members

        public bool Execute()
        {
            TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(TeamFoundationServerUrl);
            IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));

            IBuildDetailSpec spec = buildServer.CreateBuildDetailSpec(TeamProject, BuildDefinitionName);
            spec.MaxBuildsPerDefinition = 1;
            spec.Status = BuildStatus.Succeeded;
            spec.QueryOrder = BuildQueryOrder.FinishTimeDescending;

            IBuildQueryResult queryResult = buildServer.QueryBuilds(spec);

            if (queryResult.Builds.Length > 0)
            {
                m_lastSuccessfulBuildLabel = queryResult.Builds[0].LabelName;
            }

            return true;
        }

        public IBuildEngine BuildEngine
        {
            get
            {
                return m_buildEngine;
            }
            set
            {
                m_buildEngine = value;
            }
        }

        public ITaskHost HostObject
        {
            get
            {
                return m_taskHost;
            }
            set
            {
                m_taskHost = value;
            }
        }

        #endregion

        #region Private Members

        private IBuildEngine m_buildEngine;
        private ITaskHost m_taskHost;
        private String m_teamFoundationServerUrl;
        private String m_buildDefinitionName;
        private String m_teamProject;
        private String m_lastSuccessfulBuildLabel;

        #endregion
    }
}

Note the fancy querying that is possible with an IBuildDetailSpec - pretty cool... 

Calling the custom task should be similarly simple.  Something like the following should do the trick:

  <Target Name="BeforeGetChangesetsAndUpdateWorkItems">

    <GetLastSuccessfulBuildLabel TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                                 TeamProject="$(TeamProject)" 
                                 BuildDefinitionName="$(BuildDefinitionName)">
      <Output TaskParameter="LastSuccessfulBuildLabel" PropertyName="LastSuccessfulBuildLabel" />
    </GetLastSuccessfulBuildLabel>

    <GenCheckinNotesUpdateWorkItems TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                                    BuildUri="$(BuildUri)"
                                    BuildNumber="$(BuildNumber)"
                                    CurrentLabel="$(LabelName)@$(LabelScope)"
                                    LastLabel="$(LastSuccessfulBuildLabel)"
                                    UpdateWorkItems="$(UpdateAssociatedWorkItems)"
                                    ContinueOnError="true" />

  </Target>

Hope this helps!

In Orcas, we introduced a generic information storage for builds - internally this is used for all build steps, associated changesets/workitems, etc.  You can use it to attach arbitrary data to a build (and later retrieve it).  Here are a couple of quick code snippets to illustrate these two cases.

To attach a single name/value pair to a build, you would do something like this:

        TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(tfsUrl);
        IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
        IBuildDetail buildDetail = buildServer.GetBuild(buildUri);

        IBuildInformationNode node = buildDetail.Information.CreateNode();

        node.Type = "MyCompany.Custom";
        node.Fields["key"] = "value";

        buildDetail.Information.Save();

To retrieve the same name/value pair from the build, you would do something like this:

        TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(tfsUrl);
        IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
        IBuildDetail buildDetail = buildServer.GetBuild(buildUri);

        List<IBuildInformationNode> nodes = buildDetail.Information.GetNodesByType("MyCompany.Custom");

        if (nodes.Count > 0)
        {
            foreach (IBuildInformationNode node in nodes)
            {
                String value = node["key"];

                // Do something...
            }
        }

Note that the information node storage is hierarchical, so you can get a lot fancier than this if you want/need to!  Note that full documentation of the Orcas TFS Build Object Model can be found here.

The official Orcas RTM OM documentation was posted a while ago, and I neglected to blog about it!  It's available here: http://www.microsoft.com/downloads/details.aspx?FamilyID=6466b53d-d80b-4c31-8f5c-dfb5d32e9411&DisplayLang=en

Make sure to follow the instructions for viewing it after downloading:

Method 1

  1. Double-click the .chm file.
  2. In the Open File-Security Warning dialog box, clear the Always ask before opening this file check box.
  3. Click Open.
Method 2
  1. Right-click the CHM file, and then click Properties.
  2. Click Unblock.
  3. Double-click the .chm file to open the file.

I'm yanking the Beta 2 documentation now that this has been posted...

If you are planning on attending Tech*Ed Developers in sunny Orlando this year, I'll be manning the Visual Studio Team System 2008 Team Foundation Server (Version Control and Build) demo station (quite the name, eh?) for about 22 hours over the course of the four day event.  Please stop by and say hello, ask some questions, provide some feedback, etc.

Other folks at the booth will include my fellow Visual Studio North Carolina folks Ed Hintz (a man of many hats, including Dev Lead for the Version Control Client and Team System Web Access teams and Power Tools guru), and Chris Rathjen (tester extraordinaire on the Admin & Ops team).

Hope to see lots of you there!

As promised, here are some more details on other SP1 changes for TFS Build. 

3. Detect test results.

In Whidbey, a failed test would result in a failed build - builds were either Succeeded or Failed, so there wasn't really much middle ground.  In Orcas we added two new build status properties, CompilationStatus and TestStatus; and a new value for the overall build status - PartiallySucceeded.  The overall status of the build would be Succeeded if an only if there were no errors of any kind during the build; PartiallySucceeded if no errors occurred before or during compilation; and Failed otherwise.  If a test failed, CompilationStatus would be succeeded, TestStatus would be failed, and the overall Status would be partially succeeded.

Many users wanted a bit more control, and in particular wanted a failed test to result in a Failed build.  I posted a workaround that allowed this here, but it was rather hacky - the idea was to set CompilationStatus to failed when a test failed and then let the default logic kick in and fail the build.  In SP1 we introduced a new property - TreatTestFailureAsBuildFailure - that will cause a test failure to fail the build without having to modify CompilationStatus.  Just set this property to true and the new default logic will be to mark the overall status of the build as PartiallySucceeded if no errors occur before or during testing.  That is, if a test fails, CompilationStatus will be succeeded, TestStatus will be failed, and the overall Status will be failed.  Note that partially succeeded builds are still possible when errors occur after successfully running unit tests (e.g. when copying binaries to the drop location).

4. Dynamically created properties.

This one is pretty confusing, and requires an understanding of how the MSBuild task works, an understanding of the Team Build targets file, etc.  I'll describe the problem first, then the solution, and then for anybody who feels like sticking around just a bit about why this problem exists.

The Problem

The first time I heard about this issue, a user was dynamically generating a build number in the BuildNumberOverrideTarget in the standard fashion illustrated here, along with a corresponding version number with which to stamp assemblies.  He was then, however, attempting to actually use that dynamically generated version number during the compilation of his individual solutions.  That is, he had something like:

    <SolutionToBuild Include="SomeSolution.sln">
      <Properties>VersionNumber=$(VersionNumber)</Properties>
    </SolutionToBuild>

...where $(VersionNumber) was getting generated dynamically in BuildNumberOverrideTarget.  Unfortunately, the value getting passed into the individual solutions was the original value of the property - namely, the empty string.  (Note that this same issue would apply if he had attempted to access the VersionNumber property in one of the Before/AfterCompileConfiguration or Before/AfterCompileSolution targets.)

The Solution

The fix is to put all dynamically generated properties into the CustomPropertiesForBuild property (or the CustomPropertiesForClean property if you need to access the property in Before/AfterCleanConfiguration and/or Before/AfterCleanSolution).  For example, the user above could do something like:

  <Target Name="BuildNumberOverrideTarget">
    <BuildNumberGenerator>
      <Output TaskParameter="BuildNumber" PropertyName="BuildNumber" />
      <Output TaskParameter="VersionNumber" PropetyName="VersionNumber" />
    </BuildNumberGenerator>
    <PropertyGroup>
      <CustomPropertiesForBuild>$(CustomPropertiesForBuild);VersionNumber=$(VersionNumber)</CustomPropertiesForBuild>
    </PropertyGroup>
  </Target>

The Description

So what the heck is going on here anyways!?  Well, the issue revolves around the mechanism used by Team Build to support multiproc MSBuild.  In particular, the unit of parallelism in MSBuild is the project, meaning that to build configurations and solutions in parallel Team Build has to use the MSBuild task to call back into TfsBuild.proj for each configuration/solution combination - hence the Compile/CompileConfiguration/CompileSolution targets. 

Furthermore, when a project is invoked with the MSBuild task, all of the environment from the caller (properties, item groups, etc.) is lost - only those properties passed in via the Properties property of the MSBuild task (along with any global properties - see my blog post here for details) are available in the new context.  Since Team Build cannot know the full list of dynamically generated properties, none are passed into these recursive calls to TfsBuild.proj.  Or at least, that's how it used to work...  In SP1, we go ahead and pass the CustomPropertiesForBuild property into the first of these recursive calls, after which any dynamically generated properties (placed into this container) become global and are available for the rest of the chain.

Brian Harry put up a post on the improvements that will be available in the upcoming Team Foundation Server 2008 SP1 release.  Here's some more in depth info on two of the TFS Build changes:

1. Conditionalize builds on the trigger.

There are actually a few changes here...  Essentially we exposed a property on IBuildDetail called Reason that tells you why the build was started.  Reason is an enum of type BuildReason, and can have the following values:

  • Manual.  This indicates that the build was manually started (e.g. by a user through the Queue Build dialog).
  • IndividualCI.  This indicates that the build was started due to a checkin, and that it's build definition is set up to build on each checkin.
  • BatchedCI.  This indicates the the build was started due to one or more checkins, and that it's build definition is set up to accumulate checkins.
  • Schedule.  This indicates that the build was started due to the time, and that it's build definition is set up to build on a regular schedule if changes have occurred.
  • ScheduleForced.  This indicates that the build was started due to the time, and that it's build definition is set up to build on a regular schedule whether or not changes have occurred.

In addition to exposing this new property on IBuildDetail, we have also exposed it as an output property of the GetBuildDetails task and as an MSBuild property available in your TfsBuild.proj files.  This could be useful in a couple situations:

  • You can use it to detect whether a build of an individual definition is running as a result of being triggered by the system or as a result of being started manually by a user.  It may be useful to distinguish these two cases and set various properties to different default values, etc.
  • When pointing more than one build definition at the same TfsBuild.proj file (e.g. so that you can use the same script for your CI build and your Nightly build) you can use it to detect which build definition a particular build is for.  Again, it may be useful to distinguish these two cases.

For example, if you only wanted to generate a custom build number for your nightly build, you could do something like this:

  <Target Name="BuildNumberOverrideTarget" Condition=" '$(Reason)' == 'Schedule' ">
    <MyBuildNumberGenerator TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)">
      <Output TaskParameter="BuildNumber" PropertyName="BuildNumber" />
    </MyBuildNumberGenerator>
  </Target>

2. Reduce build log noise.

In Orcas RTM, we tried to reduce the dependency of TFS Build on the names of particular targets, tasks, events, etc. in MSBuild.  The goal was to make it more usable by people who wanted to radically customize their build process, and in general I think we did a pretty good job here.  Unfortunately, one side effect of the associated changes was the presence of lots of "noise" build steps in the Build Details dialog.  In particular, each project-to-project reference resulted in three build steps of the form:

  • Project 'project' is building project 'dependent project' for targets 'GetTargetPath'.
  • Project 'project' is building project 'dependent project' for targets 'GetNativeManifest'.
  • Project 'project' is building project 'dependent project' for targets 'GetCopyToOutputDirectoryItems'.

These build steps should all magically disappear when Orcas SP1 is installed. 

To accomplish this, we added a property called TargetsNotLogged to the TFS Build targets file that specifies the target names for which build steps should not be added when our logger receives ProjectStarted events, and made the default value of this property 'GetTargetPath;GetNativeManifest;GetCopyToOutputDirectoryItems'.  If you want these build steps back, just set the property to the empty string.  If you want to exclude other targets as well, you can add them to the semicolon delimted list.

 

Next week I'll post on Detect test result and/or Dynamically created properties

In an earlier post I described how one can, in Orcas, preserve the output directory structure used in a standard IDE or desktop build.  It seems that many people are looking for a simple approach, however, to augmenting the standard Team Build output directory by putting the outputs of individual solutions into individual subfolders (I get emails about this pretty regularly).  For this simple case, there is a correspondingly simple solution.  In your TfsBuild.proj file, just replace:

    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../Directory/Solution1/Solution1.sln">
      <Targets></Targets>
      <Properties></Properties>
    </SolutionToBuild>
    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../Directory/Solution2/Solution2.sln">
      <Targets></Targets>
      <Properties></Properties>
    </SolutionToBuild>

...with:

    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../Directory/Solution1/Solution1.sln">
      <Targets></Targets>
      <Properties>OutDir=$(OutDir)Solution1\</Properties>
    </SolutionToBuild>
    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../Directory/Solution2/Solution2.sln">
      <Targets></Targets>
      <Properties>OutDir=$(OutDir)Solution2\</Properties>
    </SolutionToBuild>

Note that this uses the new ability in Team Build 2008 to pass custom properties into each solution when it is built.  As shown in this earlier post, you can also call custom targets for each solution using the Targets metadata also shown in this example.

Recently I got a question about how to compile projects against multiple .NET Frameworks (e.g. 2.0 and 3.5) in Team Build.  MSBuild added support for this sort of thing in the 3.5 framework (they call it multi-targeting), but it is not particularly easy to take advantage of it in Team Build.  Here's the text of my response:

MSBuild added a bunch of different mechanisms for specifying the ToolsVersion (i.e. Framework) to be used when building particular projects.  In particular:

  • You can specify the ToolsVersion at the command-line when invoking msbuild.exe using the /tv option.
  • You can specify the ToolsVersion when invoking the MSBuild task using the ToolsVersion property.
  • You can specify a default ToolsVersion using the ToolsVersion attribute on the <Project> element for a particular project.
  • You can specify a ToolsVersion metadata item when using the MSBuild task to build an item group containing one or more projects – see http://blogs.msdn.com/msbuild/archive/2007/07/17/toolsversion-metadata-for-items-used-in-msbuild-task-s.aspx.

None of these are particularly easy to use in combination with Team Build, unfortunately.  The /tv option will not work, since the Team Build traversal project (TfsBuild.proj + Microsoft.TeamFoundation.Build.targets) uses 3.5 syntax, and will not build using the 2.0 engine.  We don’t expose the ability to set ToolsVersion directly on the MSBuild task when we invoke your solution (we didn’t realize this would be useful, given the existence of the ToolsVersion attribute on the <Project> element).  And we build solutions by invoking the MSBuild task on a property containing the name of your solution, so you cannot use a ToolsVersion metadata item.

The simplest workaround here takes advantage of the properties made available by MSBuild in its generated *.sln.cache files (these are the msbuild projects created to build solutions – they used to be in-memory only, but are now typically saved to disk and reused).  In particular, something like the following should do the trick:

  <ItemGroup>
    <SolutionToBuild Include="SomeSolution.sln">
      <Properties>ProjectToolsVersion=3.5</Properties>
    </SolutionToBuild>
    <SolutionToBuild Include="SomeSolution.sln">
      <Properties>ProjectToolsVersion=2.0</Properties>
    </SolutionToBuild>
  </ItemGroup>

The idea is to build the solution twice, specifying the tools version to be used each time using the ProjectToolsVersion property exposed in the *.sln.cache file. 

There are all kinds of wacky properties exposed in the *.sln.cache files - I highly recommend taking a look at these if you're looking to do anything fancy with individual solutions in Team Build.  If you run msbuild 3.5 from the command-line on a solution, you will find a *.sln.cache file sitting alongside the *.sln file when you are done - open it up in VS (or notepad, if you're not into fancy formatting) and have a look!

In TFS 2008, TfsBuild.proj files can be located anywhere in source control, not just in $/TeamProject/TeamBuildTypes, as was required in TFS 2005.  As a result, we changed the default recursion type used to download files from the configuration folder path (the location of TfsBuild.proj) to RecursionType.OneLevel, meaning that only the contents of the exact directory of TfsBuild.proj are downloaded.  The rationale here is that if you for some reason decided to put your TfsBuild.proj file at $/TeamProject, a fully recursive download would get the entire contents of the repository for that team project, which is probably not what you would want!  In some cases, however, you may wish to do a fully recursive download (if you store custom task assemblies in a subdirectory under your configuration folder path, for example), so we did provide a backdoor for changing this behavior.  In particular, you can set the ConfigurationFolderRecursionType key in TfsBuildService.exe.config on your build machine to "Full" to get full recursion.  Something like:

<add key="ConfigurationFolderRecursionType" value="Full" />

...in the appSettings portion of the config file should do the trick.  Make sure to restart the service after making the change, and the next build should do a fully recursive download.

There are lots and lots of MSBuild properties available to Team Build 2008 build definitions, most of which are probably unknown to the majority of users.  As such, I've tried to compile a comprehensive list of these properties so that they can (hopefully) be more widely used.  I'll do the same thing at some point here for Team Build 2005.  If I've missed anything here or made any mistakes please let me know and I'll try and get them added/fixed.

Extensibility Properties

These are properties that are designed to be overridden by users, and they are already pretty well documented in MSDN here.  I've included the same properties here for comprehensiveness, along with some others not covered by the linked document:

  • AdditionalVCOverrides.  These values are written to the vsprops file generated by Team Build for each C++ project compiled and passed to the Override property of the VCBuild task.
  • BinariesSubdirectory.  The subdirectory under the build agent's working directory to which binaries are redirected during compilation.  Default value is "Binaries".
  • BuildConfigurationsInParallel.  Boolean property which, if true, enables building configurations (e.g. Debug|Any CPU and Release|Any CPU) in parallel, using MSBuild's multi-process capability.  Defaults to true.  Note that for this property to have an effect, the number of processes used by MSBuild, controlled by the MaxProcesses key in TfsBuildService.exe.config, must be set to a number greater than one.
  • BuildlogText.  The text that points to the build log in the work item created on a compilation failure.
  • BuildNumber.  The number of the build being built.  If you wish to override the default value, make sure to do so in the BuildNumberOverrideTarget so that the standard build process logic will update the various properties that depend on the build number (the drop location, label name, etc.) properly.
  • BuildSolutionsInParallel.  Boolean property which, if true, enables building solutions in parallel, using MSBuild's multi-process capability.  Defaults to true.  Note that for this property to have an effect, the number of processes used by MSBuild, controlled by the MaxProcesses key in TfsBuildService.exe.config, must be set to a number greater than one.
  • CleanCompilationOutputOnly.  Do not use this property directly - use IncrementaBuild and/or IncrementalGet instead.
  • CreateWorkspaceTaskComment.  The comment used when Team Build's workspace is created.  Default value is "Workspace created by Team Build".
  • CustomizableOutDir.  Set this property to true to keep Team Build from overriding the OutDir property for each solution/project in the SolutionToBuild item group. 
  • CustomizablePublishDir.  Set this property to true to keep Team Build from overriding the OutDir property for each solution/project in the SolutionToPublish item group.
  • CustomPropertiesForBuild.  Allows properties to be passed into all solutions/projects compiled during the course of the build.
  • CustomPropertiesForClean.  Allows properties to be passed into all solutions/projects cleaned during the course of the build.  Note that the Clean target of individual solutions/projects is only invoked if IncrementalBuild is true.
  • DescriptionText.  The history comment of the work item created on a compilation failure.
  • ErrorWarningLogText.  The text that points to the errors/warnings log file in the work item created on a compilation failure.
  • IncrementalBuild.  A boolean property that specifies whether the build should be full or incremental.  Defaults to false (i.e. a full build). 
  • IncrementalGet.  A boolean property that specifies whether the get operation should be full or incremental.  Defaults to false (i.e. a full get).
  • MSTestRefPath.  The path to the TestToolsTask (contained in Microsoft.VisualStudio.QualityTools.MSBuildTasks.dll).  Overridable so that the 8.0 version can be used for test assemblies compiled against the 8.0 unit test framework - see V8TestToolsTask.
  • RunCodeAnalysis.  Property that controls whether code analysis is executed according to project settings (Default), all the time (Always), or never (Never).
  • RunTest.  Boolean property that controls whether or not tests are executed.
  • SkipClean.  Do not use this property directly - use IncrementalGet and/or IncrementalBuild instead.
  • SkipDropBuild.  Set this property to true to skip the CoreDropBuild target.
  • SkipGet.  Set this property to true to skip the CoreGet target.
  • SkipGetChangesetsUpdateWorkItems.  Set this property to true to skip the association of changesets and work items for successful builds.
  • SkipInitializeWorkspace.  Do not use this property directly - use IncrementalGet and/or IncrementalBuild instead.
  • SkipInvalidConfigurations.  Set this property to false to generate an error rather than a warning when an invalid configuration is present in the ConfigurationToBuild item group for one or more solutions/projects.
  • SkipLabel.  Set this property to true to skip the CoreLabel target.
  • SkipPostBuild.  Do not use this property - use SkipGetChangesetsUpdateWorkItems instead.
  • SkipWorkItemCreation.  Set this property to true to skip the CoreCreateWorkItem target on a compilation failure.
  • SourcesSubdirectory.  The subdirectory under the build agent's working directory to which sources are retrieved from version control.  Default value is "Sources".
  • StopOnFirstFailure.  Set this property to true to stop cleaning, compiling, and/or testing on the first failure encountered.
  • TestResultsSubdirectory.  The subdirectory under the build agent's working directory to which test results are redirected.  Default value is "TestResults".
  • UpdateAssociatedWorkItems.  A boolean value that controls whether associated work items have their "Fixed In" field updated on a successful build.
  • UpdateAssociatedWorkItemsOnBuildBreak.  A boolean value that controls whether associated work items are updated for broken builds.  Default value is false.
  • V8TestToolsTask.  Boolean property which, if true, causes the 8.0 TestToolsTask syntax to be used (the TestNames property was not supported in the 8.0 version of the task).
  • VCBuildAdditionaLibPaths.  Passed to the AdditionalLibPaths property of the VCBuild task for each C++ project compiled.
  • VCBuildAdditionalOptions.  Passed to the AdditionalOptions property of the VCBuild task for each C++ project compiled.
  • VCBuildToolPath.  Passed to the ToolPath property of the VCBuild task for each C++ project compiled.
  • VCBuildUseEnvironment.  Passed to the UseEnvironment property of the VCBuild task for each C++ project compiled.
  • VCOverridesOpen.  The beginning of the vsprops file used to override VCBuild properties - can be overridden to use a different ProjectType version, a different Name, etc.  Default value is an escaped version of:

<?xml version="1.0"?>
<VisualStudioPropertySheet ProjectType="Visual C++" Version="8.00" Name="Team Build Overrides"

  • VCOverridesClose.  The ending of the vsprops file used to override VCBuild properties.  Default value is an escaped version of:

</VisualStudioPropertySheet>

  • WorkItemFieldValue.  The fields and values of the work item created on a compilation failure.
  • WorkItemTitle.  The title of the work item created on a compilation failure.
  • WorkItemType.  The type of work item created on a compilation failure.

Task Behavior Extensibility Properties

These are properties that are designed to be overridden by users to control the behavior of particular tasks (e.g. the Get task).  They are organized by task.

  • Get (for more info, see the tf get docs here)
    • ForceGet.  Passed to the Force property of the Get task.  Use of this property is discouraged - use the IncrementalGet and/or IncrementalBuild helper properties instead.
    • GetOverwrite.  Passed to the Overwrite property of the Get task.  If true (the default), writable files will be overwritten automatically.
    • RecursiveGet.  Passed to the Recursive property of the Get task.  If true (the default), the get operation will be recursive; otherwise, the get operation will only get top-level files and directories.
    • GetVersion.  Passed to the Version property of the Get task.  Defaults to SourceGetVersion (see below), but can be overridden to retrieve a particular version from source control.
    • GetFileSpec.  Passed to the Filespec property of the Get task.  Defaults to empty, signifying that the entire contents of the workspace should be retrieved.
    • GetPopulateOutput.  Passed to the PopulateOutput property of the Get task - controls whether the Gets, Replaces, and Deletes properties of the Get task are populated (these provide access to the new files retrieved from version control, the files modified during the get, and the files deleted during the get).  Default value is false.
  • Label (for more info, see the tf label docs here)
    • LabelRecursive.  Passed to the Recursive property of the Label task.  If true (the default), the label operation will be recursive; otherwise, the label operation will only label top-level files and directories.
    • LabelChild.  Passed to the Child property of the Label task.  Valid values are Merge or Replace, with the default being Replace
    • LabelComment.  Passed to the Comment property of the Label task, this property controls the comment text used by the label operation.  Default value is "Label created by Team Build".
    • LabelName.  Passed to the Name property of the Label task, this property controls the name of the label generated by the label operation.  Default value is the build number.
    • LabelFiles.  Passed to the Files property of the Label task, this property controls the files labeled by the label operation.  Default value is "$/", or every file mapped in the workspace.
    • LabelScope.  Passed to the Scope property of the Label task, this property controls the scope of the label generated by the label operation.  Default value is "$/$(TeamProject)".

Other Useful Properties

These are properties that are used by the build process and should not be modified.  They can, however, be used to execute logic conditionally, to parameterize custom logic in the build process, etc.

  • BinariesRoot.  The root directory for binaries - typically the working directory for the build agent + the BinariesSubdirectory property (see above).
  • BuildAgentName.  The name of the build agent running the build.
  • BuildAgentUri.  The URI of the build agent running the build.
  • BuildBreak.  Set to true when a compilation error occurs, otherwise false.  Can be used in build break targets to determine whether they are executing after a compilation error or executing normally.
  • BuildDefinition.  This property provides the name of the definition being built.
  • BuildDefinitionId.  This property provides the integer ID of the definition being built.
  • BuildDefinitionName.  See BuildDefinition.
  • BuildDefinitionUri.  The URI of the build definition being built.
  • BuildDirectory.  The local directory being used for the build, corresponding to the expanded working directory of the build agent being used.
  • BuildProjectFolderPath.  This property provides the version control path to the TfsBuild.proj file being run, and corresponds to the ConfigurationFolderPath property of the build definition.
  • BuildUri.  The URI of the executing build.
  • ConfigurationFolderUri.  The version control URI of the directory that contains the TfsBuild.proj file being run.
  • DropLocation.  This property provides the "root" drop location for the build - either the DefaultDropLocation of the build definition or the overridden drop location provided when the build was queued.
  • IsDesktopBuild.  True for desktop builds, false for "standard" build machine builds.
  • LastBuildNumber.  The number of the last build, regardless of its status.
  • LastGoodBuildLabel.  The label created by the last "good" build.  This is the label that will be used to associate changesets and work items with the current build.
  • LastGoodBuildNumber.  The number of the last good build.
  • MachineName.  The machine name of the build agent running the build.
  • MaxProcesses.  The maximum number of processes being used by MSBuild.
  • Port.  The port being used by the build agent running the build.
  • NoCICheckinComment.  The comment which must be included in a checkin to prevent continuous integration from triggering a build.  This is critical for CI builds that need to check files in to avoid triggering an infinite loop of builds.
  • Reason.  (SP1 and later only)  The reason the build was started - None indicates the build was manually queued, Individual indicates that a "build every checkin" trigger caused the build, Batch indicates that a "rolling build" trigger caused the build, Schedule indicates that a scheduled trigger caused the build, and ScheduleForced indicates that a scheduled trigger that builds whether or not changes have occurred since the last build caused the build.
  • RequestedBy.  The user that requested the build - for triggered builds, this will typically be a service account.
  • RequestedFor.  The user for whom the build was requested - for CI builds, this will be the user whose checkin triggered the build.
  • SolutionRoot.  The root directory for source files retrieved from version control - typically the working directory for the build agent + the SourcesSubdirectory property (see above).
  • SourceGetVersion.  The version spec that will be used to retrieve sources for the build, unless the GetVersion property is overridden.
  • StartTime.  The time at which the build started.
  • TeamBuildConstants.  Allows projects being compiled to determine whether Team Build is compiling them.  Default value is "_TEAM_BUILD_".
  • TeamBuildOutDir.  The directory Team Build would have set OutDir true if CustomizableOutDir and/or CustomizablePublishDir (see above) were false.  If both are true, this value is equivalent to OutDir
  • TeamBuildRefPath.  Provides the path to Team Build binaries (the logger, tasks, etc.).  Typically %ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies.
  • TeamBuildVersion.  This property is set to 2.0 in Team Build 2008 and will be incremented in subsequent versions.
  • TeamFoundationServerUrl.  The URL of the team foundation server that owns the executing build.
  • TeamProject.  The team project of the build definition being built.
  • TestResultsRoot.  The root directory for test results - typically the working directory for the build agent + the TestResultsSubdirectory property (see above).
  • WorkspaceName.  The name of the workspace used to retrieve sources from version control. 

Target Dependency Properties

These are the properties used to set up the target dependency hierarchy in Team Build.  The can be modified to insert custom targets into the build process as an alternative to overriding the provided extensibility targets, but this should be done with care.  The standard pattern for overriding a dependency property is something like the following:

  <PropertyGroup>
    <DesktopBuildDependsOn>
      PreDesktopBuildTarget;
      $(DesktopBuildDependsOn);
      PostDesktopBuildTarget;
    </DesktopBuildDependsOn>    
  </PropertyGroup>

Note that the initial value of the property is preserved and new values are simply inserted before and after the existing ones, making sure to include semi-colons in all the appropriate spots.  For more information on these properties, you'll need to comb through the targets file - a full explanation of their functionality is beyond the scope of this blog post!

  • CleanDependsOn.
  • CleanAllDependsOn.
  • CleanCompilationOutputDependsOn.
  • CleanConfigurationDependsOn.
  • CleanSolutionDependsOn.
  • CompileDependsOn.
  • CompileConfigurationDependsOn.
  • CompileSolutionDependsOn.
  • ComputeSolutionListDependsOn.
  • CoreCompileDependsOn.  (This one is for backwards compatibility with TFS 2005 target overrides)
  • _CoreCompileDependsOn.  (This one is the new CoreCompileDependsOn for TFS 2008)
  • CoreCompileConfigurationDependsOn.
  • CoreCleanDependsOn.
  • CoreCleanAllDependsOn.
  • CoreCleanCompilationOutputDependsOn.
  • CoreCleanConfigurationDependsOn.
  • CoreCreateWorkItemDependsOn.
  • CoreDropBuildDependsOn.
  • CoreGetChangesetsAndUpdateWorkItemsDependsOn.
  • CoreGetChangesetsOnBuildBreakDependsOn.
  • CoreGetDependsOn.
  • CoreInitializeWorkspaceDependsOn.
  • CoreLabelDependsOn.
  • CoreOnBuildBreakDependsOn.
  • CoreTestDependsOn.
  • CoreTestConfigurationDependsOn.
  • CreateWorkItemDependsOn.
  • DesktopBuildDependsOn
  • DesktopRebuildDependsOn.
  • DropBuildDependsOn.
  • EndToEndIterationDependsOn.
  • GetChangesetsAndUpdateWorkItemsDependsOn.
  • GetChangesetsOnBuildBreakDependsOn.
  • GetDependsOn.
  • InitializeWorkspaceDependsOn.
  • LabelDependsOn.
  • OnBuildBreakDependsOn.
  • PostBuildDependsOn.
  • PreBuildDependsOn.
  • RunTestDependsOn.
  • TeamBuildDependsOn.
  • TestConfigurationDependsOn.
  • TestDependsOn.

First of all, sorry for the long delay between posts...  Things have been a bit crazy around here trying to meet various deadlines lately, and blogging fell right off my radar for a while.  Having said that, I've got a pretty big backlog of posts to get done, so hopefully I'll get a bunch posted here in the near future.

I've gotten several questions over the past month or so on calling custom targets defined in TfsBuild.proj for each solution/project built and/or for each configuration (e.g. Debug / Any CPU) built.  I did a post a while back on calling custom targets, but it was mostly focused on calling targets other than the default (typically "Build") in the solutions/projects included in the SolutionToBuild item group within TfsBuild.proj.  That is, these custom targets would be in the solution / project itself, not in TfsBuild.proj.

In Team Build 2008, we added several new extension points that allow you to execute custom targets within TfsBuild.proj before / after the compilation of individual solution and/or configurations.  In particular, in Team Build 2005 the compilation portion of the build process included just the following targets (with targets designed to be overridden given in bold):

BeforeCompile

CoreCompile

AfterCompile

And that was it...  CoreCompile built every solution for every configuration.  If you wanted to inject some custom logic anywhere other than before this entire process or after this entire process, you were basically out of luck.  In Team Build 2008, the compilation portion of the build process includes the following targets:

BeforeCompile

CoreCompile

    (for each configuration)

    BeforeCompileConfiguration

    CoreCompileConfiguration

        (for each solution)

        BeforeCompileSolution

        CoreCompileSolution

        AfterCompileSolution

    AfterCompileConfiguration

AfterCompile

Additionally, several properties are available in the relevant spots in this process to help you execute logic conditionally.  In particular, within each iteration of the *CompileConfiguration targets, the properties $(Platform) and $(Configuration) will provide the currently compiling platform and configuration; and within each iteration of the *CompileSolution targets, the property $(Solution) will provide the currently compiling solution. 

So - to execute custom logic for each solution, you can either insert that logic directly into the Before/AfterCompileSolution target, use the CallTarget task to explicitly call your target, etc. 

The MSBuild team is looking for some feedback from the community to keep their "vision" for the next version of MSBuld aligned with their customers wants and needs.  They've got a blog post up here that asks for you, the MSBuild experts out there, to rank various potential new features they could work on.  Here are the various options (with fuller descriptions elided):

  1. Higher performance multiprocessor support...
  2. VC support...
  3. Support for other Microsoft project types that aren't yet in MSBuild format: Deployment/MSI (*.vdproj), SQL Reporting (*.rptproj), Biztalk (*.btproj), Prompt (*.prproj), etc...
  4. Conversion of Visual Studio solution files (*.sln)...
  5. Extensible up-to-date checking...
  6. Distributed build...
  7. Extensible re-usable inline tasks...
  8. Typing and scoping for items and properties.
  9. Extensible functions...
  10. An MSBuild debugger...
  11. Visualization for project and target dependencies...

Personally, my vote would be to spend $50 on getting the full solution/project system MSBuild compliant (#'s 2, 3, and 4), $40 on the debugger (#10), and $10 on the miscellaneous syntax improvements (#'s 8, 7, and 9 - in that order).  The rest of the features (improved multiprocessor support, distributed build, project/target dependency visualization) target what I would think would be a pretty small segment of the market, especially given the current lack of real VC support. 

But hey, what do I know!?  Go to the original post and share your priorities with the MSBuild team.

Got an email from Martin Woodward this morning asking:

What would be your preferred way to fully fail the build on test failure rather than partially succeed the build?

A bit of background - Team Build 2008 marks builds as Succeeded if no errors are encountered during the build process, Partially Succeeded if compilation succeeds but an error is encountered at some other stage of the build process (e.g. a unit test fails), and Failed otherwise.  Depending on where you are in your development process, this logic may or may not make sense to you.  For example, if you are two weeks away from shipping your product, a unit test failure might be just as important to you as a compilation failure. 

The design of Team Build 2008 doesn't really allow for setting the overall status of the build directly - the setting of the build status is what indicates to TFS that the build is "complete", which triggers the build queue logic to start up the next build, etc.  As such, you really shouldn't ever set this property of the build yourself!

You can get around this by setting the properties Team Build uses to determine the overall status of the build, however - CompilationStatus, TestStatus, and an internal logger property that keeps track of whether any errors have occurred.  Note the above algorithm for determining status one more time - Succeeded if no errors are encountered, Partially Succeeded if CompilationStatus is Succeeded but an error occurs at some other stage, and Failed otherwise.  So - if you want the overall status of the build to be Failed, you'll need to set CompilationStatus to Failed.

So - here is my recommended approach:

  <Target Name="AfterTest">

    <!-- Refresh the build properties. -->
    <GetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                        BuildUri="$(BuildUri)"
                        Condition=" '$(IsDesktopBuild)' != 'true' ">
      <Output TaskParameter="TestSuccess" PropertyName="TestSuccess" />
    </GetBuildProperties>

    <!-- Set CompilationStatus to Failed if TestSuccess is false. -->
    <SetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                        BuildUri="$(BuildUri)"
                        CompilationStatus="Failed"
                        Condition=" '$(IsDesktopBuild)' != 'true' and '$(TestSuccess)' != 'true' ">

  </Target>
The idea here is to check whether or not tests have succeeded (see my previous post here on this topic) and then update the CompilationStatus (and thus the overall Status) of the build accordingly.  If you want the build to halt immediately in addition to marking the status as Failed, you can use an Error task, and you'll probably also want to move the logic to the AfterTestConfiguration target (to avoid running tests for each configuration before halting the build). 
More Posts Next page »
 
Page view tracker