- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Wednesday, April 05, 2006

Team Build + MSBuild Properties

If you are using Team Foundation Server as your SCM tool, I hope you're taking advantage of using the Team Build tool to create re-producable public builds for your projects. Very cool, and very useful, especially for those Enterprise applications. As you use Team Build you'll need to make changes to how your build process is executed. You can make the customizations in the TFSBuild.proj file. This is the file that will drive your team build. In this file there are many properties declared that are generated by the New Build Type Creation Wizard. These properties are contained in the first PropertyGroup element in that file. Some of these properties include; Description, DropLocation,BuildDirectoryPath,... which are used internally by Team Build to setup the build. Of course Team Build uses MSBuild to actually execute the build, but some things need to happen before/after MSBuild gets involved. For instance creating the Build report, which require those values. In the ideal world you'd expect for those values to be gathered using the MSBuild Object Model, but this is not the case. Team Build is extracting those values as they are declared.
There has been a few posts about this particular problem on the MSBuild MSDN Forum. Those posts are at:
Post: Property Evaluation Order (beta 3)
Properties in properties
In the post Property Evaluation Order (beta 3) I provide a workaround for most situations, but its not always perfect. Here it is for you to see:

It seems like Team build is not using the msbuild object model to get those evaluated properties, but instead are simply extracting the text value from those property declarations.

I tried to overwirte it in different places in the project file as well, with no luck. It always took the first declaration. I'm guessing that the property is not able to be overwritten because it is passed in as a command line parameter to msbuild.exe by team build. But there is a workaround that solves some of the issues. In your TFSBuild.proj file, modify the DropLocation to one that doesn't use any properties and insert the following towards the bottom:

  <PropertyGroup>

    <RootProjectFolder>ProjectX</RootProjectFolder>

 

    <DropBuildDependsOn>

      FixDropLocation;

      $(DropBuildDependsOn);

    </DropBuildDependsOn>

  </PropertyGroup>

 

  <Target Name="FixDropLocation">

    <CreateProperty Value="$(DropLocation)\$(RootProjectFolder)">

      <Output TaskParameter="Value" PropertyName="DropLocation"/>

    </CreateProperty>

    <Message Text="Drop loc: $(DropLocation)" Importance="high"/>

  </Target>

This prepends the FixDropLocation to the DropBuildDependsOn list of targets, so it will be executed before the DropBuild target. This cause your project drop files to be dropped in the location tat you want. This is able to overwrite the DropLocation property becuase we are using the CreateProperty task to overwrite this instead of the normal property declaration.

This is not however a perfect solution, because your build log will always be located in the DropLocation that was originally declared (the one w/o the property embedded within it). And in the Team build type history the drop location will also have this location as well. But it does place the files where you want them :)

Unfortunately it looks like the DropLocation that is given to TFS is happening outside the scope of MSBuild so I'm not sure you have a lot of options. You can't even pass this as a command line parameter to the tfsbuild.exe utility either. Let me know if this  is not clear.


Sayed Ibrahim Hashimi


msbuild | Team Build | TFS | Visual Studio Wednesday, April 05, 2006 8:09:52 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, March 31, 2006

VS Service Packs!

In case you haven't already heard a service pack will be released for both VS 2003 & VS 2005 this year! SP1 for VS 2003 is scheduled to ship during the second quarter, and SP1 for VS 2005 in the third. You can read more about this at: http://msdn.microsoft.com/vstudio/support/servicing/. I think its kind of interesting that VS2003 didn't have a service pack for 3 years, yet 2005 is having one not even 1 year later. Perhaps because of all the new features that were shipped with this version, however VS 2003 shipped with many new features as well. Since MSBuild is technically a part of the .NET Redistributable, I'm not sure if the service packs will impact anything related to MSBuild. If I find out more I'll be sure to post more.

Sayed Ibrahim Hashimi

Visual Studio Friday, March 31, 2006 7:07:45 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, March 30, 2006

MSBuild Tigris tasks

Previoiusly I wrote about the Microsoft SDC Tasks library, there is another great resource for MSBuild tasks. This is the MSBuild Tigris Tasks project. This is an open source project, so you can submit your tasks to this library as well. Tigris.org is known for their Source Control Management (SCM) tool, Subversion. I've used Subversion previously and was pleased with it, but my favorite is Team Foundation Server :) If you're using Subversion as your source control you'll be glad to find many task for accessing your repository. From the project's site here is a list of the MSBuild tasks that are either completed or currently under development.

Task

Description

AppPoolController*

Allows control for an app pool on a local or remote machine with IIS installed.

AppPoolCreate*

Creates a new application pool on a local or remote machine.

AppPoolDelete*

Deletes an existing application pool on a local or remote machine.

AssemblyInfo

Generates an AssemblyInfo file using the attributes given.

Attrib*

Changes the attributes of files and/or directories

FileUpdate*

Replace text in file(s) using a Regular Expression.

FtpUpload

Uploads a file using File Transfer Protocol (FTP).

FxCop*

Uses FxCop to analyze managed code.

Mail

Sends an email message.

Math.Add

Add numbers.

Math.Divide

Divide numbers.

Math.Multiple

Multiple numbers.

Math.Subtract

Subtract numbers.

Move*

Moves files on the filesystem to a new location.

NDoc

Runs NDoc to create documentation.

NUnit

Runs tests using the NUnit.

RegistryRead

Reads a value from the Registry.

RegistryWrite

Writes a value to the Registry.

Script*

Executes code contained within the task.

ServiceController*

Task that can control a Windows service.

ServiceQuery*

Task that can determine the status of a service.

Sleep*

A task for sleeping for a specified period of time.

SqlExecute*

Executes a SQL command

SvnCheckout

Checkout files from Subversion

SvnClient

Subversion Client

SvnCommit

Commit files to Subversion

SvnExport

Export files from Subversion

SvnInfo*

Get Subversion information for a file or directory.

SvnUpdate

Update files from Subversion

SvnVersion

Get Subversion revision number of a local copy

TaskSchema*

Generates a XSD schema of the MSBuild tasks in an assembly.

Time*

Gets the current date and time.

Unzip

Unzip a file to a target directory.

Version

Increments a four-part version number stored in a text file

VssAdd*

Adds files to a Visual SourceSafe database.

VssCheckin*

Checks in files to a Visual SourceSafe database.

VssCheckout*

Checks out files from a Visual SourceSafe database.

VssClean*

Removes VSS binding information and status files from a solution tree.

VssDiff*

Generates a diff between two versions of an item in a Visual SourceSafe database.

VssGet*

Gets the latest version of a file or project from a Visual SourceSafe database.

VssHistory*

Generates an XML file containing the history of an item in a VSS database.

VssLabel*

Labels an item in a Visual SourceSafe database.

VssUndoCheckout*

Cancels a checkout of an item from a Visual SourceSafe database.

WebDirectoryCreate*

Creates a new web directory on a local or remote machine.

WebDirectoryDelete*

Deletes a web directory on a local or remote machine

WebDownload*

Downloads a resource with the specified URI to a local file.

XmlRead

Reads a value from a XML document using a XPath.

XmlWrite

Updates a XML document using a XPath.

Xslt*

Merge and transform a set of xml files.

Zip

Create a zip file with the files specified.

 * Items not complete or have not been released.

Sayed Ibrahim Hashimi

msbuild | Visual Studio Thursday, March 30, 2006 4:21:48 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, March 28, 2006

Dreamcatcher preview

I'm working on an application during my spare time, its called Dreamcatcher. The purpose of this dream is to maintain a digital dream journal, or any journal for that matter. A major goal of the Dreamcatcher is to facilitate finding specific dreams easily, this is going to be accomplished by search mechanisms, and a "tagging" dreams with specific information. For instance you'll categorize the dream, you can place keywords (or labels in gmail terms) onto a dream, and other similar things. These will allow the user to find the desired dream in a set of different ways. Currently all of the data will be stored on the uesrs local machine, but I was also thinking of having a database available for users to keep their entries. This will be determined by user feedback, at a later stage. Here is what the main screen looks like:

http://www.sedodream.com/images/Dreamcatcher/main.jpg

If you are interested in using this app please send me an email (sayed.hashimi [AT] gmail.com )and I'll keep you posted on its progress. I'm expecting to have a useable release in the next couple months.

Sayed Ibrahim Hashimi

Dreamcatcher | Personal Tuesday, March 28, 2006 7:50:21 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, March 21, 2006

Me and Jesus

A co-worker of mine is playing the role of Jesus in a play at his local church. He had to have fake hair implanted inorder to fulfill the role properly. So this week he is coming to work looking a little different. I thought I'd take a picture so everyone could see this. The information for the play is available at http://www.northfloridapassionplay.com/default.htm



Sayed Ibrahim Hashimi
Tuesday, March 21, 2006 11:48:14 PM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, March 18, 2006

Microsoft SDC Tasks

One of the Microsoft teams based in the UK has created a library of MSBuild tasks that is available for you to download at: http://www.codeplex.com/sdctasks You can download binary versions as well as all the source code for these tasks. There is almost 170 tasks that they have made available! Below is a list of them all if you are interested in what they are.

 

ActiveDirectory.Group.AddUser SourceDepot.ReverseIntegrate
ActiveDirectory.Group.Create SourceDepot.Sync
ActiveDirectory.User.Create SourceSafe.Get
ActiveDirectory.User.GrantPrivilege SourceSafe.LabelGet
AssignCulture SourceSafe.LabelLatest
BizTalk2002.Configure SourceSafe.Changes
Cab.AddFile Sql.DisconnectUsers
Cab.Create Sql.Execute
Cab.ExtractFile Sql.Access.Revoke
Certificates.AddCertificate Sql.Role.AddUser
CheckComponentInstalled Sql.Access.Grant
CheckProductInstalled ShortenPath"/>
CodeCoverage.AddAssembly StringToItemList"/>
CodeCoverage.Create StringComparison"/>
CodeCoverage.MergeReports StringReplace"/>
CodeCoverage.Start Summary.AddToReport
CodeCoverage.Stop Time.Get
CompileTestSummary Time.Diff
ComponentServices.Application.AddComponent Time.Report
ComponentServices.Application.Delete Tools.DevEnv
ComponentServices.Application.RemoveComponent Tools.FxCop
ComponentServices.Application.Shutdown Tools.Installshield
ComponentServices.Application.Update Tools.MsTest"/>
ComponentServices.Component.Update Tools.Ndoc
ConsoleReadLine Tools.Nunit
CreateGuid Tools.PreSharp
Email Tools.PsExec
EventSource.Create Tools.StrongName.AddSkipVerification
EventSource.Log Tools.StrongName.ReSign
File.Delete Tools.StyleCop
File.GetFiles TrimJavascript
File.RegEx VersionNumber.CreateSourceFiles
Folder.Copy VersionNumber.Load
Folder.GetInfo VersionNumber.SplitBuildNumber
Folder.Share.Connect VersionNumber.Update
Folder.Share.Create VersionNumber.VSSUpdate
Folder.Share.Delete VirtualServer.VirtualMachine.OS.CheckHeartBeat
Folder.Share.Disconnect VirtualServer.VirtualMachine.OS.Shutdown
Folder.Share.Exists VirtualServer.VirtualMachine.Start
GetInstalledComponents VirtualServer.VirtualMachine.Stop
GetInstalledProducts Web.AppPool.Create
GetMetadataValueFromList Web.AppPool.Delete
GlobalAssemblyCache.AddAssembly Web.FtpSite.Create
GlobalAssemblyCache.RemoveAssembly Web.FtpSite.CreateVirtualDirectory
Help.Compile Web.FtpSite.Delete
Help.CreateProject Web.FtpSite.DeleteVirtualDirectory
Help.Decompile Web.FtpSite.Start
Help.DocumentExceptions Web.FtpSite.Stop
Help.InsertAfter Web.ServiceExtension.AddFile
Help.InsertBefore Web.ServiceExtension.DeleteFile
Help.InsertParent Web.WebSite.AddBinding
LogicalComparison Web.WebSite.AddFilter
MessageQueue.Create Web.WebSite.AddHttpCustomHeader
MessageQueue.Delete Web.WebSite.AddMimeType
MessageQueue.SetPermissions Web.WebSite.Continue
Msi.EmbedInstallProperties Web.WebSite.Create
Msi.EmbedUninstallProperties Web.WebSite.CreateVirtualDirectory
Msi.GetProperty Web.WebSite.Delete
Msi.Install Web.WebSite.Modify
Msi.Repair Web.WebSite.DeleteFilter
Msi.Uninstall Web.WebSite.DeleteVirtualDirectory
PerformanceCounters.Add Web.WebSite.FilterExists
PerformanceCounters.Remove Web.WebSite.Pause
Registry.CreateKey Web.WebSite.Start
Registry.DeleteKey Web.WebSite.Stop
Registry.DeleteKeyTree Web.WebSite.UnloadVirtualDirectory
Registry.Get Web.WebSite.UpdateHttpErrorSetting
Registry.Set Web.WebSite.UpdateLogSettings
Security.AddAcl Web.WebSite.UpdateServerCertificate
Security.RemoveAcl Web.Smtp.Create
ServiceProcess.Exists Web.Smtp.Start
ServiceProcess.Start Web.Smtp.Stop
ServiceProcess.Stop Wix.CompileMsi
ServiceProcess.UpdateIdentity Wix.LinkMsi
Sleep Wix.Fragment
SourceDepot.Changes Xml.CanonicalizeFile
SourceDepot.ChangesInInterval Xml.ModifyFile
SourceDepot.CreateBranch Xml.GetValue  
SourceDepot.CreateClientFromTemplate Xml.XslTransform
SourceDepot.DeleteBranch Zip.AddFile
SourceDepot.GetChangelistFromDateTime Zip.Create
SourceDepot.GetChangelistFromLabel Zip.ExtractFile
SourceDepot.GetUsersForChangelists MergeByOrder
SourceDepot.Integrate MergeByRef
SourceDepot.LabelSync  

 

I’m sure that some of these tasks you’ll find completely useless. For example I presume that most of you will not be needing the SourceDepot.* tasks. But there are several very cool tasks that are made available here. Tasks for code coverage, tasks for invoking common tools and other things. I think its great that they have made this available and hope to see some of these tasks integrated into the next realease of MSBuild.

Sayed Ibrahim Hashimi


(Edit: Updated SDC tasks link)

msbuild | Visual Studio Saturday, March 18, 2006 7:23:16 AM (GMT Standard Time, UTC+00:00)  #     | 
Monday, March 06, 2006

Removing MS Betas

A friend of mine was installing SQL Server 2005 & Visual Studio 2005 on a new machine he received. After installing SQL Server 2005, he tried to install VS 2005 and received an error stating that he couldn't install VS 2005 becase beta versions were present on his machine. I had this problem earlier when I needed to upgrade my machine from RC to RTM. The problem was that he accidently installed SQL Server 2005 CTP. I told him to run the him to download and run the Visual Studio Beta Removal tool. When I had this problem it solved it right up for me. Unfortunately, I think Windows got confused with all the installing/un-installing that was going on. He got his machine to the point where SQL Server wouldn't show up in the add/remove programs list, he couldn't run the un-installer for it because it wasn't present, and he couldn't re-install the same CTP version to uninstall it correctly. And of course he couldn't install VS 2005. It seemed like he was in a real tough spot, we searched around for more information about how to get around this situation. I found a tool, the Windows Installer Clean Up tool, that can be used to removal applications and installer files from failed apps that don't show up in the add/remove programs list. When he ran this tool he was able to see and remove the SQL related components. Following this he was able to install the correct versions of SQL Server 2005 & Visual Studio 2005.

Sayed Ibrahim Hashimi

Visual Studio | SQL Server 2005 Monday, March 06, 2006 4:57:24 AM (GMT Standard Time, UTC+00:00)  #     | 
Tuesday, February 28, 2006

MSBuild location of current project file

When using MSBuild you can import external declarations into your project by using the Import element. This is very useful for distributing your shared targets across your organization, and is a way to segregate where things are located. When MSBuild encounters this tag, a few things happen. First the current directory is changed to that of the file that is being imported. This is done so assembly locations specified by the Using Task and other imports are resolved correctly. What you may be surprised to find out is that if the importing project file declares any items, those item declarations are always relative to the top project file location. To clarify this if you have the following directory structure:

C:.

├───one

   └───Two

           YourProject.proj

├───Shared

      CurrentDirectory.dll

      SharedTargets.targets

  

   └───Another

           SharedTargets_2.targets

└───utils

        CommandLine.txt

In this scenario YourProject.proj is the top project file, it imports both SharedTargets.targets and SharedTargets_2.targets files. If the SharedTargets.targets had an item declaration of

<ItemGroup>
    <Test Include="test.txt"/>
</ItemGroup>

When imported by YourProject.proj this item declaration would actually resolve to C:\one\two\test.txt instead of the expected C:\Shared\test.txt value. In 95% of the time this is not an issue at all. But for that other 5% how can we accomplish this?

Well, there is no magic reserved MSBuild property for this. So we're gonna have to do some work here. To accomplish this we'll have to create a custom MSBuild task. If you've never created an MSBuild task you might be surprised how easy it is!

I created a new project named DirectoryTask which will house this task. Following this I added a reference to Microsoft.Build.Framework and Microsoft.Build.Utilities assemblies. Following this I wrote the task. It is shown below

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.Build.Framework;

using Microsoft.Build.Utilities;

namespace CurrentDirectory

{

    /// <summary>

    /// Task that will return the folder that contains the current project.

    /// Inheriting from AppDomainIsolatedTask causes this task to execute in its own

    /// App domain. Not necessary here, only for demonstration.

    ///

    /// Sayed Ibrahim Hashimi

    /// www.sedodream.com

    /// </summary>

    public class CurrentDir : AppDomainIsolatedTask

    {

        private ITaskItem currentDir;

        [Output]

        public ITaskItem CurrentDirectory

        {

            get

            {

                return this.currentDir;

            }

        }

        public override bool Execute()

        {

            System.IO.FileInfo projFile = new System.IO.FileInfo(base.BuildEngine.ProjectFileOfTaskNode);

            this.currentDir = new TaskItem(projFile.Directory.FullName);

           

            return true;

        }

    }

}

As you can see this is a pretty simple task. When Execute is called the directory is gathered from the project file that contains the task invocation. The name of the assembly that I built this into is CurrentDirectory.dll.

Now to see this in action I will use the same directory structure shown above. The contents of the SharedTargets.targets file is:

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

 

  <UsingTask AssemblyFile="CurrentDirectory.dll" TaskName="CurrentDir"/>

 

  <Target Name="SharedTarget">

    <CurrentDir>

      <Output ItemName="CurrentDir" TaskParameter="CurrentDirectory" />

    </CurrentDir>

 

 

    <Message Text="Inside the SharedTargets.targets" Importance="high"/>

    <Message Text="Location: @(CurrentDir->'%(Fullpath)')"/>

  </Target>

 

</Project>

This uses the CurrentDirectory task to determine what the current directory is. The SharedTargets_2.targets file is very similar and is:

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

 

  <Target Name="SharedTarget2">

 

    <CurrentDir>

      <Output ItemName="CurrentDir2" TaskParameter="CurrentDirectory" />

    </CurrentDir>

 

 

    <Message Text="Inside the SharedTargets_2.targets" Importance="high"/>

    <Message Text="Location: @(CurrentDir2->'%(Fullpath)')"/>

  </Target>

 

</Project>

 

Now let’s take a look at the very simple YourProject.Proj file.

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

  <Import Project="..\..\Shared\SharedTargets.targets"/>

  <Import Project="..\..\Shared\Another\SharedTargets_2.targets"/>

 

  <PropertyGroup>

    <PrintDependsOn>

      SharedTarget;

      SharedTarget2

    </PrintDependsOn>

  </PropertyGroup>

 

  <Target Name="Print" DependsOnTargets="$(PrintDependsOn)">

  </Target>

</Project>

 

This file simply imports the two other files and defines the print target. Now we want to invoke the Print target on this project file. To do this open the Visual Studio2005 command prompt and go the directory containing the YourProjec.proj file. Then execute the following:

>msbuild.exe YourProject.proj /t:Print

 

The results of this invocation are:

__________________________________________________

Project "C:\Data\Community\msbuild\MSBuildDirectoryExample\one\Two\YourProject.proj" (default targets):

 

Target SharedTarget:

    Inside the SharedTargets.targets

    Location: C:\Data\Community\msbuild\MSBuildDirectoryExample\Shared

Target SharedTarget2:

    Inside the SharedTargets_2.targets

    Location: C:\Data\Community\msbuild\MSBuildDirectoryExample\Shared\Another

 As you can see the correct location was resolved for both of these items. Previously if you had command line utilities in source control and .targets files that would manage invoking those tools, it was error prone. This is because the .targets file wouldn’t be able to resolve the location of the command line util, even if in the same directory. The solution to this problem it to rely on the developer to set a property (or environment variable) which states where this tool can be located; or some other similar means. With this task we no longer have to rely on such a solution. The .targets file is able to resolve the location of the command line utility. 

I have bundled all related files into a zip file which you can download below.

MSBuildDirectoryExample.zip (you may have to right-click->Save As)

 http://www.sedodream.com/content/binary/MSBuildDirectoryExample.zip

Sayed Ibrahim Hashimi

msbuild | Visual Studio Tuesday, February 28, 2006 6:23:33 AM (GMT Standard Time, UTC+00:00)  #     | 
Wednesday, February 08, 2006

MSBuild email error during build

The other day I received an intersting comment on my previous blog enty. The comment was asking how to send the details of a build error via email to a recipient(s). When I was starting with MSBuild I was also wondering how to do this, so I would like to address this topic now. This is a very useful scenario to implement if you builds are automated. If you are lucky enough to be using Team Foundation Server then you job is a lot easier because this is taken care of for you. But if you are like most .NET developers odd are that you aren't. So we need a good way to do this.
There is an SmtpTask that you can use to send an email through an MSBuild task. At first thought you might think that this will work, but you'll be saddended to find out that it doesn't. This is because the only way to trigger this task to be executed is from the project file. The only way to achieve that is by using the MSBuild OnError element to state what to do in the case of an error occuring during the course of a target executing. When you build a VS project you are most likely to execute the Build (or Rebuild which calls Build) target. This target actually perfroms no work itself, it is delegated to other targets. So having and OnError element in that target is of no use. You'd have to place one in each of the other targets that get executed from its dependencies. This means sticking that element in a lot of different places, which is no fun. So there is no way to say "If an error occurs at any time during my build execute this target". But if we change that phrase to "If an error occurs anywhere during my build then do ...", we can achive that. We do that by writing and using a custom logger!

Writing a custom logger may sound to be daunting at first, but rest assured it is not! Once you write one logger you can write many, and I'll show you how to write your first one right here! Let's cover some basics first. MSBuild ships with 2 loggers, the ConsoleLogger and the FileLogger. These classes are a part of the Microsoft.Build.BuildEngine namespace, and you can extend them. So this means that we can use the already written functionality and extend it to fit our needs. This is exactly what we are going to do in this example. We will extend the FileLogger to email the log file if an error has occured. Let's get started on this.

First create a new C# (or other managed lang of you choice) Class Library project. I named this project EmailLogger. You'll first want to add references to the following libraries.

  1. Microsoft.Build.BuildEngine
  2. Microsoft.Build.Utilities
  3. Microsoft.Build.Framework

At this point we are ready to start coding. We want to make the EmailLogger class a sub-class of the FileLogger to get its behavior. Here are some logger basics now. The logging mechanism in MSBuild is an event based system. Events include such things as ProjectStarted, BuildFinished, ErrorRaised and others. We're obviously very interested in the last one. The method

     public void Initialize(Microsoft.Build.Framework.IEventSource eventSource)

will be called before MSBuild starts to build your project. In this method is where you tell MSBuild what events you are interested in handling. In our case we will be handling only two events. Those events are; ProjectStarted and ErrorRaised. We handle the ProjectStarted because we need to grab all of the properties defined in the project file and put them somewhere. And we handle ErrorRaised to make a flag that an error has occured. Along with Initilize() we are promised that

    public override void Shutdown()

will be called after the build has finished and its time for the logger to say goodnight. This is where we will actually send the email. Let's have a look at the Initalize method shown below.

        /// <summary>

        /// This is guarenteed to be called by MSBuild before any build steps occur

        /// </summary>

        /// <param name="eventSource"></param>

        public override void Initialize(Microsoft.Build.Framework.IEventSource eventSource)

        {

            //This call will set the correct values from the parameters

            base.Initialize(eventSource);

            this.SetParameters(base.Parameters);

            //add the logger delegates here

            eventSource.ErrorRaised += new BuildErrorEventHandler(this.BuildError);

            eventSource.ProjectStarted += new ProjectStartedEventHandler(this.ProjectStarted);

        }

In this method we aren't doing anything fancy, we call SetParameters because we need to get the file location where the log is going to be saved to. When you are writing loggers remember it is upto you to parse and validate any parameters that are passed to it. If you are using the Logger class as your base class then the Verbosity is handled for you. You should chose this over implementing the ILogger interface directly. Anywayz back to the topic at hand, besides that we are registering our delegates to the appropriate events. After this the build starts, our ProjectStarted delegate gets invoked. In this method we are storing all the properties defined in the build file to a dictionary so we know how to send the email.

This logger works like this, it will gather the settings for sending the email from the project's properties. These can either be defined in the project file itself, or passed in as command line arguments to msbuild.exe using the /p switch. The properties that it will look for are named:

        EmailLogger_SmtpHost

        EmailLogger_From

        EmailLogger_To

        EmalLogger_CC

        EmailLogger_SmtpPort

        EmailLogger_TimeOut

        EmailLogger_SendOnSuccess

The alternative to this approach which would actually be more correct is to send all of these in a parameters to the logger. But that would be really annoying to have to type all of these values in at the command line each time you want to perform a build. Even putting these into an msbuild.rsp file is not as convienent. So I figured to put them into the project is not such a bad idea.

 

The ProjectStarted handler is shown below.

        /// <summary>

        /// The event that is raised when the project is started.

        /// This will examine all of the properties defined in the project file

        /// and store them in the <code>properties</code> dictionary

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        void ProjectStarted(object sender, ProjectStartedEventArgs e)

        {

            if (e.Properties != null)

            {

                foreach (DictionaryEntry entry in e.Properties)

                {

                    this.properties.Add(entry.Key.ToString(), entry.Value.ToString());

                }

            }

        }

The BuildError method is very simple, it is shown below.

        /// <summary>

        /// This is called in the event of an error.

        /// In this example we just mark that there was an error and the email is sent

        /// at the end of the build in the <code>Shutdown</code> method.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        void BuildError(object sender, BuildErrorEventArgs e)

        {

            this.errorOccurred = true;

        }

Besides this we are pretty much just left with the Shutdown() method. It is shown below.

        /// <summary>

        /// This is called before msbuild exits.

        /// This is the method that will actually send the email out if there was an error

        /// or if send on success was specified as a property to 'true'

        /// </summary>

        public override void Shutdown()

        {

            //this should close down the log file

            base.Shutdown();

 

            //get the values for the email from the properties           

 

            if (this.properties == null || this.properties.Count <= 0)

            {

                return;

            }

 

            Emailer emailer = new Emailer();

 

            emailer.SmtpHost = this.GetProperty(EmailLoggerStrings.SmtpHostString);

            emailer.From = this.GetProperty(EmailLoggerStrings.FromString);

            emailer.To = this.GetProperty(EmailLoggerStrings.ToStr);

            emailer.Cc = this.GetProperty(EmailLoggerStrings.CcString);

            emailer.SetSmtpPort(this.GetProperty(EmailLoggerStrings.SmtpPortString));

            emailer.SetTimeout(this.GetProperty(EmailLoggerStrings.TimeoutString));

 

            bool emailOnSuccess = false;

            string emailOnSuccessStr = this.properties[EmailLoggerStrings.EmailOnSuccessString];

            if (!string.IsNullOrEmpty(emailOnSuccessStr))

            {

                emailOnSuccess = bool.Parse(emailOnSuccessStr);

            }

            if ( errorOccurred || emailOnSuccess )

            {

                //we need to send to email the log file to recipient here

                emailer.Subject = "Build log " + DateTime.Now.ToShortDateString();

                emailer.Body = "Log file attached";

                emailer.Attachment = this.logFile;

 

                emailer.SendEmail();

            }

        }

In this method we let the base (FileLogger) shutdown first. This will cause the logfile to be written out to disk, then we create the email and send if if there was an error, or if it was stated to send even on success. This is pretty much it from an MSBuild perspective. We have the Emailer class, but there is no MSBuild stuff there, just stuff to send the email.

Now we need to know how to use this logger. You are going to want to place the dll, in my case it's named EmailLogger.dll in a location that is accessible by other projects during the build process. In my case I have a SharedLoggers directory that contains these guys. So I copied it there. Then at the command line when we perform a build we need to tell MSBuild to use this logger. Do that by:

>msbuild.exe

PROJECT_FILE_NAME /l:EmailLogger,(PATH_TO_LOGGER)\EmailLogger.dll;logfile=test.log;verbosity=detailed

And you'll have to specify those above properties in your project file that you are building like so:

  <PropertyGroup>

    <EmailLogger_SmtpHost>fill this in</EmailLogger_SmtpHost>

    <EmailLogger_From>sayed.hashimi@gmail.com</EmailLogger_From>

    <EmailLogger_To> sayed.hashimi@gmail.com </EmailLogger_To>

    <EmalLogger_CC></EmalLogger_CC>

    <EmailLogger_SendOnSuccess>true</EmailLogger_SendOnSuccess>

  </PropertyGroup>

Then start your build. If you are building a solution, then these properties have to be defined in a project file that is being built, or at the command line. If you have multiple definitions this could get confusing from this implementation. Maybe more on that at a later date, too late now. That should be about all you need to know. The files are made available here for your convienence. Oh yeah, this hasn't been throughly tested, I just put this together so use it at your own risk. If you have modifications I'd appreciate you letting me know so I can fix my version as well.

 

EmailLoggerProjectFiles.zip (23.26 KB)   You may have to right-click Save as to get this file.

EmailLogger.dll (20 KB)  You may have to right-click Save as to get this file


Sayed Ibrahim Hashimi

 

msbuild | Visual Studio Wednesday, February 08, 2006 7:22:28 AM (GMT Standard Time, UTC+00:00)  #     | 
Tuesday, January 31, 2006

MSBuild logfile

I've seen this question asked a few times, here and there. I'm sure the answer is in a few other places as well as here, but I don't have the link. Anywayz the topic is how to create a file that contains the biuld log contents. This is a pretty simple question because MSBuild ships with a file logger. So all we have to do is to tell MSBuild to use it and pass it the options that we want to use.
First you'll need to open the Visual Studio 2005 Command Prompt and navigate to the directory that contains your project. Lets call this project, DreamcatcherUI.proj. To specify to use the file logger we will have to use the /l switch when we invoke msbuild on it. This is the logger switch, the long version of this is /logger.
The syntax for using this switch is:
     /l:<logger class>,<Assembly>[;Parameters 4 logger]
In order to use the MSBuild file logger we can use the following syntax
    /l:FileLogger,Microsoft.Build.Engine;verbosity=detailed;logfile=mylog.log
So the msbuild command to build the DreamcatcherUI.proj would be:
>msbuild.exe /t:Build /l:FileLogger,Microsoft.Build.Engine;verbosity=detailed;logfile=mylog.log
The verbosity is the level of detail that you want in your log file, there are 5 settings: quiet[q]; minimal[m];normal[n];detailed[d]; and diagnostic[diag]. The name in brackets is the short name, you can use these in place of the full name if desired. The logfile parameter is the location that you want the file saved to. If not specified then a file msbuild.log will be created with the log in the current directory.
It is important to mention that the parameters section of the logger is upto the logger implementation to parse and make sense of. For the FileLogger you have 2 other parameters that you can pass in those append and encoding. If append=true then the file will be appended to and overwriten if otherwise. You can use the encoding to specify a certain encoding for the log file that is to be created.

Sayed Ibrahim Hashimi

msbuild | Visual Studio Tuesday, January 31, 2006 5:16:12 AM (GMT Standard Time, UTC+00:00)  #     |