1 ;;; This is experimental code that uses the s-expression
2 ;;; representation of a C/C++ library interface to generate Foreign
3 ;;; Function Interface definitions for use with Kevin Rosenberg's
6 ;;; Written by Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
8 (eval-when (:compile-toplevel :load-toplevel :execute)
9 (require 'port) ; from CLOCC
14 ;; Interaction with the SWIG binary
16 (defvar *swig-source-directory* #p"/home/mkoeppe/s/swig1.3/")
18 (defvar *swig-program* (merge-pathnames "preinst-swig" *swig-source-directory*))
20 (defun run-swig (swig-interface-file-name &key directory-search-list module
22 (let ((temp-file-name "/tmp/swig.lsp"))
24 (port:run-prog (namestring *swig-program*)
26 :args `(,@(and c++ '("-c++"))
28 ,@(mapcar (lambda (dir)
30 "-I" (namestring dir)))
31 directory-search-list)
35 ,(namestring swig-interface-file-name)))))
36 #+cmu (unless (or (zerop (ext:process-exit-code process))
38 (error "Process swig exited abnormally"))
39 (with-open-file (s temp-file-name)
44 (defun parse-swigtype (type-string &key start end junk-ok)
45 "Parse TYPE-STRING as SWIG's internal representation of C/C++
46 types. Return two values: The type description (an improper list) and
47 the terminating index into TYPE-STRING."
48 ;; SWIG's internal representation is described in Source/Swig/stype.c
52 (setq end (length type-string)))
53 (flet ((prefix-match (prefix)
54 (let ((position (mismatch prefix type-string :start2 start :end2 end)))
56 (= position (length prefix)))))
57 (bad-type-error (reason)
58 (error "Bad SWIG type (~A): ~A" reason
59 (subseq type-string start end)))
61 (and (< index (length type-string))
62 (char type-string index)))
63 (cons-and-recurse (prefix start end)
64 (multiple-value-bind (type-description index)
65 (parse-swigtype type-string :start start :end end
67 (values (cons prefix type-description)
70 ((prefix-match "p.") ; pointer
71 (cons-and-recurse '* (+ start 2) end))
72 ((prefix-match "r.") ; C++ reference
73 (cons-and-recurse '& (+ start 2) end))
74 ((prefix-match "a(") ; array
75 (let ((closing-paren (position #\) type-string
79 (bad-type-error "missing right paren"))
80 (unless (eql (type-char (+ closing-paren 1)) #\.)
81 (bad-type-error "missing dot"))
82 (cons-and-recurse (list 'ARRAY (subseq type-string (+ start 2) closing-paren))
83 (+ closing-paren 2) end)))
84 ((prefix-match "q(") ; qualifier (const, volatile)
85 (let ((closing-paren (position #\) type-string
89 (bad-type-error "missing right paren"))
90 (unless (eql (type-char (+ closing-paren 1)) #\.)
91 (bad-type-error "missing dot"))
92 (cons-and-recurse (list 'QUALIFIER (subseq type-string (+ start 2) closing-paren))
93 (+ closing-paren 2) end)))
94 ((prefix-match "m(") ; C++ member pointer
95 (multiple-value-bind (class-type class-end-index)
96 (parse-swigtype type-string :junk-ok t
97 :start (+ start 2) :end end)
98 (unless (eql (type-char class-end-index) #\))
99 (bad-type-error "missing right paren"))
100 (unless (eql (type-char (+ class-end-index 1)) #\.)
101 (bad-type-error "missing dot"))
102 (cons-and-recurse (list 'MEMBER-POINTER class-type)
103 (+ class-end-index 2) end)))
104 ((prefix-match "f(") ; function
105 (loop with index = (+ start 2)
106 until (eql (type-char index) #\))
107 collect (multiple-value-bind (arg-type arg-end-index)
108 (parse-swigtype type-string :junk-ok t
109 :start index :end end)
110 (case (type-char arg-end-index)
111 (#\, (setq index (+ arg-end-index 1)))
112 (#\) (setq index arg-end-index))
113 (otherwise (bad-type-error "comma or right paren expected")))
116 finally (unless (eql (type-char (+ index 1)) #\.)
117 (bad-type-error "missing dot"))
118 (return (cons-and-recurse (cons 'FUNCTION arg-types)
120 ((prefix-match "v(") ;varargs
121 (let ((closing-paren (position #\) type-string
124 (unless closing-paren
125 (bad-type-error "missing right paren"))
126 (values (list 'VARARGS (subseq type-string (+ start 2) closing-paren))
127 (+ closing-paren 1))))
128 (t (let ((junk-position (position-if (lambda (char)
129 (member char '(#\, #\( #\) #\.)))
131 :start start :end end)))
132 (cond (junk-position ; found junk
134 (bad-type-error "trailing junk"))
135 (values (subseq type-string start junk-position)
138 (values (subseq type-string start end)
141 (defun swigtype-function-p (swigtype)
142 "Check whether SWIGTYPE designates a function. If so, the second
143 value is the list of argument types, and the third value is the return
145 (if (and (consp swigtype)
146 (consp (first swigtype))
147 (eql (first (first swigtype)) 'FUNCTION))
148 (values t (rest (first swigtype)) (rest swigtype))
149 (values nil nil nil)))
154 (defvar *uffi-definitions* '())
156 (defconstant *uffi-default-primitive-type-alist*
158 ("unsigned char" . :unsigned-byte)
159 ("signed char" . :byte)
161 ("signed short" . :short)
162 ("unsigned short" . :unsigned-short)
164 ("signed int" . :int)
165 ("unsigned int" . :unsigned-int)
167 ("signed long" . :long)
168 ("unsigned long" . :unsigned-long)
171 ((* . "char") . :cstring)
172 ((* . "void") . :pointer-void)
175 (defvar *uffi-primitive-type-alist* *uffi-default-primitive-type-alist*)
177 (defun uffi-type-spec (type-list)
178 "Return the UFFI type spec equivalent to TYPE-LIST, or NIL if there
179 is no representation."
180 (let ((primitive-type-pair
181 (assoc type-list *uffi-primitive-type-alist* :test 'equal)))
184 (cdr primitive-type-pair))
185 ((and (consp type-list)
186 (eql (first type-list) '*))
187 (let ((base-type-spec (uffi-type-spec (rest type-list))))
189 ((not base-type-spec)
192 (list '* base-type-spec)))))
197 (defvar *uffi-output* nil)
199 (defun emit-uffi-definition (uffi-definition)
200 (format *uffi-output* "~&~S~%" uffi-definition)
201 (push uffi-definition *uffi-definitions*))
203 (defun make-cl-symbol (c-identifier &key uninterned)
204 (let ((name (substitute #\- #\_ (string-upcase c-identifier))))
209 (defvar *class-scope* '() "A stack of names of nested C++ classes.")
211 (defvar *struct-fields* '())
213 (defvar *linkage* :C "NIL or :C")
215 (defgeneric handle-node (node-type &key &allow-other-keys)
216 (:documentation "Handle a node of SWIG's parse tree of a C/C++ program"))
218 (defmethod handle-node ((node-type t) &key &allow-other-keys)
219 ;; do nothing for unknown node types
222 (defmethod handle-node ((node-type (eql 'cdecl)) &key name decl storage parms type &allow-other-keys)
223 (let ((swigtype (parse-swigtype (concatenate 'string decl type))))
224 (let ((*print-pretty* nil) ; or FUNCTION would be printed as #' by cmucl
226 (format *uffi-output* "~&;; C Declaration: ~A ~A ~A ~A~%;; with-parms ~W~%;; of-type ~W~%"
227 storage type name decl parms swigtype))
228 (multiple-value-bind (function-p arg-swigtype-list return-swigtype)
229 (swigtype-function-p swigtype)
230 (declare (ignore arg-swigtype-list))
232 ((and (null *class-scope*) function-p
233 (or (eql *linkage* :c)
234 (string= storage "externc")))
235 ;; ordinary top-level function with C linkage
238 (flet ((unique-argname (name)
239 ;; Sometimes the functions in SWIG interfaces
240 ;; do not have unique names. Make them unique
241 ;; by adding a suffix. Also avoid symbols
242 ;; that are specially bound.
244 (setq name (format nil "arg~D" argnum)))
245 (let ((argname (make-cl-symbol name)))
246 (when (boundp argname) ;specially bound
247 (setq argname (make-cl-symbol name :uninterned t)))
248 (push argname argname-list)
251 (mapcan (lambda (param)
253 (destructuring-bind (&key name type &allow-other-keys) param
254 (let ((uffi-type (uffi-type-spec (parse-swigtype type))))
257 (format *uffi-output* "~&;; Warning: Cannot handle type ~S of argument `~A'~%"
259 (return-from handle-node))
260 ((eq uffi-type :void)
263 (let ((symbol (unique-argname name)))
264 (list `(,symbol ,uffi-type))))))))
267 (uffi-type-spec return-swigtype)))
268 (unless uffi-return-type
269 (format *uffi-output* "~&;; Warning: Cannot handle return type `~S'~%"
271 (return-from handle-node))
272 (emit-uffi-definition `(UFFI:DEF-FUNCTION ,name ,uffi-arg-list :RETURNING ,uffi-return-type))))))
273 ((and (not (null *class-scope*)) (null (rest *class-scope*))
274 (not function-p)) ; class/struct member (no nested structs)
275 (let ((uffi-type (uffi-type-spec swigtype)))
277 (format *uffi-output* "~&;; Warning: Cannot handle type ~S of struct field `~A'~%"
279 (return-from handle-node))
280 (push `(,(make-cl-symbol name) ,uffi-type) *struct-fields*)))))))
282 (defmethod handle-node ((node-type (eql 'class)) &key name children kind &allow-other-keys)
283 (format *uffi-output* "~&;; Class ~A~%" name)
284 (let ((*class-scope* (cons name *class-scope*))
285 (*struct-fields* '()))
286 (dolist (child children)
287 (apply 'handle-node child))
288 (emit-uffi-definition `(,(if (string= kind "union")
291 ,(make-cl-symbol name) ,@(nreverse *struct-fields*)))))
293 (defmethod handle-node ((node-type (eql 'top)) &key children &allow-other-keys)
294 (dolist (child children)
295 (apply 'handle-node child)))
297 (defmethod handle-node ((node-type (eql 'include)) &key name children &allow-other-keys)
298 (format *uffi-output* ";; INCLUDE ~A~%" name)
299 (dolist (child children)
300 (apply 'handle-node child)))
302 (defmethod handle-node ((node-type (eql 'extern)) &key name children &allow-other-keys)
303 (format *uffi-output* ";; EXTERN \"C\" ~A~%" name)
304 (let ((*linkage* :c))
305 (dolist (child children)
306 (apply 'handle-node child))))
308 ;;(defun compute-uffi-definitions (swig-interface)
309 ;; (let ((*uffi-definitions* '()))
310 ;; (handle-node swig-interface)
311 ;; *uffi-definitions*))
318 (defvar *gifplot-interface*
319 (run-swig (merge-pathnames "Examples/GIFPlot/Interface/gifplot.i"
320 *swig-source-directory*)
321 :directory-search-list (list (merge-pathnames "Examples/GIFPlot/Interface/" *swig-source-directory*))))
323 (defvar *simple-gifplot-interface*
324 (run-swig (merge-pathnames "Examples/GIFPlot/Include/gifplot.h"
325 *swig-source-directory*)
326 :directory-search-list (list (merge-pathnames "Examples/GIFPlot/Interface/" *swig-source-directory*))
329 (defvar *cplex-glue-directory* #p"/home/mkoeppe/cvs/cplex-glue/")
331 (defvar *cplex-glue-interface*
332 (run-swig (merge-pathnames "cplex.i" *cplex-glue-directory*)
333 :directory-search-list (list (merge-pathnames "Lib/guile"
334 *swig-source-directory*)
335 *cplex-glue-directory*)
342 ;;(let ((*uffi-primitive-type-alist* (cons '("Pixel" . :unsigned-int) *uffi-default-primitive-type-alist*)))
343 ;; (eval (cons 'progn (compute-uffi-definitions *simple-gifplot-interface*))))
346 (with-open-file (f "/tmp/swig-uffi.lisp" :direction :output
347 :if-exists :supersede)
348 (let ((*uffi-definitions* '())
350 (*uffi-primitive-type-alist*
351 (cons '("Pixel" . :unsigned-int) *uffi-default-primitive-type-alist*)))
352 (apply 'handle-node *simple-gifplot-interface*)))
355 (with-open-file (f "/tmp/swig-uffi.lisp" :direction :output)
356 (let ((*uffi-definitions* '())
358 (*uffi-primitive-type-alist*
359 (cons '("Pixel" . :unsigned-int) *uffi-default-primitive-type-alist*)))
360 (apply 'handle-node *cplex-glue-interface*)))
362 (compile-file "/tmp/swig-uffi.lisp")
364 (uffi:load-foreign-library (merge-pathnames "Examples/GIFPlot/libgifplot.a"
365 *swig-source-directory*))
367 (load "/tmp/swig-uffi.lisp")
369 (load (merge-pathnames "Examples/GIFPlot/Common-Lisp/full/runme.lisp" *swig-source-directory*))
371 (action (namestring (merge-pathnames "Examples/GIFPlot/Common-Lisp/full/cmap"
372 *swig-source-directory*)))
376 ;;; Link to SWIG itself
380 (defparameter *c++-compiler* "g++")
382 (defun stdc++-library (&key env)
383 (let ((error-output (make-string-output-stream)))
384 (let ((name-output (make-string-output-stream)))
385 (let ((proc (ext:run-program
387 '("-print-file-name=libstdc++.so")
391 :error error-output)))
393 (error "Could not run ~A" *c++-compiler*))
394 (unless (zerop (ext:process-exit-code proc))
395 (system:serve-all-events 0)
396 (error "~A failed:~%~A" *c++-compiler*
397 (get-output-stream-string error-output))))
398 (string-right-trim '(#\Newline) (get-output-stream-string name-output)))))
400 (defvar *swig-interface* nil)
402 (defvar *swig-uffi-pathname* #p"/tmp/swig-uffi.lisp")
405 (setq *swig-interface*
406 (run-swig (merge-pathnames "Source/swig.i" *swig-source-directory*)
407 :directory-search-list
408 (list (merge-pathnames "Source/" *swig-source-directory*))
412 (with-open-file (f *swig-uffi-pathname* :direction :output)
413 (let ((*linkage* :c++)
414 (*uffi-definitions* '())
416 (*uffi-primitive-type-alist* *uffi-default-primitive-type-alist*))
417 (apply 'handle-node *swig-interface*)))
418 (compile-file *swig-uffi-pathname*)
419 (alien:load-foreign (merge-pathnames "Source/libswig.a"
420 *swig-source-directory*)
421 :libraries (list (stdc++-library)))
422 ;; FIXME: UFFI stuffes a "-l" in front of the passed library names
423 ;; (uffi:load-foreign-library (merge-pathnames "Source/libswig.a"
424 ;; *swig-source-directory*)
425 ;; :supporting-libraries
426 ;; (list (stdc++-library)))
427 (load (compile-file-pathname *swig-uffi-pathname*)))
433 ;; * How to do type lookups? Is everything important that SWIG knows
434 ;; about the types written out? What to make of typemaps?
436 ;; * Wrapped functions should probably automatically COERCE their
437 ;; arguments (as of type DOUBLE-FLOAT), to make the functions more
440 ;; * Why are the functions created by FFI interpreted?
442 ;; * We can't deal with more complicated structs and C++ classes
443 ;; directly with the FFI; we have to emit SWIG wrappers that access
446 ;; * A CLOS layer where structure fields are mapped as slots. It
447 ;; looks like we need MOP functions to implement this.
449 ;; * Maybe modify SWIG so that key-value hashes are distinguished from
450 ;; value-value hashes.