So today I figured it would be an interesting excercise to write a small Gtk# application with Java. To do this, I used IKVM the Java VM implementation that runs on top of .NET and Mono.
There are two possible ways of using IKVM: one is to use it as a Just-in-Time compiler which translates the Java bytecodes into .NET Intermediate Language as it goes. But this means that at runtime you are compiling things twice: the Java-to-CIL JIT and the CIL-to-Native JIT.
Gtk# is really a bad name. Because Gtk# is not limited to C#, any programming language in the .NET framework can use it today and because it covers more than only the Gtk API, it covers various other components of the GNOME Development Platform.
First, you need to get your hands on Mono and Gtk#, you can get these from Mono Download page and Gtk# from their sourceforge page, or if you are lazy like I am, you use the packages on Red Carpet.
The next step is to download IKVM.
Now, Gtk# is a .NET assembly (this is the ECMA lingo for "library"), and Java does not know anything about this. It is necessary first to generate some stubs for these classes to let the Java compiler knows about the types defined in the C# world. This is done using the netexp.exe program from IKVM, like this:
$ mono netexp.exe /mono/lib/mscorlib.dll $ mono netexp.exe /mono/lib/gtk-sharp.dll $ mono netexp.exe /mono/lib/glib-sharp.dll $ mono netexp.exe /mono/lib/atk-sharp.dll
The above commands basically "imports" all of the types and their definitions into something suitable for Java to consume, the result is:
$ ls *.jar atk-sharp.jar glib-sharp.jar gtk-sharp.jar mscorlib.jar
The netexp.exe program will import all of the types into the "cli" namespace. So if you had a class called "Gtk.Window", it will be exposed to Java as "cli.Gtk.Window".
So I begun with a small Java program, I had to Google for Hello World, since I did not remember much of Java, and I have to say, I am still not comfortable with the classpath thing. Anyways here is my sample little program:
import cli.Gtk.*; public class Demo { public static void main(String[] args) { Application.Init (); Application.Run (); } }
To compile the above program type:
$ javac -classpath gtk-sharp.jar Demo.java
This produces a Demo.class file that contains the Java bytecodes. The -classpath file instructs the Java compiler to find the type definitions on the gtk-sharp.jar file that we had previously produced with netexp.exe.
Now, it is not possible to run this directly in Java, since the jar files produced by netexp.exe are only stubs, so we will need to run this in the Mono world using IKVM in JIT mode:
$ mono ikvm.exe -classpath .:gtk-sharp.jar Demo
The above just sits there waiting for events, so feel free to kill that. We will now add some meat to the program, this is a slightly more interesting sample:
import cli.Gtk.*; import cli.GLib.*; public class Demo { public static void main(String[] args) { Application.Init (); Window w = new Window ("Hello Mono with Java#"); Button b = new Button ("Click me"); w.Add (b); w.ShowAll (); Application.Run (); } }
To compile, you will need to reference the libraries we created before:
$ javac -classpath 'gtk-sharp.jar:glib-sharp.jar:atk-sharp.jar: mscorlib.jar:.' Demo.java
This will produce Demo.class, now run it:
$ mono ikvm.exe -classpath .:gtk-sharp.jar Demo
The result is shown here:
The above is basically translating Demo.class from JVM bytecodes to ECMA CIL, then the Mono JIT translates that into native x86 code. The next step is to precompile the Java code directly into .NET code, which will skip the double JIT process:
$ mono ikvmc.exe -reference:`pwd`/classpath.dll Demo.class \ gtk-sharp.jar $ ls *exe Demo.exe $
The above compiled the code directly into a a Mono/.NET executable, to run it, just do:
$ mono Demo.exe
But we can go one step further. We can avoid completely the JIT process by precompiling the .exe file which contains instructions in the ECMA Common Intermediate Language into native x86 code using Mono's Ahead-of-Time compiler, to do this, type:
$ mono --aot Demo.exe $ ls Demo.* Demo.class Demo.dll Demo.exe Demo.exe.so
The Demo.exe.so contains the native x86 code. Mono still requires the Demo.exe file to be around to extract metadata, but the Just-in-Time compiler will not be used in that case:
$ mono Demo.exe
Mono detects the shared object file and resolves methods to those contained in the image file. You can see the native code generated by using the objdump command:
$ objdump -d Demo.exe.so ...
Caveat: in some versions of IKVM references to System.IntPtr are turned into gnu.classpath.RawData, and your program might not compile in the javac, to fix this problem do:
$ echo "package gnu.classpath;" > RawData.java $ echo "public class RawData {}" >> RawData.java $ javac RawData.java $ mkdir -p gnu/classpath $ mv RawData.class gnu/classpath
Mono and IKVM depend on the GNU Classpath, so it is as complete as open source Java, but you get the added advantage of accessing any Mono/.NET libraries as well.
Today Mono/IKVM can run large applications like Eclipse, Jython and JBoss. Screenshot of Eclipse running on Mono.