wiki:CustomFramework

Version 2 (modified by rme, 3 years ago) (diff)

--

Building Framework Interfaces

Clozure CL comes with pre-built interfaces for several Mac OS X frameworks. It's possible to create interfaces for other frameworks, too.

Build Framework

If the framework of interest is not a system-supplied framework, you will need to build the framework from the Objective-C sources containing the desired functionality. This can be done with Xcode, and there are good explanations elsewhere.

Use ffigen

To support its FFI, CCL maintains a binary database of information about classes, methods, functions, types, and variables available from foreign libraries in several ".cdb" files. You will need to generate this information for your particular library. In order to do this, you will need to obtain and build ffigen4. Once you have done so, navigate to $CCL_DIRECTORY/darwin-x86-headers or $CCL_DIRECTORY/darwin-x86-headers64, depending. In this directory, create a directory with the same name as the Framework you built in step 1, and inside that directory, create a directory named "C". Inside C, you must write a shell script named "populate.sh," that invokes another shell script "h-to-ffi.sh," (which should be on your path after building and installing ffigen4) on every header in your framework. The directories that already exist in darwin-x86-headers[64] contain examples that can be adapted to this purpose. (Note that Apple's frameworks have one "mega header" that includes all of the others, so many of the example shell scripts only have to pass one header to h-to-ffi.sh. This will generate a directory tree that contains (eventually) several ".ffi" files inside the directory "C."

Build interface files

Now, the ".ffi" files must be converted to ".cdb" files. This happens entirely within Lisp. Start up the lisp and do (require 'ffi) and (ccl::parse-standard-ffi-files :interface-dir), where :interface-dir is the name of the directory that you created in the step above.

CCL maintains a list of "active interface directories". In order to successfully (require 'cocoa), the only directories that can be active are :cocoa and :libc. Unfortunately, the previous command adds your interface directory to this list, which will cause (require 'cocoa) to bork. So, exit the REPL or otherwise modify the state of the Lisp image to be original and pristine.

Use new interfaces

Now, you can (require 'cocoa), followed by an (objc:load-framework :framework :interface-dir) and use the classes from your own Objective-C framework.

Example: Accelerate.framework

As an example, here's how to build interfaces for the Accelerate framework, which is an Apple-supplied framework. This assumes that you've already installed ffigen.

$ cd /usr/local/src/ccl/darwin-x86-headers64
$ mkdir -p accelerate/C
$ cd accelerate/C
$ cat >populate.sh
#!/bin/sh
rm -rf System Developer usr
SDK=/Developer/SDKs/MacOSX10.5.sdk
CFLAGS="-m64 -fobjc-abi-version=2 -isysroot ${SDK} -mmacosx-version-min=10.5"; export CFLAGS
h-to-ffi.sh ${SDK}/System/Library/Frameworks/Accelerate.framework/Headers/Accelerate.h
^D
$ sh populate.sh
+++ .../Accelerate.framework/Headers/Accelerate.h
$ ccl
Welcome to Clozure Common Lisp Version 1.7-dev-r14622:14636  (DarwinX8664)!
? (require 'parse-ffi)
PARSE-FFI
("PARSE-FFI")

? (parse-standard-ffi-files :accelerate)
#P".../Accelerate.framework/Headers/Accelerate.ffi" ...
T
? 

Now, if you're using the Objective-C bridge already, just do (objc:load-framework :accelerate). Otherwise, do this:

? (open-shared-library "/System/Library/Frameworks/Accelerate.framework/Accelerate")      
#<SHLIB /System/Library/Frameworks/Accelerate.framework/Accelerate #x3020005344CD>
? (use-interface-dir :accelerate)
#<INTERFACE-DIR :ACCELERATE #P"accelerate/" #x30200054C80D>
? (rletz ((x (:array :double 10)))
    (setf (paref x (:array :double) 5) 100d0
	  (paref x (:array :double) 8) -1000d0)
    (#_cblas_idamax 10 x 1))
8
?