Daniel Janus’s blog
Who said Common Lisp programs cannot be small?
9 August 2008
So, how much disk space does your average CL image eat up? A hundred megs? Fifty? Twenty? Five, perhaps, if you’re using LispWorks with a tree-shaker? Well then, how about this?
[nathell@chamsin salza2-2.0.4]$ ./cl-gzip closures.lisp test.gz
[nathell@chamsin salza2-2.0.4]$ gunzip test
[nathell@chamsin salza2-2.0.4]$ diff closures.lisp test
[nathell@chamsin salza2-2.0.4]$ ls -l cl-gzip
-rwxr-xr-x 1 nathell nathell 386356 2008-08-09 11:08 cl-gzip
That’s right. A standalone executable of a mini-gzip, written in Common Lisp, taking up under 400K! And it only depends on glibc and GMP, which are available by default on pretty much every Linux installation. (This is on a 32-bit x86 machine, by the way).
I used the most recent version of ECL for compiling this tiny example. The key to the size was configuring ECL with --disable-shared --enable-static CFLAGS="-Os -ffunction-sections -fdata-sections" LDFLAGS="-Wl,-gc-sections"
. This essentially gives you a poor man’s tree shaker for free at a linker level. And ECL in itself produces comparatively tiny code.
I built this example from Salza2’s source by loading the following code snippet:
(defvar salza
'("package" "reset" "specials"
"types" "checksum" "adler32" "crc32" "chains"
"bitstream" "matches" "compress" "huffman"
"closures" "compressor" "utilities" "zlib"
"gzip" "user"))
(defvar salza2
(mapcar (lambda (x) (format nil "~A.lisp" x))
salza))
(defvar salza3
(mapcar (lambda (x) (format nil "~A.o" x))
salza))
(defun build-cl-gzip ()
(dolist (x salza2)
(load x)
(compile-file x :system-p t))
(c:build-program
"cl-gzip"
:lisp-files salza3
:epilogue-code
'(progn
(in-package :salza2)
(gzip-file (second (si::command-args))
(third (si::command-args))))))
(build-cl-gzip)
(Sadly enough, there’s no ASDF in here. I have yet to figure out how to leverage ASDF to build small binaries in this constrained environment.)
This gave me a standalone executable 1.2 meg in size. I then proceeded to compress it with UPX (with arguments --best --crp-ms=999999
) and got the final result. How cool is that?
I am actively looking for a new job. If you happen to like my writings and think I might be just the right man for the team you’re building up, please feel free to consult my résumé or pass it on.
Update 2010-Jan-17: the above paragraph is no longer valid.