Setting up multiple Java VMs under Cygwin
It is useful to have different versions of the JVM installed, for a number of reasons:
- Different optimisation features from different JVM implementations
- Different language features from different JVM versions
- Java classes compiled with “Tiger” won't run in “Mantis”…
It is also useful to be able to quickly switch between installed JREs/JDKs depending on the task at hand.
If I'm hacking in Linux, the JPackage project provides a much nicer solution to this problem, and the Linux distro' I'm using (SUSE 10.0) uses JPackage. It'd be nice if there was an update-alternatives
for Cygwin, but since there isn't I've come up with this hack.
2007-03-09T08:06+1100 - Update: Neater
JVersion
code I've recently upgraded to Mustang and noticed that this broke my function, since it installs to a different base directory again… So here is a new version that hopefully covers off future default directory names that Sun comes up with for Dolphin and the Open Source releases.
# Path variables
if [ x"$X_PATH_NO_JAVA" = x ]; then
export X_PATH_NO_JAVA=~/bin:$PATH # save path without JAVA, for future switches
fi
if [ x"$JAVA_BASE" = x ]; then
JAVA_BASE=d:\\java # default if not set in Windows
fi
# Functions
function JVersion() {
# Select a Java Development Kit to use.
# This provides similar functionality in Cygwin, to the
# updatealternatives command of Debian/SuSE linux.
#
# Note, different releases have different standard dirs,
# so we just look for "k" which seems to be the
# thing they still have in common. Also the sed scripts
# which pull out the version number installed, for
# reporting, must be specific for each release...
#Convert JAVA_BASE to Cygwin format
export JAVA_BASE=`cygpath -up $JAVA_BASE`
# Use function's argument to select, defaulting to Mantis
case $1 in
1.6 | 1.6.0 | 6.0 | 6 | mustang | Mustang | MUSTANG)
export JAVA_VER=`ls $JAVA_BASE | grep k1.6.0`
JVERNUM=`echo $JAVA_VER | sed s/^jdk//g`
;;
1.5 | 1.5.0 | 5.0 | 5 | tiger | Tiger | TIGER)
export JAVA_VER=`ls $JAVA_BASE | grep k1.5.0`
JVERNUM=`echo $JAVA_VER | sed s/^j2sdk//g`
;;
* | MANTIS)
export JAVA_VER=`ls $JAVA_BASE | grep k1.4.2`
JVERNUM=`echo $JAVA_VER | sed s/^j2sdk//g`
;;
esac
#Set the JAVA_HOME variable (used by some Java programs, so
#must be in Windows format for those programs to understand)
export JAVA_HOME=`cygpath -wp $JAVA_BASE/$JAVA_VER`
#Add the correct JDK runtime to Cygwin's path
export PATH=$JAVA_BASE/$JAVA_VER/bin:$X_PATH_NO_JAVA
#Report what happened
echo Java Version: $JVERNUM
echo Java Base: $JAVA_BASE
echo "Java Home: $JAVA_HOME (`cygpath -up $JAVA_HOME`)"
}
#Now run it to set up initial Java environment:
JVersion > /dev/null
This is all you need now, no complicated set-up variables and such rubbish. You'll notice that the setup steps are now just to set $X_PATH_NO_JAVA
(save the $PATH
without Java in it), and set up $JAVA_BASE
if necessary. Then after the function is declared and I run it to set a default Java environment for Cygwin (Still Mantis for now, since my work is using it for a system I support).
Read on for old stuff and false-starts...
...
I have a shell function called JVersion
which you use to select the version of the JVM you want to use, and a bunch of environment variables to make it all work. Here's how you use it:
$ JVersion tiger Java Version: 1.5.0_04 Java Base: /cygdrive/d/java Java Home: /cygdrive/d/java/j2sdk1.5.0_04 $ java -version java version "1.5.0_04" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05) Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing) $ $ JVersion mantis Java Version: 1.4.2_05 Java Base: /cygdrive/d/java Java Home: /cygdrive/d/java/j2sdk1.4.2_05 $ java -version java version "1.4.2_05" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_05-b04) Java HotSpot(TM) Client VM (build 1.4.2_05-b04, mixed mode) $
When using Java from the command-line (DOS or Cygwin), you need to have the interpreter (for JRE) and development tools (for JDK) on the system search PATH. You also used to have to set the CLASSPATH for the JRE to find bootstrap and extension classes, but these are now found automatically via the System property "sun.boot.class.path
" and the Java Extension Mechanism. However, it's still useful to have a JAVA_HOME variable for some third-party tools, such as IDEs.
I've written a bash function which uses some control variables to allow easy switching of JVMs on Cygwin. It's still a bit of a hack, but I find it useful when I'm on Windows to work from the bash prompt as well as whatever IDE I'm hacking in.
The control variables are
-
JAVA_BASE
= the base directory where all the JVMs are installed. I put each JVM in it's own directory underD:\java
This variable should be in the Windows environment, but it defaults tod:\java
in my.bashrc
if it was not set (i.e. I forgot to):if [ x"$JAVA_BASE" = x ]; then JAVA_BASE=d:\\java # default if not set in Windows fi
I'm sticking to DOS pathnames for this variable, as I intend to replicate this functionality with batch files one day, in case I'm mad enough to run Java from a Windows command shell. Anyway, I then convert this to a Cygwin path with the following
sed
(1) andawk
(1) hack:JAVA_BASE=`echo $JAVA_BASE | sed 's////g'` # fix DOS sloshes export JAVA_BASE=`echo $JAVA_BASE | awk '{print(tolower($1))}' \\ | sed 's/d://cygdrive/d/g'` # fix drive
-
JAVA_MANTIS
= the version (without the release part) corresponding to “Mantis” -
JAVA_TIGER
= the version (without release) corresponding to “Tiger” -
JAVA_VER
= the specific version and release of the currently selected JVM (e.g. 1.5.0_04)if [ x"$JAVA_MANTIS" = x ]; then export JAVA_MANTIS=`ls $JAVA_BASE | grep 1.4.2 \\ | sed s/^j2sdk//g` fi if [ x"$JAVA_TIGER" = x ]; then export JAVA_TIGER=`ls $JAVA_BASE | grep 1.5.0 \\ | sed s/^j2sdk//g` fi if [ x"$JAVA_VER" = x ]; then export JAVA_VER=$JAVA_MANTIS # default if not set fi
-
JAVA_HOME
= the install directory of the currently selected JVM (e.g.d:\java\j2sdk1.4.2_05
) X_PATH_NO_JAVA
= the system search path, without a JVM
PATH = the system search path
The aim is to set JAVA_HOME and PATH so that the Java tools can be found. They are set using the previous variables:
export JAVA_HOME=$JAVA_BASE/j2sdk$JAVA_VER if [ x"$X_PATH_NO_JAVA" = x ]; then export X_PATH_NO_JAVA=~/bin:$PATH # save path without JAVA fi export PATH=$JAVA_HOME/bin:$X_PATH_NO_JAVA
So, the above code is run inside my .bashrc
, which takes care of finding the installed JVMs and setting Mantis as the default to use. But what about switching? Well, here's the shell function:
function JVersion() { case $1 in 1.5 | 1.5.0 | 5.0 | 5 | tiger | Tiger | TIGER) export JAVA_VER=$JAVA_TIGER ;; *) export JAVA_VER=$JAVA_MANTIS ;; esac JAVA_BASE=`echo $JAVA_BASE | sed 's////g'` # fix DOS sloshes export JAVA_BASE=`echo $JAVA_BASE | awk '{print(tolower($1))}' \ | sed 's/d://cygdrive/d/g'` # fix drive export JAVA_HOME=$JAVA_BASE/j2sdk$JAVA_VER export PATH=$JAVA_HOME/bin:$X_PATH_NO_JAVA echo Java Version: $JAVA_VER echo Java Base: $JAVA_BASE echo Java Home: $JAVA_HOME }
You may have noticed that this hack is rather brittle. It works well, but only so long as you follow these steps:
- Install your Javas on your
D:
drive (or you have to fix all of thesed
scripts!) - Have a “base” directory, such as
D:\java
- Have a Windows environment variable called
JAVA_BASE
equal to the “base” directory - Install your Javas each in a sub-directory of your “base”, called
j2sdkw.x.y_z
Additionally it only caters for a single release of each Java version, and only for Java versions “Mantis” (1.4.2) and “Tiger” (1.5.0). If you want to install “Mustang” and switch to it, you'll need to add a JAVA_MUSTANG
variable, equal to 1.6.0 and also add a case for 1.6.0 to both the JAVA_VER
setup code and the JVersion
case
statement… ugly, but I haven't come up with a more elegant solution yet. Ultimately I'd like to make a proper replacement for update-alternatives
, but since symlinks will only work for the Cygwin tools, and not the Java tools themselves, it seems unlikely I'll come up with a nicer solution.
A note about paths in Java on Cygwin:
This is noted in the Cygwin docs somewhere I think, but just a reminder: java.exe
, javac.exe
, etc. are Win32 programs, and are not linked to the Cygwin DLLs in any way. So always they will need DOS style paths (D:\somedir\etc
). They won't understand Cygwin's /cygdrive/d/whatever
. Also as I just alluded to, any Cygwin links will not be followed by the Java tools, for the same reason. In bash
(1), you'll need to escape the sloshes if you're setting the classpath on the command line:
java -classpath D:\\\\java\\\\special-classes\\\\somefile.jar MyClass
Since java transposes the /
and \
characters itself, it may be possible to do the following:
java -classpath D:/java/special-classes/somefile.jar MyClass
But I haven't tested it…
2006-06-17T16:39+1000 - Update: Cygwin's cygpath
(1) command I should have seen this before spending so long playing with sed and sloshes…Cygwin's cygpath
(1) command can be used to fix the Unix/DOS path stuff, much simpler than mucking about with sed
and awk
. So in the above, instead of code like this:
JAVA_BASE=`echo $JAVA_BASE | sed 's////g'` # fix DOS sloshes export JAVA_BASE=`echo $JAVA_BASE | awk '{print(tolower($1))}' \\ | sed 's/d://cygdrive/d/g'` # fix drive… that mess can be replaced replaced with this:
export JAVA_BASE=`cygpath -up $JAVA_BASE`
… (using back-quotes, or the $()
notation if you prefer). Similarly, to run Java with a classpath specified in a Cygwin format (i.e. from a Cygwin script or prompt):
java -classpath `cygpath -wp /cygdrive/d/java/special-classes/somefile.jar` MyClass