2012-06-30

How to compile Java programs to stand-alone executables for Windows, Linux etc.

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\).

2012-06-28

pdfsizeopt released for Windows

This blog post is to announce that pdfsizeopt, a PDF file size optimizer is now available for Windows (Win32) systems. See the Windows installation instructions. Previously pdfsizeopt was available only on Unix systems, with Linux and Mac OS X explicitly supported.

pdfsizeopt is a program for converting large PDF files to small ones. More specifically, pdfsizeopt is a free, cross-platform command-line application (for Linux, Mac OS X, Windows and Unix) and a collection of best practices to optimize the size of PDF files, with focus on PDFs created from TeX and LaTeX documents. pdfsizeopt is written in Python, so it is a bit slow, but it offloads some of the heavy work to its faster (C, C++ and Java) dependencies. pdfsizeopt was developed on a Linux system, and it depends on existing tools such as Python 2.4, Ghostscript 8.50, jbig2enc (optional), sam2p, pngtopnm, pngout (optional), and the Multivalent PDF compressor (optional) written in Java.