This blog post explains how to compile Java programs (e.g. a set of .java, .class and .jar files) to stand-alone binary executables which run on Linux, Windows etc. Each executable is platform-specific, but executables can be generated for many different platforms. A JVM or JRE on the target system is not required to run the executable.
The main idea is to use GCJ, The GNU Compiler for Java to generate the executable. For example, do it like this on Ubuntu Lucid, to generate a Linux executable:
Linux
$ cat >Hello.java <<'END' public class Hello { public static void main(String args[]) { System.out.println("Hello, World!"); } } END $ sudo apt-get install gcj $ gcj -v Target: x86_64-linux-gnu Configured with: ... Thread model: posix gcc version 4.4.3 (Ubuntu 4.4.3-1ubuntu4.1) $ gcj --main=Hello -g -o hello Hello.java $ ./hello Hello, World $ $ ls -l hello -rwxr-x--- 1 user group 13846 2012-06-30 15:20 hello $ ldd ./hello linux-vdso.so.1 => (0x00007fff01fff000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007fb352663000) libgcj.so.10 => /usr/lib/libgcj.so.10 (0x00007fb34f4fc000) libm.so.6 => /lib/libm.so.6 (0x00007fb34f278000) libpthread.so.0 => /lib/libpthread.so.0 (0x00007fb34f05b000) librt.so.1 => /lib/librt.so.1 (0x00007fb34ee53000) libz.so.1 => /lib/libz.so.1 (0x00007fb34ec3b000) libdl.so.2 => /lib/libdl.so.2 (0x00007fb34ea37000) libc.so.6 => /lib/libc.so.6 (0x00007fb34e6b4000) /lib64/ld-linux-x86-64.so.2 (0x00007fb35289a000)
If you don't want the executable to depend on libgcj, you can prepend -static-libgcj
to the gcj
command-line, but that won't work with the stock gcj package on Ubuntu Lucid, because libgcj.a was not included in the package. However, if you compile your own GCC (and enable Java), that will support -static-libgcj
.
Windows
GCJ also runs on Windows: in Cygwin and MinGW. (You can generate Win32 executables with it.) GCJ 4.4.0 was released as part of MinGW, but GCJ's setup process is mostly undocumented and contains lots of gotchas. You can't just install the latest MinGW, because after GCC 4.4, MinGW doesn't include GCJ in GCC. So, for your convenience, I created a ZIP archive containing GCJ 4.4.0 for Windows (using MinGW) and all its dependencies. Just download http://pts-mini-gpl.googlecode.com/files/wgcj44-r1.zip, extract it, and start running bin/gcj .
It is a working GCJ (GNU Java Compiler) 4.4.0 compiled for Win32. You can use it to compile .java and .class files to Win32 .exe files. The generated .exe is stand-alone, it doesn't need a JDK or JRE, and it can run on any Win32 system. (There are some bugs, restrictions and incompatibilities between e.g. OpenJDK and GNU ClassPath, the Java standard library GCJ 4.4 uses. So your Java programs won't work out-of-the-box, but it's possible to make small porting changes to make them work.)
It has been tested and found working on Windows XP and Wine 1.2 on Ubuntu Lucid. So you don't need a Windows machine in order the be able to release a Windows .exe version of your Java program. Just run the GCJ 4.4.0 above using Wine on Linux (or Mac OS X etc.) to generate the .exe .
The binaries are from MinGW (thus they are free software under the GPL, parts under different free licenses, see MinGW's license). Most of the files were extracted from archives downloaded from this MinGW download page.
Example invocation in debug mode (with line numbers in exception stack traces):
bin\gcj -static-libgcj -static-libgcc --main=Prog -g -o prog.exe P*.java
This will generate a 44 MB binary. It's pretty huge, but it contains a whole JRE and the Java standard library with debug symbols.
Example invocation in optimized mode (without line numbers in exception stack traces):
bin\gcj -static-libgcj -static-libgcc --main=Prog -s -O2 -o prog.exe P*.java
This will generate a 13 MB binary.
If you get ExceptionInInitializerError when trying to use the classes Date, SimpleDateFormat or Calendar, e.g.
Exception in thread "main" java.lang.ExceptionInInitializerError at java.lang.Class.initializeClass(t.exe) at java.util.Calendar.getInstance(t.exe) at t.main(t.exe) Caused by: java.lang.NullPointerException at java.io.InputStreamReader.read(t.exe) at java.io.BufferedReader.fill(t.exe) at java.io.BufferedReader.readLine(t.exe) at java.util.Properties.load(t.exe) at java.util.Properties.load(t.exe) at java.util.Calendar.(t.exe) at java.lang.Class.initializeClass(t.exe) ...2 more
, then add libgcj_properties.a like this (without the line break):
bin\gcj -Wl,--whole-archive -lgcj_properties.a -Wl,--no-whole-archive -static-libgcj -static-libgcc --main=Prog -s -O2 -o prog.exe P*.java
You can add the bin directory to the PATH. After that you can invoke gcj
directly (without the bin\
).