From this post, I will introduce topics on compiling Lisp programs
with ECL, Embeddable Common Lisp. ECL is an implementation of Common
Lisp, and it is especially powerful on combining lisp programs with C
programs. You can embed ECL as a lisp engine in C programs, or call C
functions via ffi. In a series of posts, several ways to compile lisp
programs and use them from C programs are introduced. As a starter, I
explain file types generated by some compilation approaches.
I suppose GNU/Linux system and gcc as a development environment.
You can generate following files with ECL.
- Object file (.o)
- Fasl file (.fas)
- Static library (.a)
- Shared library (.so)
- Executable file
Relations among them are depicted below:
As you can see, object file works as an intermediate file format. If
you want to compile more than two lisp files, you might better to
compile with a :system-p t
option, which generates object files.
On linux systems, ECL invokes gcc -c
for generating object files.
An object file consists of some functions in C:
- Functions corresponding to Lisp functions
- The initialization function which registers defined functions on the lisp environment
Consider the example below.
1 (defun say-hello ()
2 (print "Hello, world"))
During compilation, This simple lisp program is translated into the C
program, and then compiled into the object file. The C program contains two functions:
static cl_object L1say_hello
: corresponding to 'say-hello' function
ECL_DLLEXPORT void _eclwm2nNauJEfEnD_CLSxi0z(cl_object flag)
: initialization function
In order to use these object files from your C program, you have to call initialization functions before using lisp functions (such like 'say-hello'). However the name of an init function is seemed to be randomized and not user-friendly. This is because object files are not intended to be used directly. ECL provides some user-friendly ways to generate compiled lisp programs(as static/shared libraries or executable), and in each approach, object files act as intermediate files.
If you want to make a library which is loaded dynamically from lisp program, you should choose fasl file format.
In current ECL(9.10.2), a fasl file is just a shared library file. This means you can load fasl files with dlopen
and initialize it by calling a init function from C programs, but this is not an intended usage. Recommended usage is loading fasl files by calling load
lisp function.
Creating a fasl file from one lisp file is very easy.
1 (compile-file "hello.lsp")
To create a fasl file from some lisp files, firstly you have to compile each lisp file into object file, and then combine them with c:build-fasl
.
1
2 (compile-file "hello.lsp" :system-p t)
3
4 (compile-file "goodbye.lsp" :system-p t)
5
6
7 (c:build-fasl "hello-goodbye"
8 :lisp-files '("hello.o" "goodbye.o"))
ECL can compile lisp programs to static libraries, which can be linked with
C programs. A static library is created by c:build-static-library
with some compiled object files.
1
2 (compile-file "hello.lsp" :system-p t)
3
4 (compile-file "goodbye.lsp" :system-p t)
5
6
7 (c:build-static-library "hello-goodbye"
8 :lisp-files '("hello.o" "goodbye.o")
9 :init-name "init_hello_goodbye")
When you use static/shared library, you have to call init functions. The name of the function is specified by :init-name
option. In this example, "init_hello_goodbye" is it. The usage of this function is shown below:
1 #include <ecl/ecl.h>
2 extern void init_hello_goodbye(cl_object cblock);
3
4 int
5 main(int argc, char **argv)
6 {
7
8 cl_boot(argc, argv);
9
10
11 read_VV(OBJNULL, init_hello_goodbye);
12
13 ...
14
15
16 cl_shutdown();
17
18 return 0;
19 }
Because the program itself does not know the type of the init
function, a prototype declaration is inserted. After booting up the
lisp environment, invoke init_hello_goodbye
via
read_VV
. init_hello_goodbye
takes a argument, and read_VV
supplies an appropriate one. Now that the initialization is finished, we can use functions and other stuffs defined in the library.
Almost the same as the case of static library.
Some of you might want to create a standalone executable from lisp
programs. ECL supports executable file generation. First, compile all
lisp files to object files. After that, calling c:build-program
does
the job.
1
2 (compile-file "hello.lsp" :system-p t)
3
4 (compile-file "goodbye.lsp" :system-p t)
5
6
7 (c:build-program "hello-goodbye"
8 :lisp-files '("hello.o" "goodbye.o"))
In this post, some file types that can be compiled to with ECL were introduced. Each file type has adequate purpose:
- Object file: intermediate file format for others
- Fasl file: loaded dynamically via
load
lisp function
- Static library: linked with and used from C programs
- Shared library: loaded dynamically and used from C programs
- Executable: standalone executable
ECL provides a high-level interface c:build-*
for each format. If you want to use them, you can find detailed description in the manual. Enjoy!