<< Here Comes The Cute Logo ... | Home | Thursday Ant Quiz >>

Java, Bash, Cygwin, ...

I haven't used the A, B, C, ... style title for a while. Today's post calls attention to all distributors of Java products that provides a Windows batch file and a Unix shell script to run their product:

YOUR SHELL SCRIPT DOESN'T WORK FOR CYGWIN

The problems usually stem from the assumption that a shell script works in the Unix like environment where paths are separated by a ":". Well, for Java under Cygwin, this assumption is no longer valid because the Windows version of the JDK commands such as java or javac expect the CLASSPATH environment variable and the arguments to the -classpath and -bootclassapth command line options to be in the Windows format—with a ";"-separator.

As an example, here is the java shell script shipped with the CICE/ARM closures prototype:

#!/bin/sh
CICEARM_HOME=`dirname $0`/..
java -Xbootclasspath/p:$CICEARM_HOME/lib/rt.jar "$@"

There are two problems with this script. First, it's self recursive, so you cannot put the directory that contains this script into your PATH, or it will call itself in an infinite loop. That's easily fixed by resolving the java call not through the PATH but through JAVA_HOME:

#!/bin/sh
CICEARM_HOME=`dirname $0`/..
"${JAVA_HOME}/bin/java" -Xbootclasspath/p:$CICEARM_HOME/lib/rt.jar "$@"

That brings us to the second problem: the mis-interaction between the Unix style directory names and path separators that bash uses and the JDK commands. If I install the CICE/ARM prototype in the /opt/cicearm-b05 directory, and add /opt/cicearm-b05/bin to my PATH, the above script will expand a simple java Foo command into:

C:\Program Files\Java\jdk1.6.0_10/bin/java -Xbootclasspath/p:/opt/cicearm-b05/bin/../lib/rt.jar Foo

Notice here that we are passing a Cygwin style path /opt/cicearm-b05/bin/../lib/rt.jar to the java command, which will interpret the path as a Windows style path. Most of the time, this will lead to the wrong result.

Fortunately, Cygwin provides a command cygpath that can be used to convert the Cygwin style path into a Windows style path. With the help of this command, and a little bit of uname magic to detect if the bash script is running in Cygwin, we can make the script run properly in Cygwin:

#!/bin/sh
CICEARM_HOME=`dirname $0`/..

# detect Cygwin
cygwin=false;
case "`uname`" in
  CYGWIN*) cygwin=true;
esac

BCP=${CICEARM_HOME}/lib/rt.jar

if $cygwin; then
  BCP=`cygpath --path --windows $BCP`
fi

"${JAVA_HOME}/bin/java" -Xbootclasspath/p:${BCP} "$@"

I know there are not a whole lot vocal Cygwin users out there. Most users would just fix their bash scripts for Cygwin quietly and move on. But for projects still in development, doing the same modification for every snapshot release becomes tiresome very quickly. That's why I usually send my modifications back to the project. And sometimes they even took it.

Tags :


Re: Java, Bash, Cygwin, ...

I have a couple points to note about your examples. First, your -Xbootclasspath/p:... argument should be quoted, in case ${BCP} includes spaces after converting it to a Windows path. Using --dos would work too. Second, I don’t have JAVA_HOME set, in Windows or Cygwin. (Or in Debian.) At least with my versions of Java and Cygwin, you can work it out:
if [ "$cygwin" = true -a -z "$JAVA_HOME" ]; then
  java_ver=$(regtool get "/machine/SOFTWARE/JavaSoft/\
Java Runtime Environment/CurrentVersion")
  if [ "$java_ver" ]; then
    javahome=$(regtool get "/machine/SOFTWARE/JavaSoft/\
Java Runtime Environment/$java_ver/JavaHome")
    if [ "$javahome" ]; then
      JAVA_HOME=$(cygpath "$javahome")
    fi
  fi
fi
There are different registry paths for the JDK instead of the JRE. This assumes you want JAVA_HOME in Cygwin format, not Windows format.

Re: Java, Bash, Cygwin, ...

I've been playing with OpenJMS a bit recently, and even though I'm using Linux for this particular project, their scripts do seem to account for Cygwin. So kudos to them. But this whole thing reinforces my current notion that cygwin is not the way to go. I'm fine with bash on Linux, and just as fine with cmd.exe on Windows. If I need a gcc/make build environment mingw with msys works great. Cygwin leads to these half-here and half-there situations like /cygdrive/d vs d:/ vs d:\ and CR vs CRLF. I don't need this extra complexity.

Re: Java, Bash, Cygwin, ...

Many big projects, such as Apache Ant and Apache FOP, have shell scripts that work right in Cygwin.

They end up that way not because the projects believed in Cygwin, but because the Cygwin script patches sent in by people like me are non-threatening and taking them couldn't hurt anybody else.

Not everyone need to become a believer of Cygwin. Only one is enough.

Re: Java, Bash, Cygwin, ...

In general, only one is not enough. Submitting a patch is nice, but that script must be maintained as the script evolves in the future. If the project developers have little interest or experience in maintaining Cygwin-specific bash scripts (especially if they are already providing Windows-specific batch files) then it seems reasonable not to include the patch.

Re: Java, Bash, Cygwin, ...

Well, of course.

I'm trying to motivate my fellow Cygwin users to send in the patches. In that regard, zero certainly is not enough.

As for your stated fear of "If we take this patch, we'll need to support it forever", that needs not be the case. An alternative thought would be "we'll take it. It won't break any existing users. And I'll rely on the same guy to send in patches if we break it in the future."

Re: Java, Bash, Cygwin, ...

Regarding the CICE/ARM prototype, I'll be happy to incorporate your suggested Cygwin changes into the script in the next build. Thanks for pointing it out :)

Re: Java, Bash, Cygwin, ...

That's cool.

Re: Java, Bash, Cygwin, ...

Would it be too unreasonable to expect java and javac to have some flags for different environments? The TCL folks figured out a while back that if you want "write once run everywhere" to work, you have to factor OUT O/S dependencies. Paths should be stored internally in an O/S independent manner. The compiler/interpreter needs to be smart enough to translate.

Add a comment Send a TrackBack