Stranger In A Strange Land, Welcome to FFI and LISP!
I have managed to blunder my way around the ASDF system sufficiently well enough to do all the things that I have wanted to do… so far. I have been using LISP more and more, on and off, for about eighteen months now and the more I suffer the more I like it. It *absolutely* does make you think differently and *absolutely* does make you realise Philip Greenspun was bang on the mark. This could be a long blog post but interesting to me anyway…. LOL.
Having used the cl-opengl bindings for hacking in the past, I decided I wanted to have a go at the cl-glfw bindings instead as I do not like the way the event loop forces you to structure your application and also, having downloaded and built the glfw library and run the examples it seemed like it would be a good exercise to convert them into LISP as the examples worked well on my 10.5.8 intel Mac, in both ‘-x11′ and Cocoa builds of the code. There is a problem with the keyboard handling in Cocoa mode, which oddly enough is listed as a bug on the cl-glfw site with the status ‘closed’ and yet it still broke on my Mac. The X11 build was just fine.
So, firing up EMACS + SLIME I duly attempted to install the cl-glfw binding only to be met by this error in the debugger:
Unable to load any of the alternatives: ((:FRAMEWORK "GLFW") (:DEFAULT "glfw") (:DEFAULT "libglfw")) [Condition of type CFFI:LOAD-FOREIGN-LIBRARY-ERROR] Restarts: 0: [RETRY] Try loading the foreign library again. 1: [USE-VALUE] Use another library instead. 2: [TRY-RECOMPILING] Recompile glfw and try loading it again 3: [RETRY] Retry performing #<ASDF:LOAD-OP NIL {1003C8A391}> on #<ASDF:CL-SOURCE-FILE "glfw" {1003607D91}>. 4: [ACCEPT] Continue, treating #<ASDF:LOAD-OP NIL {1003C8A391}> on #<ASDF:CL-SOURCE-FILE "glfw" {1003607D91}> as hav$ 5: [RETRY] Retry SLIME REPL evaluation request. --more--
WTF? Cut to the present, skipping a few hours of very frustrating head banging trying to get the damned thing to load into my SBCL image made me realise that sometimes you have to go back to basics. In this case, that will be the FFI system. Up until now most of the packages I have used (cl-sql, cl-opengl and many others) have all used the UFFI system, but this particular binding is built atop the more modern and allegedly funkier CFFI.
Over the next hour or three I tried in vain to tweak DYLD_LIBRARY_PATH, I read the manual pages for dlopen(3), I copied the library to ‘/usr/lib’, I tried to manually use CFFI:DEFINE-FOREIGN-LIBRARY and CFFI:LOAD-FOREIGN-LIBRARY at the REPL to reduce the problem to its smallest incarnation and all with the same message as before. I was by now beginning to think that the actual library itself might be faulty in some way, so I did this:
otool -tv /usr/local/lib/libglfw.dylib | grep __* | less
and got a huge list of exported symbols. This kind of made me feel a little more confident that the library that had been built was in fact usable: if otool managed to load it and print out the symbols then at least it was a good ‘un!
So either CFFI was having issues in actually locating it or it was not able to actually load the thing into memory to apply the bindings. Now we are getting to the limits of my knowledge on this stuff so from here on in, I have been learning / hacking and writing as I go so bear with me. At this juncture I do not even know if I will win or end up going back to cl-opengl.
The first thing I thought to do was go read the source code where the error condition was actually raised: this is in the libraries.lisp file, here is the code…
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | (defun load-foreign-library (library) "Loads a foreign LIBRARY which can be a symbol denoting a library defined through DEFINE-FOREIGN-LIBRARY; a pathname or string in which case we try to load it directly first then search for it in *FOREIGN-LIBRARY-DIRECTORIES*; or finally list: either (:or lib1 lib2) or (:framework <framework-name>)." (restart-case (typecase library (symbol (let* ((lib (get-foreign-library library)) (spec (foreign-library-spec lib))) (when spec (setf (foreign-library-handle lib) (load-foreign-library-helper library spec)) lib))) (t (make-instance 'foreign-library :spec (list (list library)) :handle (load-foreign-library-helper nil library)))) ;; Offer these restarts that will retry the call to ;; LOAD-FOREIGN-LIBRARY. (retry () :report "Try loading the foreign library again." (load-foreign-library library)) (use-value (new-library) :report "Use another library instead." :interactive read-new-value (load-foreign-library new-library)))) |
So was it failing to load because it didn’t find it or because it couldn’t eat it?
The next thing that I tried was loading CFFI manually first, then adding the location of the library to the foreign library path list like so:
(pushnew "/usr/local/lib/" cffi:*foreign-library-directories*)
and tried again. Same error. Just to make sure, I evaluated that variable from inside the debugger by hitting ‘e’ and entering the name and getting…
cffi:*foreign-library-directories* [Return] ("/usr/local/lib/") => result
So it looks like the library is refusing to load or I have done something really really dumb up to this point. If you are in a position to put me out of my misery then please leave a comment, only (legal) suggestions relevant to the problem at hand will be considered.
Finally I read the CFFI source code and ultimately all I got was a bad head-ache. Sometimes reading the source is *not* a good idea as it only serves to remove you even further from a solution unless you absolutely know what you are doing, and thus far in time I haven’t a clue about how CFFI works.
As it is a portable wrapper across various LISP implementations I guess that it would be using the built-in SBCL FFI mechanisms. I found a file in the CFFI src folder called ‘cff-sbcl.lisp’ which I am now going to wander off and study over a nice cold pint of cider. Apple mac and apple juice. A great combination.
So. right now I am no nearer getting myself a working solution BUT there are some points to bear in mind for on-going progress..
- I have not given up yet
- I never give up, esp. with dumb computers.
- What one man can do, another man can do.
I know it *can* be done because the author of the cl-glfw binding used it to write CityScape and if he managed to do that then I sure as hell will get over my problems.
I am now going to write some code to use the SBCL FFI calls to try and load the library and if that fails I am gong to write some tutorials on writing a dynamically loadable library using gcc on a Mac and then load it using SBCL/FFI, just so as I know what the hell is going on.
Then I will write my own FFI binding for my own little library and hopefully then things will start to make a little more sense.
Stay tuned.
…
8 comments to “Stranger In A Strange Land, Welcome to FFI and LISP!”
January 6th, 2010 at 4:42 pm
I’m having the same issue. When trying to load cocoabuilder (from lispbuilder-sdl) I get the same issue. I went through the same steps, modified the different cffi paths, building a .framework dir, putting it and the dylib everywhere imaginable … but still nothing.
January 6th, 2010 at 4:50 pm
I still have not solved this issue: I have been too busy to come back to it. However I did wrote two other small posts about writing a small dynamic library to learn FFI better. One day! If I was on a linux box I would use “strace” to see what file failed to load… I wonder if I could attach an Instruments session and use that to see where the failed file load is happening ?? That is something you may want to try as I don’t know when I have the time!
January 13th, 2010 at 12:17 pm
The main reason we chose GLFW over SDL was that we thought it’d be easier to use on a Mac. Unfortunately we (as in Bill and I) didn’t have a Mac to test it on.
It’s possible that it depends on further libraries (such as GL?) on the Mac, which also have to be loaded.
I’d advise trying to load it with a stub that uses whatever native FFI your lisp has first, before figuring out CFFI – the former may be more informative in it’s errors.
January 14th, 2010 at 11:32 pm
John (Terminator) Connors,
Just *this* evening I have spent more time and even lodged a post on the GLFW forums, the link is here in case you want to see what I tried.
https://sourceforge.net/projects/glfw/forums/forum/247562/topic/3519056
I am really getting desperate to solve this now! I downloaded and built version 2.6.0 tonight but to no avail. I then had a small light-bulb: Use ‘Instruments’ to track the file load activity and see what is going on but even that only confused me more as it def. shows file activity at /usr/lib/libglfw.dylib AND /usr/local/lib/libglfw.dylib and yet still it doesn’t find it. Having seen the load paths I also copied the files into /usr/lib and tried again but still it gives the same error.
However, I have seen mutterings about the 2.6 version not being dynamically loadable and so I may try the 2.7 bleeding edge which apparently produces dynamically loadable libraries for all of the supported platforms.
I am beginning to suspect that the CFFI/FFI system *is* actually locating the library but for some reason it cannot be loaded… maybe I should check it is a 32 bit build as my SBCL is 32 bit build. I think that the error message is now a red-herring in that, having read the underlying LISP code, there is no other bail out option at that point other than what is says! “Unable to load…” means just that, not “couldn’t find” but “couldn’t eat it”.
I have not given up yet but this is turning out to be harder than I expected! Tomorrow I will download the bleeding edge version and have another go. I *know* it works because the examples work just fine. Agh! Computers!!!
Cheers.
UPDATE: 11:52 PM, get a life update…
I have downloaded the latest SVN sources, no joy. I modded the makefile to use -arch i386 just to be sure. No joy. All the example apps still run fine. Tried -m32 as well, no joy. So, what does it take to be able to load and use cl-glfw. What REALLY BAFFLES me is this web-site: https://trac.wvr.me.uk/cityscape/wiki The game was written with SBCL and GLFw so it must be do-able. Admittedly it was on windows so the FFI is different.
Maybe I am using UFFI instead without realising it? I will check that out.
So, where to go from here…? I will attempt to cut and paste some bindings manually and see if I can call a function from the REPL, at least that will prove *something*…. I am too dumb to know when to quit!
HOw do I know that, because I am thinking that I will also write a tiny little C program to use the native library, statically linked. But why should I when the GLFW examples work? I *did* write my little dynamically loaded test which kind of worked but had window issues, BUT the main thing is that the library did load and did function. AGH!
Deep down I know I did something dumb and nobody is going to help me until I realise just how dumb I have been…
July 2nd, 2010 at 10:11 am
I know its some time later..but..did you ever get anywhere with this? Cityscape did work on a mac at one point, but it’s quite possible the current trunk is broken.
The way to work out whether the problem is
to try loading it using the implementations native FFI and an absolute path. If that doesn’t work the problem is on the library side.
CFFI also has a *DARWIN-FRAMEWORK-DIRECTORIES*: I don’t know if that is relevant. We actually used a custom build of 2.6 with a tweaked that produced a dynamically loadable library..
..your experiences, and those of others have convinced me that lisp bindindgs should actually ship with the binary they are bound to. It’s actually quite easy to have /bin directory in the same directory as your .asd file and locate dynamic libraries in there. It’s what the lispbuilder-sdl people do, and I’m convinced it’s a good idea. Possibly with patches to the makefile that you used to generate the binary too, so as to keep in the spirit of FLOSS
July 23rd, 2010 at 1:03 am
I have cl-glfw working with snow leopard however I have to manually select glfw.dylib with the “Use another library instead.” debugger option. I’d like to know how to load this properly.
July 23rd, 2010 at 1:05 am
I meant libglfw.dylib.
July 26th, 2010 at 11:52 pm
An an update to my last post I should say that some of the cl-glfw demos work in that a window appears with a running thread however the window is not interactive (although in gears the fps is still reported at the REPL).
I assume there is an issue with threading since the only way to continue is to abort C-c C-c, leaving the running SBCL/foreign library unstable with any further attempt to run cl-glfw app’s crashing the image and reporting an ugly os message.
I am using:
OS X 10.6.4
SBCL 1.0.40
cl-glfw 2.7 Trunk
slime 2010-07-22