Sunday, 26 June 2005

Compiling for older JDKs

Most developers seem to assume that they can simply set the javac setting to output 1.2 or 1.3 compatible bytecodes, and they can then compile using JDK1.4 or 1.5. But, this doesn't work.

The problem is that the Java class libraries have changed between JDK1,3 and 1.4 (and so on). Each change provides the potential to break the build.

For example, in JDK1.4 a new method was added to StringBuffer - append(StringBuffer). In itself, this was not newsworthy. However, this method overloads (not overrides!) other methods, notably append(Object).

If you compile using JDK1.4, and you are appending a StringBuffer to a StringBUffer, javac will choose the nice JDK1.4 append(StringBuffer) method. When you use this JAR on JDK1.3 however, you will get a NoSuchMethodError (not Exception!). This is because overloaded methods are bound into the bytecode at compile time.

Oh, and that 'use 1.3 bytecodes' flag in javac? Well that makes no difference. It just avoids writing bad bytecodes, and doesn't check for valid methods.

I'm writing this because it has come up twice recently on commons-dev. Firstly, during the release of Jelly 1.0, where it took quite some effort to convince people of the problem. Secondly, during ongoing work on Email 1.0, where exactly this issue occurred.

The only safe solution is to:

  • compile the JAR file using the lowest JDK supported by your open source library
  • make sure that is the JAR file released to ibiblio's maven repository
  • include that jar file in the source distribution as well as the binary
  • make sure that the manifest file correctly references the lowest JDK version
And to achieve this I use ant and not maven.

Its not a maven issue per se, but maven does require a higher JDK to run itself than most of the libraries I release. Now, I am told that maven can build using a different JDK version to that which it runs on (maven.compile.executable). Well, great if that works. But, I'm not sure that I trust it to actually do all the necessary steps.

Hence my appoach - all compilation steps are done with ant. All website steps are done with maven. Just using the right tool for the right problem in my book.

2 comments:

  1. I have a similar situation at work. The programs I write need to work on Personal Java, which is basically a subset of the JDK 1.1 API (with a smidgen of JDK 1.2 APIs, just to spice things up). Unfortunately, my code sometimes runs afoul of a compiler bug that wasn't fixed until JDK 1.4, so compiling with the JDK 1.1 compiler is out of the question. The solution of course is to set *both* the -source and -target compiler switches to 1.1 and provide the JDK 1.1 classes.zip in the -bootclasspath switch. Doing all three things means there are no compatibility problems.

    ReplyDelete
  2. Yup, -bootclasspath is what you want. There have been various bugs fixed in javac over the versions. I had a situation in which an old javac was creating illegal byte code just as my system went live. Generally it is safest to use the latest JDK with -source, -target and -bootclasspath.

    ReplyDelete