How-to: Creating stand-alone executable with SBCL, cl-gtk2 and cl-launch

August 29, 2009

Let’s create a simple stand-alone GUI application. The program will be able to launch and show a window with “Hello, world” label.

To build GUI, we’ll use the CL-GTK2. The process will be described step by step.

Start up Slime and load CL-GTK2:

(asdf:oos 'asdf:load-op :cl-gtk2-gtk)

Create a source file (call it hello-world.lisp) with the following content:

(defpackage :hello-world
  (:use :cl :gobject :gtk)
  (:export :main :run))

(in-package :hello-world)

(defun main ()
  (within-main-loop
    (let ((w (make-instance 'gtk-window :title "Hello, world"))
          (l (make-instance 'label :label "Hello, world!")))
      (container-add w l)
      (connect-signal w "destroy" (lambda (w)
                                    (declare (ignore w))
                                    (gtk-main-quit)))
      (widget-show w))))

(defun run ()
  (main)
  (join-main-thread))

In this source file, hello-world package is defined that exports two functions: main and run. The ‘main’ function is used during development – it launches the program in a background thread (the within-main-loop macro does that) and returns immdeiately; and the ‘run’ function is used when launching the program not from Slime (it returns only when the application is closed).

The join-main-thread function waits until the Gtk+ main loop finishes. It finishes when the window is closed and the gtk-window widget receives the “destroy” signal.

We can start the program from the Slime’s REPL:

(hello-world:main)

After you enter this expression at the REPL, Gtk+ background thread will be started and the control returns to the REPL.

Now let’s create the stand-alone program that will not require Lisp compiler and Lisp libraries on the user’s machine.

First, we define the ASDF system.

To do this, we create the hello-world.asd file at the same directory where hello-world.lisp file is located with the following contents:

(defsystem :hello-world
  :name "hello-world"
  :components ((:file "hello-world"))
  :depends-on (:cl-gtk2-gtk))

This definition specifies the way of building the program: first load the cl-gtk2-gtk system and load the hello-world.lisp source file.

Usually ASDF system definitions are used to define systems that contain code of libraries and are placed (in Linux) into system-wide directory /usr/share/common-lisp/systems. But this time we define a system for a particular application and do not place it into this directory.

Now we can use cl-launch to create and executable file that contains all the Lisp code that is necessary.

First we install cl-launch. In Gentoo Linux with lisp-overlay this can be achieve with the simple command at shell prompt:

emerge dev-lisp/cl-launch

In other Linux distributions you can use the ASDF-INSTALL to install cl-launch into system-wide or used directory:

(require :asdf-install)
(asdf-install:install :cl-launch)

Next we should create a Lisp image (a “core” file) that contains the cl-gtk2-gtk system and newly created hello-world application. If we will not create the image, the all code will be loaded from source files or from FASLs. Neither is fast. For example, loading cl-gtk2-gtk from FASLs takes as much as 30 seconds on my machine. During development, this is neglibible as it is loaded only once at startup.

Enter the following command to create the Lisp image:

cl-launch.sh -s hello-world -d hello-world-image

cl-launch loads the hello-world system (from hello-world.asd file from current directory) and saves the image as the ‘hello-world-image’ file.

If several Lisp implementations are installed, you can choose which of them is used:

cl-launch.sh --lisp sbcl -s hello-world -d hello-world-image

(But at the moment, cl-gtk2-gtk supports image dumping only with SBCL)

Next we use cl-launch to create a shell script that starts the program from a newly created image:

cl-launch.sh -m hello-world-image -i '(hello-world:run)' -o hello-world

Now, we should have a ‘hello-world’ shell script created.

Let’s run it:

./hello-world

In an instant, a window appears.

The application startup time is acceptable.

Now the only things that are needed to redistribute the application are hello-world-image file and hello-world shell script (and Gtk+ libraries, of course).

But the image size is quite big. On my machine, the image produced by 64-bit Linux version of SBCL takes 64 megabytes. Of it, SBCL takes about 42 megabytes, and the rest is cl-gtk2. SBCL is a native code generating compiler, so it produces large images. Byte-code compilers (like clisp) should have smaller sizes.

If this size is too much, there is an option of using gzexe program to compress the image.

gzexe hello-world-image

On my machine, this decreases the image size produced by SBCL from 64 megabytes to 12 megabytes and somewhat increases the startup time (but it is still quite fast).

It seems that one possible way of redistributing Lisp applications is to compile them from sources on target machine.

Advertisements

2 Responses to “How-to: Creating stand-alone executable with SBCL, cl-gtk2 and cl-launch”

  1. Tomislav Tomsic Says:

    Very informative and great post, as usual. As your software, cl-gtk2 is not just very, very good, but inspiring too, in itself, a very rare quality , this is precisely what I need at the moment, while I am researching it :).

    Once again, great post, as usual.

    Greetings.

  2. Alexander Says:

    I guess instead:

    (defun run ()
    (main)
    (join-main-thread))

    here must be:

    (defun run ()
    (main)
    (join-gtk-main))


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: