See it in action: CubeDemo.jar (4.7 MB) (Linux, Windows, and Mac OS X) (source)
NativeGuide is a Java utility library I wrote that makes native libraries easier to manage. Really, it’s a very small and simple library, but it provides some critical functionality I desire and I feel many libraries are sorely lacking. Since I would absolutely love for other Java libraries to start using it, I went through the extra effort to have it placed conveniently in the central Maven repository: com.nullprogram:native-guide.
As a side note, I actually worked out my own staging solution rather than rely on Maven — I always go out of my way to avoid Maven. For reuse-ability, I added it to my sample-java-project as the “bundle” target. It generates all of the required artifacts, digitally signs them, and bundles them up.
The demo at the top of this post contains native libraries for running OpenGL programs on seven platforms. NativeGuide takes care of getting the proper library in place for the JVM to load. All of this is hopefully transparent to you, the user, so that this appears to be a regular run-anywhere Java application.
The issue that NativeGuide solves is a special case of the distribution problem. Distribution is one of the major problems every language/platform must solve. That is, how do you get your application to the end user so that they can run it? In the case of a C or C++ application, you compile it for the user’s native system, either statically link or pack up the dependencies, and wrap it all up in an installer. Python and Perl have working solutions for different platforms. Ruby must have something figured out, though I don’t know what it is.
I believe this is one of Lisp’s biggest problems, because there’s no real solution yet. Assuming the user already has a Lisp system installed — a very big assumption — what do you deliver? FASL files aren’t compatible between Lisps and sometimes even between versions of the same Lisp. You’re left either targeting their specific Lisp system or distributing source — which either you can’t do or you really can’t expect the user to compile. That’s a long ways away from a simple double-click shortcut. If you somehow got that work, then how to you make it convenient to launch?
If the user doesn’t have a Lisp system installed you then have to package your own. Maxima does this, but their solution takes a lot of work and maintenance. It’s unreasonable for every application to do this. SBCL is pretty close to making that part work well, through a feature to save executable images. However, until very recently this came with lots of overhead. A “Hello, World” program would be around 60 MB, since it includes an entire Common Lisp system. Another problem is the program is entirely integrated to the particular SBCL with which it was compiled. The user can’t install a newer, potentially more secure, SBCL and run it with the new one.
I think Java’s method of application distribution is really nice. The
JRE is easy to install so it’s available almost
everywhere. Unfortunately, when it’s not installed and you try to
package a JRE with your application, the situation starts to look like
SBCL. Fortunately, that’s not a very common problem. Applications can
be packed up into a single
.jar file, including all the art assets,
configuration XML, and so on, and distributed as a single file, which
is usually easy to launch. Java provides special support for
accessing those packed up resources right from the packaging:
However, the same courtesy is not provided for packed up native
libraries. There’s no support for loading them from within a
file. What sometimes happens is these libraries end up being delivered
alongside the application. They have to be installed somewhere and
there has to be some kind of OS/architecture detection in the
launcher, the hardest place to do it, to determine the right libraries
to use. In a few cases they’re packed up with the application, which
copies them out to a temporary directory for loading. That’s even more
effort duplication, having to detect the OS and architecture again,
figuring out where exactly to copy them, what to do if they’ve already
been copied, maybe updating the
java.library.path which isn’t as
sample as just setting the system property.
Libraries often don’t worry about that part, leaving it to the users of the libraries to figure something out. Everyone using the library makes their own solutions in parallel when it would have been better if the work was done just once by the library. (A few libraries have done the right thing.)
The goal of NativeGuide is to make all of that mess go away. You pack
up your native libraries as resources and register them with
NativeGuide at run-time. NativeGuide detects which library the JRE can
use, copies the correct ones to a temporary directory, and appends
that directory to the
java.library.path if needed. For example, this
is what registration looks like,
NativeGuide.prepare(Arch.LINUX_32, "/x86/libexample.so"); NativeGuide.prepare(Arch.LINUX_64, "/amd64/libexample.so"); NativeGuide.prepare(Arch.WINDOWS_32, "/x86/example.dll"); NativeGuide.prepare(Arch.WINDOWS_64, "/amd64/example.dll");
Outside of this registration, the whole thing is transparent. I’ve
already used it with two projects allowing me to package them up into
.jar file as if no native library was being used. One is a
project at work which uses
RXTX and the other
is a branch of my sample-java-project:
allows me to produce a single
.jar OpenGL application that users on
Linux, Windows, and Mac OS X can just double-click to run.
So my hope is to get libraries using NativeGuide to save everyone time. The users of the library would probably be unaware that NativeGuide is there. Just like all of my code, I made it public domain, so there can be no license objections. However, if the library maintainers don’t choose use it, I made it flexible enough that you can work around them with little trouble.