In this series, Dermot explains how to integrate
a programming language into Visual Studio 2005. Part
Three: MSBuild (see also: Part
One and Part
Two) |
Build systems have been around for
a long time. The
classic is ‘make’, originally distributed
with Unix from around 1977. This has been through several
variants since then, but all share the same idea of building
a ‘target’ from a set of ‘source’ files.
The target is generally rebuilt when one or more of the
source files is changed.
Make files have generally served well when working in
a command line environment – that is, under Windows,
you open a command prompt and type away. But generally,
they haven’t worked as well when combined with
an IDE such as Visual Studio. In older versions of Visual
Studio, there was a considerable overlap between what
the Make file did – fundamentally connecting inputs
to outputs – and the information required to define
the structure of a project – folders, hierarchies
of files, files that need to be in the IDE but are not
part of the output and so on.
A further problem with the older Make systems has been
the difficulty of tracking down problems. The syntax
of Make is, well, terse. In a big Make file, figuring
out what on earth is going on can be quite challenging.
Some time ago (1999, I think), Sun developed Ant – an
XML based Make system in which actions and dependencies
are described by XML structures. The nice thing about
XML is that it is extensible – you can add things
as you need them.
The
good thing about XML is that it’s
extensible. The bad thing is that it’s unreadable:
you really need to get a good Sidekick (see
below).
And so enter Microsoft. Microsoft is very good at recognising
clever ideas and, shall we say, ‘incorporating’ them
into its products. Microsoft has taken the Ant ideas
and produced a new product, MSBuild, which not only replaces
Microsoft’s own Nmake product, it integrates into
Visual Studio as well. This is the neat bit: put another
way, MSBuild not only describes how a product is to be
built, it also describes how the components fit into
Visual Studio.
It’s this dual purpose of MSBuild that I think
is really good. You can use MSBuild from the command
line to build a program (and that is actually one of
the best ways of testing it), but you can also use it
to define a Visual Studio ‘project’, describing
how the project appears in the Solution Explorer. Further,
by using the Visual Studio SDK, you can add and manipulate
your own property data and store the whole lot in a nice,
well structured, MSBuild file.
Basics
There are a number of good tutorials around on how to
use MSBuild (see this Microsoft video for
a very good guided tour), so I’m not going to try
and reproduce those here. But what I found to be missing
when I first started looking at MSBuild was a high level
view of what you use the individual bits for. That’s
what I’ll try and cover here.
First off, MSBuild uses ‘Properties’. A
Property is something that has a value and is defined
like this:
<Optimize>false</Optimize>
Properties are included in ‘Property Groups’ which
can be enabled or disabled. So you might want to turn
optimization on for a debug version like so:
<PropertyGroup Condition=" '$(Configuration)'
== 'Debug' ">
<Optimize>false</Optimize>
</PropertyGroup>
Essentially, a Property is just a key-value pair. In
the example above, the value of ‘Optimize’ is ‘false’.
In conjunction with Properties, MSBuild uses another
concept – an ‘Item’. An Item is an
input to the build system – a source file or files
if you like. An Item has a name, what to include or exclude
and a set of ‘meta items’ that you can associate
with the Item. Here’s an example from
the Steel Ruby
programming IDE for Visual Studio
which I am currently developing:
<Ruby Include="test.rb">
<Arguments>1
2</Arguments>
</Ruby>
The Item’s name is ‘Ruby’, and the
file to include is ‘test.rb’. The meta-data here
is described by the Arguments tag, and I’m using
it to define the arguments that will eventually be passed
to the Ruby interpreter itself.
Here’s what the Ruby ‘item’ looks
like in the Visual Studio Property Window.
MSBuild will then run the Ruby interpreter on the file
like this:
ruby test.rb 1 2
But how does MSBuild actually go about doing the business
of running a program? This is done via the third component
of MSBuild – the ‘Task’. A Task is
always included in a group of related tasks under a ‘Target’.
So a basic build sequence might look like this:
<Target Name="BuildRuby" Inputs="@(Ruby)">
<MakeDir Directories="SyntaxCheck" />
<SteelRubyBuild RubyFiles="@(Ruby)"
ProjectDirectory="$(MSBuildProjectDirectory)"
InterpreterName="$(InterpreterName)"
RubyLibraryPath="$(LibraryPath)" />
</Target>
Here, under the Target ‘BuildRuby’, I’ve
got two Tasks, ‘MakeDir’ and ‘SteelRubyBuild’.
The first of these, MakeDir, is a standard MSBuild Task
that creates a directory. There are a number of these
Tasks covering some commonly used operations. But the
more interesting one is the ‘SteelRubyBuild’ Task.
This a home grown one. It’s actually a part of
a DLL I wrote that contains a number of related Tasks.
In this case, this particular Task (written in C#) runs
the Ruby interpreter via a .NET Process class. Simple
really, but very effective.
One thing to note about this Target is that the inputs
to the Target – the input dependencies, to be more
accurate – are specified as a list of Items. The
syntax is
Inputs="@(Ruby)"
This tells MSBuild to aggregate all the Ruby Items in
the build script and to pass the whole shebang to the
mechanism that MSBuild uses to determine if the tasks
within the target are to be called. You can either get
the Target Tasks invoked all the time (as here) or you
can use an ‘incremental’ build, which determines
which Items are passed to the target Tasks, only if the
input Item has been changed.
Note that individual Properties are substituted by the
$() syntax:
ProjectDirectory="$(MSBuildProjectDirectory)"
Some Properties are built into MSBuild (like MSBuildProjectDirectory)
while other you can define yourself. These Properties
are passed as arguments to the tasks which of course
do the real work of compiling, linking and so on that
any realistic build system requires.
I’ve been using MSBuild for a few months now,
and apart from having some initial difficulties with
the syntax (it doesn’t seem to have any sort of
grammar that I can find), I’ve found it to be a
simple and reliable tool. The incorporation of MSBuild
into the core of Visual Studio – there’s
a powerful way of using MSBuild directly via the BuildEngine
.NET classes – is a big plus. Currently, I’ve
only just scratched the surface of MSBuild to perform
Ruby syntax checking. The next step is to use MSBuild
to construct real and complicated web sites. It should
be interesting!
Here’s what the MSBuild XML looks like
in Visual Studio’s Solution Explorer. There’s
one peculiarity – the solution file itself isn’t
in MSBuild format. I’d expect to see this remedied
in the next version of Visual Studio.
As I mentioned earlier, one of the problems with the
old style Make systems was the lack of any serious debugging
facilities. MSBuild does provide quite a decent system
for finding out what is going on: simply invoke MSBuild
from the command line with the appropriate switches and
you’ll get more or less detailed output. One nice
touch – the output is in colour! This certainly
makes life quite a bit easier when you are trying to
see what is happening.
Debugging
MSBuild can often be conveniently done from the command
line. There are various levels of diagnostics that you
can select. Best of all, the output is colour coded!
There’s one other tool no
MSBuild developer should be without: MSBuild Sidekick.
This wonderful (and free) gizmo comes courtesy of Attrice
Corporation (http://www.attrice.info/msbuild).
With it you can display the rather user unfriendly XML
in a really easy to follow format. It still seems that
the simple ideas are often the best.
MSBuild Sidekick displays the
XML content of an MSBuild file in a nice structured format.
And the cost of MSBuild Sidekick? Zero!
June 2006 |