John Lam has a nice doc about MSBuild, Microsoft's XML based build system of the next Visual Studio version. This issue has far more information than has publicly been available yet.
You may need a Windows machine running Acrobat Reader to read it as both xpdf on Linux and preview on MacOS X seem to be lacking the fonts necessary for it.
path: /en/dotNet/msbuild | #
Alex Kipman posts pointers to an online version of chapter 2 of Brent Rector's book.
The part talking about Ant and NAnt has been completely rewritten and now looks a lot nicer and more informed than the original version (search for the English language text under TLS347).
I'm sure Alex has played a significant role in this and want to thank him - as well as I want to thank Brent for reconsidering the section.
Brent's main point now is that Ant places the burden of timestamp based dependency tracking on task writers, I think I've addressed this already. I obviously still think that Ant's choice is the better one 8-)
The other point is about "build scripts" vs. "project manifest", that I'll have to think about a bit more.
path: /en/dotNet/msbuild | #
In Target Dependencies in MSBuild vs. Ant I claim that Ant's model was more powerful than the file name based approach taken by make and MSBuild's target inputs/outputs dependencies.
Let me give some reasons:
The name mangling performed by a task can be quite complex.
Let's take rmic as an example. If you use RMI over IIOP, it creates
a/b/c/_D_Stub.class
fora/b/c/D.class
ifD
is an interface. If you want to express this with patterns, you have to add full regular expression support to the core of your build system (or something similar), so users can specify (.*)([^/\.]+)\.class => \1_\2_Stub.class as a transformation rule.What's even worse than this IMHO, is that the user has to specify this transformation at all. With Ant's <rmic>, the user doesn't have to care for the name mangling rules.
Name mangling may depend on the value of an attribute.
Continuing with the rmic example, the above rules apply only if you use
iiop=true
, otherwise it would be the same resulting class but without the leading underscore.If the user decides to switch from IIOP to JRMP as transport, she will also have to modify the pattern in the build file. No need to do so in Ant.
Name mangling may even depend on the content of the file.
Once again rmic. If you use IIOP and the class is a concrete class and not an interface, you get two files. One is
a/b/c/_D_Tie.class
and an additional_Stub
class for the interface implemented byD
.If you want to capture this upfront, you have to state the dependency explicitly. No pattern matching is going to work. You end up with one target per concrete class that way - and the user has to keep it up to date. Ant's rmic uses reflection on
D
to find the correct names.The same applies to source files generated from Corba IDL (in case Java's RMI is too esoteric). The file name of the generated source doesn't have to be related to the name of the IDL file at all. You can hard-code the file names or you can write a smart task for this purpose.
Name mangling depends on a concrete implementation of an abstract, vendor-neutral task.
Again rmic. Ant supports pluggable back-ends for this, and one of them is the rmic compiler of Weblogic which uses a different name mangling scheme. So the user will have to know which tool is going to be used and adapt the patterns in the build file to it. The same is true for <jspc> for example where the generated names depend on the container.
I understand that vendor-neutral tasks are not that important for MSBuild. ;-)
Non-local dependencies.
What if your source or target is a file reached via ftp or scp? Build ftp, WebDAV, ssh, cvs, VSS and what-not support into the core of the tool?
Non-file dependencies.
<jar> allows users to specify the manifest in-line.
The task reads the original manifest of an existing archive and compares it to the manifest it would create now. If they are different, the archive gets recreated. To achieve the same with file dependencies, the archiving target must use the build file itself as an input file - and may end up recreating the archive much more often than necessary.
What if you want to do something based on interactive user input?
Sometimes you want to get around dependency checking.
See Ant <copy>'s overwrite attribute for example, it will copy files even when the target is newer than the source.
Tasks that delete files.
How do you specify that the result of a target is the removal of input files?
Complex class dependencies
Say class
A
extends classB
and they end up in two different assemblies. Now you changeB
, changing its public or protected API, you should recompileA
as well.Ant's <javac> doesn't catch this, neither can a simplistic input/output dependency. Possible solutions are (1) hard-coding the dependencies, creating targets for each such dependency. (2) write a smart task that removes the assembly that contains
A
, this is what Ant's <depend> task does - see the previous point, how do you express this? (3) write an even smarter task that understands the programming language, sorts out what needs to be recompiled and then invokes the compiler, here it is for Ant and Java.
Like I previously said, you can create smart tasks in MSBuild as well. But if you start out with the target input/output checking in your build file, switching to smarter tasks may become a steep learning curve. You may even have to implement them yourself when the shipped tasks are not smart enough.
path: /en/dotNet/msbuild | #
If you should be reading Brent Rector's "Introducing Longhorn for Developers" by Microsoft Press that has been handed out at PDC apparently, make sure you also read what a Program Manager of Microsoft's Visual Studio Core Team says about the NAnt chapter.
Update: a revised version of the chapter is now online at MSDN and this will be the version that's going to appear in press.
path: /en/dotNet/msbuild | #
Last post on MSBuild for today, promised.
MSBuild supports something called Item. This is basically the same as a data-type with an id attribute in Ant. Its main use in MSBuild is to specify inputs and outputs of tasks and targets - in Ant the references are only used for task inputs.
While I don't like the inputs/outputs for Target too much, I think the idea to have a task accept a reference as input and provide its output as a reference is useful. For some tasks it may be difficult to calculate the output, if possible at all, but as a general concept this may work.
The input for tasks like <copy> could be some type of file-collection, as would be the output. Let's assume we want to copy some files and add those that we've just copied to an archive.
What we'd do with Ant now
<fileset id="templates" dir="styles"> <patternset id="template-patterns"> <include name="**/*.xsl"/> </patternset> </fileset> <copy todir="${build}"> <fileset refid="templates"/> </copy> <zip destfile="files.zip"> <fileset dir="${build}"> <patternset refid="template-patterns"/> </fileset> </zip>
Let's assume all task can specify inputs (<copy> and <zip> would accept <fileset> among other things as inputs) and outputs via inputItems and outputItems attributes respectively. inputItems points to an existing project reference, outputItems creates such a reference.
Then we could write:
<fileset id="templates" dir="styles"> <include name="**/*.xsl"/> </fileset> <copy todir="${build}" inputItems="templates" outputItems="copy-of-templates"/> <zip destfile="files.zip" inputItems="copy-of-templates"/>
We could even create a special TaskContainer that magically connected the output of task n
to the input of task n+1
, avoiding temporary references like "copy-of-templates":
<fileset id="templates" dir="styles"> <include name="**/*.xsl"/> </fileset> <pipe> <copy todir="${build}" inputItems="templates"/> <zip destfile="files.zip"/> </pipe>
Something that needs more thought, of course.
path: /en/dotNet/msbuild | #