Depending on how your application needs to interoperate with other software, you may want to build it as a DLL (also referred to as a dynamic library) rather than an executable.
Supply the names of your library's exports in a list value for the deliver
keyword :dll-exports. Each name in dll-exports should be a string naming a Lisp function defined by fli:define-foreign-callable
.
The deliver function argument should be nil
, because a dynamic library does not have a startup function.
Supply the file type of the delivered image in the deliver file argument if necessary.
As when delivering a LispWorks executable, start at deliver level 0. Increase the delivery level, if desired, after you have debugged your library. Whenever possible, debug your code running in the LispWorks development image. If the problem only occurs when your code runs inside a dynamic library, you may be able to debug it on your development machine in a dynamic library created by save-image
rather than deliver.
A Microsoft Windows application should use LoadLibrary
to load the DLL and GetProcAddress
to find the address of the exported names. On other platforms the application should use dlopen
and dlsym
.
On some platforms there are special requirements for a program that loads a LispWorks dynamic library, as follows:
The program should be linked with libpthread.so
.
The program should be linked with libpthread.so
.
The program should be compiled and linked multi-threaded, for example using the -mt
option to Oracle's cc
.
No special requirements.
On non-ARM platforms a dynamic library can be loaded into LispWorks using fli:register-module
, and this is a convenient way of testing it. See Further example for an example.
For more information about the behavior of LispWorks dynamic libraries see the chapter "LispWorks as a dynamic library" in the LispWorks User Guide and Reference Manual .
The script below creates hello.dll
.
-------------------- hello.lisp -------------------------
(in-package "CL-USER")
(load-all-patches)
;; The signature of this function is suitable for use with
;; rundll32.exe.
(fli:define-foreign-callable ("Hello"
:calling-convention :stdcall)
((hwnd w:hwnd)
(hinst w:hinstance)
(string :pointer)
(cmd-show :int))
(capi:display-message "Hello world")
;; quit when library's job is done
(dll-quit))
(deliver nil "hello" 0 :dll-exports '("Hello") :interface :capi)
---------------------------------------------------------
You can build the DLL with this command line:
MS-DOS> lispworks-7-0-0-x86-win32.exe -build hello.lisp
and you can test it with this command line:
rundll32 hello.dll,Hello
The Application Builder tool provides another way to build and test hello.dll
:
hello.lisp
and do
Works > Build > Build
to build the DLL.rundll32
in the Execute pane, enter hello.dll,Hello
in the Arguments pane, and press
OK
to test the library.See the example (4 files) in the LispWorks library at
examples/delivery/dynamic-library/linux/
This example creates a LispWorks dynamic library and also a test program for loading it on Linux.
To build and run the example, follow the instructions in rundll.c
.
This example builds a dynamic library which in principle could be loaded by any application and called to calculate square numbers.
For illustrative purposes, we show how to load the dynamic library into the LispWorks development image. This illustrates some platform-specific initialization. Then we use the library, ensure it exits cleanly, and finally delete the dynamic library file.
Note that on Linux/Macintosh/FreeBSD, to deliver a dynamic library, the build machine must have a C compiler installed.
For convenience the code is presented without external files. To run it, copy each form in turn and enter it at the Listener prompt.
(defvar *dynamic-library-path*
(merge-pathnames (make-pathname :name "CalculateSquareExample"
:type scm::*object-file-suffix*)
(get-temp-directory)))
(defun save-dynamic-library ()
(let* ((file (open-temp-file :file-type "lisp"))
(ns (namestring file)))
(format file
"
(fli:define-foreign-callable (calculate-square :result-type :int)
((arg :int))
(* arg arg))
(deliver nil ~s 5 :dll-exports '(\"calculate_square\"))"
(namestring *dynamic-library-path*))
(close file)
(sys:call-system-showing-output (list (lisp-image-name)
"-build"
ns ))
(delete-file file nil)))
(save-dynamic-library)
(fli:define-foreign-function (my-quit-lispworks "QuitLispWorks")
((force :int)
(milli-timeout :int))
:result-type :int
;; specifying :module ensures the foreign function finds
;; the function in our module
:module 'my-dynamic-library)
(fli:define-foreign-function (my-init-lispworks "InitLispWorks")
((milli-timeout :int)
(base-address (:pointer-integer :int))
(reserve-size (:pointer-integer :int)) ; really size_t
)
:result-type :int
:module 'my-dynamic-library)
(fli:define-foreign-function calculate-square
((arg :int))
:result-type :int
:module 'my-dynamic-library)
(defun run-the-dynamic-library ()
(fli:register-module 'my-dynamic-library
:connection-style :immediate
:file-name *dynamic-library-path*)
;; Windows and Mac OS X can detect and resolve memory clashes.
;; On other platforms, tell the library to load at different
;; address (that is, relocate) because otherwise it will use
;; the same address as the running LispWorks development image.
;; Relocation may be needed when loading a LispWorks dynamic
;; library in other applications.
#-(or mswindows darwin)
(my-init-lispworks 0
#+lispworks-64bit #x5000000000
#+lispworks-32bit #x50000000
0)
(dotimes (x 4)
(format t "square of ~d = ~d~%" x
(calculate-square x)))
(my-quit-lispworks 0 1000)
(fli:disconnect-module 'my-dynamic-library))
(run-the-dynamic-library)
Check the output to see that it computed square numbers.
(delete-file *dynamic-library-path* nil)
On Macintosh/Linux/FreeBSD/Unix you can supply files to be included in the library via the deliver
keyword argument :dll-added-files. This is useful if you need to write wrappers around calls into the library.
You can specify whether your LispWorks dynamic library initializes itself automatically on loading with the deliver
keyword argument :automatic-init. For more information see "Initialization of the dynamic library" in the
LispWorks User Guide and Reference Manual
.
LispWorks Delivery User Guide - 15 Feb 2015