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 | #
MSBuild like NAnt allows conditions to be placed on all tasks, targets and the grouping elements for properties and items. Ant allows conditions on targets and a very small subset of tasks (basically <fail> and nothing else).
MSBuild and NAnt conditions can test for String (in)equality and file existence/absence. NAnt adds a couple of more conditions and supports boolean AND when multiple attributes are used. In Ant, the only condition you can test at the task/target level is whether a property has been set or not.
So Ant looks quite limited in comparison. Ant makes up for much of this via the condition task. With this, you can set properties based on conditions, those conditions can do all of the things that NAnt and MSBuild can do plus try to access HTTP URLs, connect to arbitrary sockets, compare file checksums, ... . On top of that, and/or/not are supported as boolean operations.
The Ant-Contrib project adds an <if> task that leverages Ant's condition framework, and I would rather add <if> to Ant than have all tasks support conditions. Otherwise you are going to repeat the same condition over and over again.
But then again, I've never had a build situation complex enough to ever use the <if> task (and I'm the author of it). The first example for conditionals you see in the MSBuild docs is
<PropertyGroup Condition="'$(Config)'== 'DEBUG'"> <Property Optimize = "false" /> <Property OutputPath = "bin\Debug\" /> <Property WarningLevel = "1" /> </PropertyGroup> <PropertyGroup condition="'$(Config)'=='RETAIL'"> <Property Optimize = "true" /> <Property OutputPath = "bin\Release\" /> <Property WarningLevel = "4" /> </PropertyGroup>in Ant, I'd use <pre class="code"> <property file="${config}.properties"/> </pre> and two property files instead. YMMV.
path: /en/dotNet/msbuild | #
I should have opened the archives before I took them home. I don't have OpenOffice installed at home (and downloading it via my modem is no fun), so I couldn't read the docs that come as Word documents over the weekend.
In the meantime, Steve has had a first look at the docs as well. I agree with most of his points, but disagree on the target dependencies.
In Ant (and I think NAnt as well) targets depend on other targets, period. You declare the dependencies between targets and (N)Ant sorts out which targets to run in which order if you ask it to execute one.
In traditional make, you create files and those files depend on other files and you have rules that tell make how to build files from files. make will sort out which files to build when you tell it your "target" file. If your "target" cannot be expressed as a file, you need ".PHONY" targets, something that is the normal mode of operation in Ant.
This difference is one of the major stumbling blocks for people used to make when they come to Ant. All dependencies in Ant are "phony" and the file timestamp level dependencies are resolved by the tasks themselves. Ant doesn't avoid to run the target containing your compilation tasks by checking timestamps, it leaves this up to the <javac> task which looks at the timestamps.
Ant's model is more powerful1 as it allows tasks to perform smarter dependency checking that just looking at file timestamps. Like <zip> looking into the archive or <cc> parsing header files to calculate dependencies.
At the same time, it requires users to write a new task when all they need is a file-timestamp-checking on top of a target that performs a couple of operations. This is why we invented <apply> and <uptodate>. The former adds file dependency checking to <exec>, the latter sets a property that can be used in <target>'s if/unless attributes.
MSBuild supports both notions of dependencies. <Target> has a DependsOnTargets attribute that can be used in the way Ant's depends attribute works. In addition <Target> can specify its required inputs and the generated outputs. The inputs and outputs can be used by MSBuild to calculate the inter-target dependencies.
I'm not sure what happens when you use both dependency mechanisms together. It may lead to strange situations where the implicit dependencies and the ones stated explicitly in DependsOnTargets contradict each other.
Where Ant puts the burden of dependency checking onto the task writer, MSBuild does that in its core. Ant offers the functionality via utility classes, but the task writer still has to write the code herself.
MSBuild's model does not preclude smarter dependency checking in tasks (this is where I disagree with Steve), but it buries it under the more traditional make like dependency mechanism. I wonder whether for example the compilation tasks perform any file timestamp comparisons at all, the docs are not clear, but from my reading of the walk-through, I'd say they don't. In "Add dependency analysis to 301" they avoid recompilation in CSC by adding Inputs/Outputs to the Target containing the Task.
Supporting both types of dependencies inside MSBuild may be confusing to users. In the end I'd guess only one will survive and the other mechanism will become more or less unused. As it is probably easier to write a task without task-internal dependency checking, this may lead to "dumber" tasks than their (N)Ant counterparts.
If you wanted to write a Zip task that adds all files to an archive that are not already part of it (or are newer than the files inside of the archive) without making the task perform the tests, you'll end up with an incredibly complex dependency engine in the MSBuild core, though. Looking at the file name transformations that they already have to support now (prepend a directory, change the extension, ...), this is asking for trouble IMHO.
path: /en/dotNet/msbuild | #