Hello,
There's been some discussion on IRC about what CFFI's "smarter" foreign library interface should look like. I put together some code and here's what my attempt looks like:
(define-foreign-library opengl (:darwin (:framework "OpenGL")) (:unix (:alternatives "libGL.so" "libGL.so.1" #p"/myhome/mylibGL.so")) (:windows "opengl32.dll") ;; and a hypothetical example of a particular platform ;; where the OpenGL library is split in two. ((:and :some-system :some-cpu) "libGL-support.lib" "libGL-main.lib"))
The library is then used with the following macro:
(use-foreign-library opengl)
So here's what's going on when loading this opengl library:
- First, the proper clause is selected in a cond-like fashion. The symbols in there are tested against symbols in *features* that belong to the cffi-features[1] package. The :and, :or and :not operators are supported.
- Then, the rest of the elements in the clause are handled. There can be more than one. For example you could define GLUT and OpenGL in one define-foreign-library form and whatever support libraries those might need.
- Each of the elements can be:
a) A string, eg. "libGL.so". In this case it's passed to load-foreign-library directly. What this means in e.g. unix-like systems is that the library will be searched in:
1. the LD_LIBRARY_PATH environment variable. 2. the directories in /etc/ld.so.cache (or equivalent) 3. /usr/lib and /lib
If that fails, it tries to find it in the directories inside cffi:*foreign-library-directories* which is similar to asdf:*central-registry* in the sense that you can push stuff like '*default-pathname-defaults* or '(user-homedir-pathname) and they'll be "evaluated".
b) A pathname, in which case CFFI doesn't try to find it and simply passes its namestring to load-foreign-library.
c) A list of the form (:alternatives ...). Here we try to load each of the alternatives in order until one is loaded successfully. If none of the alternatives are loaded we get an error.
d) A pair of the form (:framework "name"). CFFI will try to load the "name" framework, looking at the directories in cffi:*darwin-framework-directories* which is similar to cffi:*foreign-library-directories*. This list contains the following paths by default:
1. ~/Library/Frameworks 2. /Library/Frameworks 3. /System/Library/Frameworks
Obviously, the user can push more paths into this list.
There are some functions that I'm not sure are worth exporting: find-foreign-library, find-and-load-foreign-library and find-darwin-framework. Also, maybe a functional interface to use-foreign-library could be useful?
Comments?
[1] We'll be removing :cffi/no-foreign-funcall, and pushing cffi-features:foreign-funcall instead. Also, we provide others like cffi-features:{windows,unix,darwin,ppc32,x86} so that users can rely on these across different lisps.