MSBuild Logger in Delphi .NET
Project: Create a custom XML logger dll for use with MSBuild using Delphi.
Starting with Delphi 2007 Codegear uses MSBuild as the build engine. There is a split in the Delphi community over this being a good thing, but I think it is. MSBuild is Microsoft’s scripting tool for the build process and is very comparable to make. The ‘dproj’ file that is created with a project in Delphi is actually an XML file that can be used with MSBuild.
I think this is a good thing because I can use the exact same tool as the IDE to automates builds. Delphi WAnt worked for years but you have to manually create the build script and keep it in sync with the project. Now Delphi keeps the MSBuild script in sync since it is the same file it uses when you compile in the IDE.
While making the switch from WAnt to MSBuild I realized I no longer had to redirect stdout and parse the output for errors during the build process. I could just tell MSBuild to use a custom logger and I could write the log as easy to parse XML. It was finally time for me to learn a little .NET. All that is needed to create a custom logger is create a class that supports the ILogger interface.
During the following steps I will create a basic logger using Delphi .NET that should be easy to extend to any format you might need. The first thing is to start a new project.
Select ‘New | Other … ‘ and then select Library under ‘Delphi for .NET Projects.
Save the new library project as “DCBuildLogger.dproj” and add the references to the .NET assemblies Microsoft.Build.Framework and Microsoft.Build.Utilities.
Right click References in the Project manager and select Add Reference.
Select Microsoft.Build.Frameworkand Microsoft.Build.Utilities and click Add References
Select ‘File | New | Other’ and select Unit under Delphi for .NET projects
Save the new unit as BuildLogger.pas. Now that everything is setup we can get to the interesting bits. First add a uses clause and specify the proper units.
uses SysUtils, Classes, System.Xml, Microsoft.Build.Utilities, Microsoft.Build.Framework;
Next define a class that will implement the ILogger interface called TXMLBuildLogger. We are actually extending the Logger class which already supports the interface. Include a couple of basic functions to make the first version of the logger somewhat useful.
type
TXMLBuildLogger = class(Logger)
private
FXMLWriter: XmlTextWriter;
procedure eventSource_ProjectStarted(sender: TObject;
e: ProjectStartedEventArgs);
procedure eventSource_ProjectFinished(sender: TObject;
e: ProjectFinishedEventArgs);
public
procedure Initialize(eventSource: Microsoft.Build.Framework.IEventSource); override;
procedure Shutdown; override;
end;
The methods eventSource_ProjectStarted and eventSource_ProjectFinished will be called by MSBuild when the a project has been started or finished. The Initialize procedure is where the XMLTextWriter is setup and Shutdown is where all the cleanup occurs. Press CTRL+C to create the stubs.
procedure TXMLBuildLogger.Initialize(
eventSource: Microsoft.Build.Framework.IEventSource);
begin
FXMLWriter := XmlTextWriter.Create('c:\BuildLog.xml', nil);
FXMLWriter.Formatting := Formatting.Indented;
FXMLWriter.WriteStartDocument;
FXMLWriter.WriteStartElement('Build');
Include(eventSource.ProjectStarted, eventSource_ProjectStarted);
Include(eventSource.ProjectFinished, eventSource_ProjectFinished);
end;
The first step in Initialize is creating the XMLTextWriter. This example uses a hardcoded filename for the output of c:\BuildLog.xml. Setting the indented property is completely optional but makes the XML output easier to check while debugging. WriteStartDocument must be called to start ouput and WriteStartElement is called to create the XML node that contains the output from the logger. The two Include lines set the method to use for the specified event.
procedure TXMLBuildLogger.Shutdown; begin FXMLWriter.WriteEndElement; FXMLWriter.Close; FXMLWriter := nil; end;
In the Shutdown method cleanup after the XMLTextWriter. Write the closing tag for the Build node created in Initialize and Close the document.
procedure TXMLBuildLogger.eventSource_ProjectStarted(sender: TObject; e:
ProjectStartedEventArgs);
begin
FXMLWriter.WriteStartElement('Project');
FXMLWriter.WriteAttributeString('File', e.ProjectFile);
end;
When MSBuild calls eventSource_ProjectStarted create a new element call Project. Write the project file as an attribute of the Project node called File.
procedure TXMLBuildLogger.eventSource_ProjectFinished(sender: TObject; e:ProjectFinishedEventArgs); begin FXMLWriter.WriteEndElement; end;
Close the Project node when eventSource_ProjectFinished is called.
We could build the library using the IDE but let’s use MSBuild. Open a command prompt and navigate to the directory where DCBuildLogger.dproj was saved. Build the project one time to generate DCBuildLogger.dll.
Now that the dll has been generated we can use it in a test build. The class name for the logger and the full path to the library must be specified. Rerun msbuild but specify the /logger switch and add the needed parameters. Include the /noconsole switch to turn off the output to the command window.
msbuild /noconsole /logger:TXMLLogger,c:\Develop\DCBuildLogger\DCBuildLogger.dll
This should have created a file named BuildLog.xml in c:\. Take a look at the output from the logger.
<?xml version="1.0"?> <Build> <Project File="C:\Develop\DCBuildLogger\DCBuildLogger.dproj" /> </Build>
While it isn’t exactly useful it is a decent proof of concept. Check back for updates on adding more features and handling other events such as TargetStarted, TargetFinished, ErrorEventRaised, WarningEventRaised, and the generic StatusEventRaised that reports every status change.
Posted: December 7th, 2008 under Delphi.
Tags: Delphi .NET ILogger