On Thu, Jul 28, 2011 at 12:50 AM, Mark McCurry <mark.d.mccurry@gmail.com> wrote:
Well, I had no luck digging through the mailing list and the wiki is vague.
I have managed to get the linking working after playing around for a bit.
The process I used to link the static lisp code "F-NAME.lisp" was:

1) Run ecl script to:
(compile-file F-NAME.lisp :system-p t)
(c:build-static-library F-NAME :lisp-files '(F-NAME.o))

2) In C file to be linked:
cl_boot(argc, argv);
void init_lib_F_NAME(cl_object);
read_VV(OBJNULL, init_lib_F_NAME);
cl_funcall(...);

3) Compile C with static library

Is this the 'right' way to embed a static piece of lisp code into a C program?

There is no single right way to do it. Possibilities are many and I would favor some that many people do not like.

TYPE I: ECL in control

* Compile all your C code into a statically or dynamically linked library, say libc_extensions.a.

* Organize your program in the lisp part, including the lisp code that is to be executed at startup. Say the "startup" function is MY-PACKAGE:ENTRY-POINT

* Compile each file using the flag :SYSTEM-P T and link all the resulting files into a program using C:BUILD-PROGRAM

   (let ((list-of-files '("mylisp1.lisp" "mylisp2.lisp" ... ))
         (compiled-files (mapcar (lambda (x) (compile-file x :system-p t))
                                 list-of-files))
     (c:build-static-library "lisp_extension"
                             :lisp-files compiled-files
                             :ld-flags '("-lc_extension")
                             :epilogue-code '(MY-PACKAGE:ENTRY-POINT)
     ))

I very much prefer this way because it hides from you the link flags and also the initialization of ECL. It is also much simpler to capture and handle exceptions from lisp code than from C.

Note that you need not use :epilogue-code or the function MY-PACKAGE;ENTRY-POINT, because on startup _all_ lisp code will be executed sequentially.

TYPE II: Your code in control

* Organize your lisp code in a set of lisp files, with certain public functions that are to be called from C perfectly identified by name.

* Compile and build everything into a statically or dynamically linked library (that itself depends on libecl.so or ecl.dll!)

   (let ((list-of-files '("mylisp1.lisp" "mylisp2.lisp" ... ))
         (compiled-files (mapcar (lambda (x) (compile-file x :system-p t))
                                 list-of-files))
     (c:build-static-library "lisp_extension"
                             :lisp-files compiled-files))

* Compile your C program as usual and add your lisp library as a dependency. In this case you would have to add -llisp_extension and -lecl when linking the program and you have to code your main program to initialize the lisp library

int main(int argc, char **argv)
{
  cl_boot(argc, argv);
  extern cl_object init_LIB_LISP_EXTENSION(cl_object);
  read_VV(OBJNULL, init_LIB_LISP_EXTENSION);
  ...
}

This is more or less what you did but you have to make sure that both your lisp code and ECL's library is included in the dependencies of your program, otherwise it will fail to link. I do not like it very much, except for very large projects where somehow ECL has to be integrated by hand. It has the severe disadvantage that you have to know how to create lisp objects, find lisp symbols and call lisp functions in C, and use some interfaces (read_VV) which may change in the near future.

There are detailed examples about how to build libraries and programs using ECL as a linker and including external C code in examples/build and examples/asdf (they have been updated in CVS because some files had disappeared) When organizing and building your code I very much recommend using ASDF and ASDF:MAKE-BUILD (http://ecls.sourceforge.net/new-manual/ch16.html) instead of compiling your files manually.

Juanjo

--
Instituto de Física Fundamental, CSIC
c/ Serrano, 113b, Madrid 28006 (Spain)
http://juanjose.garciaripoll.googlepages.com