Using my just published Ant tasks for NAnt and MSBuild and Ant's .NET Antlib I had some fun with the various things installed on my company notebook:

$ NAnt.exe -f:pingpong.build
NAnt 0.85 (Build 0.85.1932.0; rc3; 16.04.2005)
Copyright (C) 2001-2005 Gerry Shaw
http://nant.sourceforge.net

Buildfile: file:///c:/Dokumente und Einstellungen/stefan.bodewig/AntTask/pingpong.build
Target framework: Microsoft .NET Framework 1.1
Target(s) specified: runAnt


echo:

     [echo] This is NAnt

runAnt:

[loadtasks] Scanning assembly "AntTask" for extensions.
      [ant] Buildfile: pingpong.xml
      [ant]
      [ant] echo:
      [ant]      [echo] This is Apache Ant version 1.6.5 compiled on June 2 2005 on JDK 1.5.0_05-b05
      [ant]
      [ant] runMSBuild:
      [ant] [dn:msbuild] Microsoft (R) Build Engine Version 2.0.50727.42
      [ant] [dn:msbuild] [Microsoft .NET Framework, Version 2.0.50727.42]
      [ant] [dn:msbuild] Copyright (C) Microsoft Corporation 2005. All rights reserved.
      [ant]
      [ant] [dn:msbuild] Build started 21.12.2005 20:29:57.
      [ant] [dn:msbuild] __________________________________________________
      [ant] [dn:msbuild] Project "C:\Dokumente und Einstellungen\stefan.bodewig\AntTask\pingpong.proj" (runAnt target(s)):
      [ant]
      [ant] [dn:msbuild] Target echo:
      [ant] [dn:msbuild]     This is MSBuild
      [ant] [dn:msbuild] Target runAnt:
      [ant] [dn:msbuild]     Buildfile: pingpong.xml
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     echo:
      [ant] [dn:msbuild]          [echo] This is Apache Ant version 1.7alpha compiled on December 19 2005 on JDK 1.4.2_09-b05
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     runNAnt:
      [ant] [dn:msbuild]       [dn:nant] NAnt 0.85 (Build 0.85.1932.0; rc3; 16.04.2005)
      [ant] [dn:msbuild]       [dn:nant] Copyright (C) 2001-2005 Gerry Shaw
      [ant] [dn:msbuild]       [dn:nant] http://nant.sourceforge.net
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] Buildfile: file:///C:/Dokumente und Einstellungen/stefan.bodewig/AntTask/pingpong.build
      [ant] [dn:msbuild]       [dn:nant] Target framework: Mono 1.0 Profile
      [ant] [dn:msbuild]       [dn:nant] Target(s) specified: echo
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] echo:
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant]      [echo] This is NAnt
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] BUILD SUCCEEDED
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]       [dn:nant] Total time: 0 seconds.
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]
      [ant] [dn:msbuild]     BUILD SUCCESSFUL
      [ant] [dn:msbuild]     Total time: 1 second
      [ant] [dn:msbuild]
      [ant]
      [ant] [dn:msbuild] Build succeeded.
      [ant] [dn:msbuild]     0 Warning(s)
      [ant] [dn:msbuild]     0 Error(s)
      [ant]
      [ant] [dn:msbuild] Time Elapsed 00:00:01.24
      [ant]
      [ant] BUILD SUCCESSFUL
      [ant] Total time: 1 second
      [ant]

BUILD SUCCEEDED

Total time: 2 seconds.

This is NAnt on MS Framework 1.1 invoking Ant 1.6.5 on JDK 1.5 invoking MSBuild on MS Framework 2.0 invoking Ant's SVN trunk on JDK 1.4 (my default environment) invoking NAnt on Mono 1.1.12.

path: /en/dotNet | #

No, I'm not talking about the .NET Antlib again, this time it is the other way around. I've written my first tasks for NAnt and MSBuild and they invoke Ant.

I encountered some issues with MSBuild, mainly that I didn't find a way to define nested elements and that I can return true or false from the Execute method without any notable effect - I'll have to dig into it.

Sources, docs and binaries are available in http://stefan.samaflost.de/code/nant-msbuild-AntTask/AntTask-1.0.0.zip, please also check the archive's PGP signature (a link to my key is on the left).

path: /en/dotNet | #

Last month the Antlib has been promoted to an official subproject of Ant, Steve and Conor are now committers to it as well. We still need to update the site, though. Gump builds it nightly and runs the tests (well, only the ones for dotnetexec) on Mono.

I finally found some time to try MSBuild and immediately found that the task didn't work (the argument is /target not /targets). Well, now it does:

$ cat test.xml
<project xmlns:dn="antlib:org.apache.ant.dotnet">
  <dn:msbuild>
    <build>
      <Message Text="Hello world"
        xmlns="http://schemas.microsoft.com/developer/msbuild/2003"/>
    </build>
  </dn:msbuild>
</project>

$ ant -lib build/lib -f test.xml
Buildfile: test.xml
[dn:msbuild] Microsoft (R) Build Engine Version 2.0.50727.42
[dn:msbuild] [Microsoft .NET Framework, Version 2.0.50727.42]
[dn:msbuild] Copyright (C) Microsoft Corporation 2005. All rights reserved.

[dn:msbuild] Build started 15.12.2005 20:21:56.
[dn:msbuild] __________________________________________________
[dn:msbuild] Project "c:\Dokumente und Einstellungen\stefan.bodewig\Lokale Einstellungen\Temp\build1543310185.xml" (default targets):

[dn:msbuild] Target generated-by-ant:
[dn:msbuild]     Hello world

[dn:msbuild] Build succeeded.
[dn:msbuild]     0 Warning(s)
[dn:msbuild]     0 Error(s)

[dn:msbuild] Time Elapsed 00:00:00.10

BUILD SUCCESSFUL
Total time: 0 seconds

This means, the MSBuild task now sort of officially works, I've updated the snapshot at http://people.apache.org/~bodewig/dotnet/ and also provide a special version for Ant 1.6.x that is minimally limited as it won't support build snippets nested into the MSBuild task.

In fact, the "most recent" version I've uploaded will require a version of Ant's svn trunk not older than a few minutes since I had to teach Ant's DOMElementWriter some new tricks to make it support XML namespaces.

Those snapshots are only temporary, I'll come up with a release plan rather soon.

After my tests today, the NAnt, NUnit and dotnetexec tasks work reasonably well on Mono (tested on Linux and MacOS X) and Microsoft's .NET frameworks 1.1 and 2.0 - MSBuild requires Microsoft's 2.0. They could certainly use some more testing.

path: /en/Apache/Ant/dotnet | #

In What does this code print? Eric Gunnerson shows this C# code (int derives from object in C#):

using System;

public class A {
    public void F(int i) {
        Console.WriteLine("A: {0}", i);
    }
}

public class B : A {
    public void F(object o) {
        Console.WriteLine("B: {0}", o);
    }
}

public class Test {
    public static void Main() {
        B b = new B();
        b.F(1);
    }
}

And the answer B: 1 surprised me. So I tried the equivalent in Java (three source files)

public class A {
    public void f(Integer i) {
        System.err.println("A: " + i.toString());
    }
}

public class B extends A {
    public void f(Object o) {
        System.err.println("B: " + o.toString());
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.f(new Integer(1));
    }
}

and it printed A: 1. What I expected.

Before I dig deeper into the Java case, let's see why the C# version works the way it does. Eric explains that method dispatch at runtime will occur on the most derived class that holds a method that could satisfy a certain method invocation and then will search for the best match in that class. So here the runtime will find that F in B does match the invocation in Test and is never going to look at A.

The reason for this behavior, he says, is versioning. If you invoke any method on B and some time later anybody introduces a new method into A that would better match your invocation the runtime will still choose B's method.

So how does it work in Java? Sometimes I enjoy digging into the JLS, sick, I know. Java performs much of the method resolution at compile time. At compile time it will determine which method signature matches a certain invocation best - and will select A - and at runtime it will use the method in the most derived class that has the same signature which was determined at compile time.

This has interesting consequences. If you remove the definition of f from A and recompile everything, the the code will (certainly) print B: 1. Now add back f in A and only recompile A but not Test, it still prints B: 1. Only if you recompile Test as well, it will return to A: 1.

In a way Java solves the versioning problem, but only as long as you don't recompile your code against a newer version of A. While C#'s way is counterintuitive (to a seasoned Java developer, at least), it is more predictable at the same time. I'm not sure which way I prefer.

path: /en/dotNet | #

In the past I've been using cc-mode and just told it to use C++ for my C# coding on Linux or my Mac. Not terrific, but it worked.

A post on the Mono developer list pointed to http://mfgames.com/linux/csharp-mode and Dylan Moonfire followed up with a comment that he had just released version 0.4.0. It seems to play well with XEmacs (different other versions I tried all failed for me). Looks like I finally get "internal" highlighted as a keyword.

path: /en/dotNet/XEmacs | #