1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; grovel.lisp --- The CFFI Groveller.
5 ;;; Copyright (C) 2005-2006, Dan Knap <dankna@accela.net>
6 ;;; Copyright (C) 2005-2006, Emily Backes <lucca@accela.net>
7 ;;; Copyright (C) 2007, Stelian Ionescu <sionescu@cddr.org>
8 ;;; Copyright (C) 2007, Luis Oliveira <loliveira@common-lisp.net>
10 ;;; Permission is hereby granted, free of charge, to any person
11 ;;; obtaining a copy of this software and associated documentation
12 ;;; files (the "Software"), to deal in the Software without
13 ;;; restriction, including without limitation the rights to use, copy,
14 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
15 ;;; of the Software, and to permit persons to whom the Software is
16 ;;; furnished to do so, subject to the following conditions:
18 ;;; The above copyright notice and this permission notice shall be
19 ;;; included in all copies or substantial portions of the Software.
21 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 ;;; DEALINGS IN THE SOFTWARE.
31 (in-package #:cffi-grovel
)
35 (defun trim-whitespace (strings)
36 (loop for s in strings
37 collect
(string-trim '(#\Space
#\Tab
) s
)))
41 ;;; This warning is signalled when cffi-grovel can't find some macro.
42 ;;; Signalled by CONSTANT or CONSTANTENUM.
43 (define-condition missing-definition
(warning)
44 ((%name
:initarg
:name
:reader name-of
))
45 (:report
(lambda (condition stream
)
46 (format stream
"No definition for ~A"
47 (name-of condition
)))))
51 ;;; The header of the intermediate C file.
52 (defparameter *header
*
54 * This file has been automatically generated by cffi-grovel.
55 * Do not edit it by hand.
60 ;;; C code generated by cffi-grovel is inserted between the contents
61 ;;; of *PROLOGUE* and *POSTSCRIPT*, inside the main function's body.
63 (defparameter *prologue
*
65 #include <grovel/common.h>
67 int main(int argc, char**argv) {
69 FILE *output = argc > 1 ? fopen(argv[1], \"w\") : stdout;
70 fprintf(output, \";;;; This file has been automatically generated by \"
71 \"cffi-grovel.\\n;;;; Do not edit it by hand.\\n\\n\");
74 (defparameter *postscript
*
82 (defun unescape-for-c (text)
83 (with-output-to-string (result)
84 (loop for i below
(length text
)
85 for char
= (char text i
) do
86 (cond ((eql char
#\") (princ "\\\"" result
))
87 ((eql char
#\newline
) (princ "\\n" result
))
88 (t (princ char result
))))))
90 (defun c-format (out fmt
&rest args
)
91 (let ((text (unescape-for-c (format nil
"~?" fmt args
))))
92 (format out
"~& fputs(\"~A\", output);~%" text
)))
94 (defun c-printf (out fmt
&rest args
)
96 (format out
"~A" (unescape-for-c (format nil item
)))))
97 (format out
"~& fprintf(output, \"")
100 (loop for arg in args do
103 (format out
");~%")))
105 ;;; TODO: handle packages in a better way. One way is to process each
106 ;;; grovel form as it is read (like we already do for wrapper
107 ;;; forms). This way in can expect *PACKAGE* to have sane values.
108 ;;; This would require that "header forms" come before any other
110 (defun c-print-symbol (out symbol
&optional no-package
)
112 (let ((package (symbol-package symbol
)))
114 ((eq (find-package '#:keyword
) package
) ":~(~A~)")
115 (no-package "~(~A~)")
116 ((eq (find-package '#:cl
) package
) "cl:~(~A~)")
120 (defun c-write (out form
&optional no-package
)
123 (eq 'quote
(car form
)))
125 (c-write out
(cadr form
) no-package
))
128 (loop for subform in form
129 for first-p
= t then nil
130 unless first-p do
(c-format out
" ")
131 do
(c-write out subform no-package
))
134 (c-print-symbol out form no-package
))))
136 ;;; Always NIL for now, add {ENABLE,DISABLE}-AUTO-EXPORT grovel forms
137 ;;; later, if necessary.
138 (defvar *auto-export
* nil
)
140 (defun c-export (out symbol
)
141 (when (and *auto-export
* (not (keywordp symbol
)))
142 (c-format out
"(cl:export '")
143 (c-print-symbol out symbol t
)
144 (c-format out
")~%")))
146 (defun c-section-header (out section-type section-symbol
)
147 (format out
"~% /* ~A section for ~S */~%"
151 (defun remove-suffix (string suffix
)
152 (let ((suffix-start (- (length string
) (length suffix
))))
153 (if (and (> suffix-start
0)
154 (string= string suffix
:start1 suffix-start
))
155 (subseq string
0 suffix-start
)
158 (defun strcat (&rest strings
)
159 (apply #'concatenate
'string strings
))
161 (defgeneric %process-grovel-form
(name out arguments
)
162 (:method
(name out arguments
)
163 (declare (ignore out arguments
))
164 (error "Unknown Grovel syntax: ~S" name
)))
166 (defun process-grovel-form (out form
)
167 (%process-grovel-form
(form-kind form
) out
(cdr form
)))
169 (defun form-kind (form)
170 ;; Using INTERN here instead of FIND-SYMBOL will result in less
171 ;; cryptic error messages when an undefined grovel/wrapper form is
173 (intern (symbol-name (car form
)) '#:cffi-grovel
))
175 (defvar *header-forms
* '(c include define flag typedef
))
177 (defun header-form-p (form)
178 (member (form-kind form
) *header-forms
*))
180 (defun make-c-file-name (output-defaults)
181 (make-pathname :type
"c" :defaults output-defaults
))
183 (defun generate-c-file (input-file output-defaults
)
184 (let ((c-file (make-c-file-name output-defaults
)))
185 (with-open-file (out c-file
:direction
:output
:if-exists
:supersede
)
186 (with-open-file (in input-file
:direction
:input
)
187 (flet ((read-forms (s)
189 (form (read s nil nil
) (read s nil nil
)))
190 ((null form
) (nreverse forms
))
194 (flag (warn "Groveler clause FLAG is deprecated, use CC-FLAGS instead.")))
197 (setf *package
* (find-package (second f
)))
200 ;; flatten progn forms
201 (mapc #'process-form
(rest f
)))
202 (t (push f forms
)))))
203 (process-form form
)))))
204 (let* ((forms (read-forms in
))
205 (header-forms (remove-if-not #'header-form-p forms
))
206 (body-forms (remove-if #'header-form-p forms
)))
207 (write-string *header
* out
)
208 (dolist (form header-forms
)
209 (process-grovel-form out form
))
210 (write-string *prologue
* out
)
211 (dolist (form body-forms
)
212 (process-grovel-form out form
))
213 (write-string *postscript
* out
)))))
216 (defparameter *exe-extension
* #-windows nil
#+windows
"exe")
218 (defun exe-filename (defaults)
219 (let ((path (make-pathname :type
*exe-extension
*
220 :defaults defaults
)))
221 ;; It's necessary to prepend "./" to relative paths because some
222 ;; implementations of INVOKE use a shell.
223 (when (or (not (pathname-directory path
))
224 (eq :relative
(car (pathname-directory path
))))
225 (setf path
(make-pathname
226 :directory
(list* :relative
"."
227 (cdr (pathname-directory path
)))
231 (defun tmp-lisp-filename (defaults)
232 (make-pathname :name
(strcat (pathname-name defaults
) ".grovel-tmp")
233 :type
"lisp" :defaults defaults
))
235 (cffi:defcfun
"getenv" :string
240 #+(or cygwin
(not windows
)) "cc"
241 #+(and windows
(not cygwin
)) "c:/msys/1.0/bin/gcc.exe")
243 (defparameter *cc-flags
*
246 #+darwin
(list "-I" "/opt/local/include/")
248 ;; ECL internal flags
249 #+ecl
(list c
::*cc-flags
*)
250 ;; FreeBSD non-base header files
251 #+freebsd
(list "-I" "/usr/local/include/")))
253 ;;; FIXME: is there a better way to detect whether these flags
255 (defparameter *cpu-word-size-flags
*
259 (ecase (cffi:foreign-type-size
:pointer
)
263 (defparameter *platform-library-flags
*
264 (list #+darwin
"-bundle"
268 (defun cc-compile-and-link (input-file output-file
&key library
)
270 `(,(or (getenv "CC") *cc
*)
271 ,@*cpu-word-size-flags
*
273 ;; add the cffi directory to the include path to make common.h visible
275 (directory-namestring
277 (asdf:system-definition-pathname
:cffi-grovel
))))
278 ,@(when library
*platform-library-flags
*)
279 "-o" ,(native-namestring output-file
)
280 ,(native-namestring input-file
))))
282 ;; if it's a library that may be used, remove it
283 ;; so we won't possibly be overwriting the code of any existing process
284 (ignore-some-conditions (file-error)
285 (delete-file output-file
)))
286 (apply #'invoke arglist
)))
288 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
289 ;;; *the extent of a given grovel file.
290 (defun process-grovel-file (input-file &optional
(output-defaults input-file
))
291 (with-standard-io-syntax
292 (let* ((c-file (generate-c-file input-file output-defaults
))
293 (exe-file (exe-filename c-file
))
294 (lisp-file (tmp-lisp-filename c-file
)))
295 (cc-compile-and-link c-file exe-file
)
296 (invoke exe-file
(native-namestring lisp-file
))
299 ;;; OUT is lexically bound to the output stream within BODY.
300 (defmacro define-grovel-syntax
(name lambda-list
&body body
)
301 (with-unique-names (name-var args
)
302 `(defmethod %process-grovel-form
((,name-var
(eql ',name
)) out
,args
)
303 (declare (ignorable out
))
304 (destructuring-bind ,lambda-list
,args
307 (define-grovel-syntax c
(body)
308 (format out
"~%~A~%" body
))
310 (define-grovel-syntax include
(&rest includes
)
311 (format out
"~{#include <~A>~%~}" includes
))
313 (define-grovel-syntax define
(name &optional value
)
314 (format out
"#define ~A~@[ ~A~]~%" name value
))
316 (define-grovel-syntax typedef
(base-type new-type
)
317 (format out
"typedef ~A ~A;~%" base-type new-type
))
319 ;;; Is this really needed?
320 (define-grovel-syntax ffi-typedef
(new-type base-type
)
321 (c-format out
"(cffi:defctype ~S ~S)~%" new-type base-type
))
323 (define-grovel-syntax flag
(&rest flags
)
324 (appendf *cc-flags
* (trim-whitespace flags
)))
326 (define-grovel-syntax cc-flags
(&rest flags
)
327 (appendf *cc-flags
* (trim-whitespace flags
)))
329 ;;; This form also has some "read time" effects. See GENERATE-C-FILE.
330 (define-grovel-syntax in-package
(name)
331 (c-format out
"(cl:in-package #:~A)~%~%" name
))
333 (define-grovel-syntax ctype
(lisp-name size-designator
)
334 (c-section-header out
"ctype" lisp-name
)
335 (c-export out lisp-name
)
336 (c-format out
"(cffi:defctype ")
337 (c-print-symbol out lisp-name t
)
339 (format out
"~& type_name(output, TYPE_SIGNED_P(~A), ~:[sizeof(~A)~;~D~]);~%"
341 (etypecase size-designator
346 (unless (keywordp lisp-name
)
347 (c-export out lisp-name
))
348 (let ((size-of-constant-name (symbolicate '#:size-of- lisp-name
)))
349 (c-export out size-of-constant-name
)
350 (c-format out
"(cl:defconstant "
351 size-of-constant-name lisp-name
)
352 (c-print-symbol out size-of-constant-name
)
353 (c-format out
" (cffi:foreign-type-size '")
354 (c-print-symbol out lisp-name
)
355 (c-format out
"))~%")))
357 ;;; Syntax differs from anything else in CFFI. Fix?
358 (define-grovel-syntax constant
((lisp-name &rest c-names
)
359 &key
(type 'integer
) documentation optional
)
360 (when (keywordp lisp-name
)
361 (setf lisp-name
(format-symbol "~A" lisp-name
)))
362 (c-section-header out
"constant" lisp-name
)
363 (dolist (c-name c-names
)
364 (format out
"~&#ifdef ~A~%" c-name
)
365 (c-export out lisp-name
)
366 (c-format out
"(cl:defconstant ")
367 (c-print-symbol out lisp-name t
)
371 (format out
"~& if(_64_BIT_VALUE_FITS_SIGNED_P(~A))~%" c-name
)
372 (format out
" fprintf(output, \"%lli\", (int64_t) ~A);" c-name
)
373 (format out
"~& else~%")
374 (format out
" fprintf(output, \"%llu\", (uint64_t) ~A);" c-name
))
376 (format out
"~& fprintf(output, \"%s\", print_double_for_lisp((double)~A));~%" c-name
)))
378 (c-format out
" ~S" documentation
))
380 (format out
"~&#else~%"))
382 (c-format out
"(cl:warn 'cffi-grovel:missing-definition :name '~A)~%"
384 (dotimes (i (length c-names
))
385 (format out
"~&#endif~%")))
387 (define-grovel-syntax cunion
(union-lisp-name union-c-name
&rest slots
)
388 (let ((documentation (when (stringp (car slots
)) (pop slots
))))
389 (c-section-header out
"cunion" union-lisp-name
)
390 (c-export out union-lisp-name
)
392 (let ((slot-lisp-name (car slot
)))
393 (c-export out slot-lisp-name
)))
394 (c-format out
"(cffi:defcunion (")
395 (c-print-symbol out union-lisp-name t
)
396 (c-printf out
" :size %i)" (format nil
"sizeof(~A)" union-c-name
))
398 (c-format out
"~% ~S" documentation
))
400 (destructuring-bind (slot-lisp-name slot-c-name
&key type count
)
402 (declare (ignore slot-c-name
))
403 (c-format out
"~% (")
404 (c-print-symbol out slot-lisp-name t
)
409 (c-format out
" :count ~D" count
))
411 ;; nb, works like :count :auto does in cstruct below
412 (c-printf out
" :count %i"
413 (format nil
"sizeof(~A)" union-c-name
)))
416 (c-format out
")~%")))
418 (defun make-from-pointer-function-name (type-name)
419 (symbolicate '#:make- type-name
'#:-from-pointer
))
421 ;;; DEFINE-C-STRUCT-WRAPPER (in ../src/types.lisp) seems like a much
422 ;;; cleaner way to do this. Unless I can find any advantage in doing
423 ;;; it this way I'll delete this soon. --luis
424 (define-grovel-syntax cstruct-and-class-item
(&rest arguments
)
425 (process-grovel-form out
(cons 'cstruct arguments
))
426 (destructuring-bind (struct-lisp-name struct-c-name
&rest slots
)
428 (declare (ignore struct-c-name
))
429 (let* ((slot-names (mapcar #'car slots
))
430 (reader-names (mapcar
433 (strcat (symbol-name struct-lisp-name
) "-"
434 (symbol-name slot-name
))))
436 (initarg-names (mapcar
438 (intern (symbol-name slot-name
) "KEYWORD"))
440 (slot-decoders (mapcar (lambda (slot)
446 (declare (ignore lisp-name c-name
))
447 (cond ((and (eq type
:char
) count
)
448 'cffi
:foreign-string-to-lisp
)
452 `(defclass ,struct-lisp-name
()
453 ,(mapcar (lambda (slot-name initarg-name reader-name
)
454 `(,slot-name
:initarg
,initarg-name
455 :reader
,reader-name
))
460 (make-from-pointer-function-name struct-lisp-name
))
462 ;; this function is then used as a constructor for this class.
463 `(defun ,make-function-name
(pointer)
464 (cffi:with-foreign-slots
465 (,slot-names pointer
,struct-lisp-name
)
466 (make-instance ',struct-lisp-name
467 ,@(loop for slot-name in slot-names
468 for initarg-name in initarg-names
469 for slot-decoder in slot-decoders
472 collect
`(,slot-decoder
,slot-name
)
473 else collect slot-name
))))))
474 (c-export out make-function-name
)
475 (dolist (reader-name reader-names
)
476 (c-export out reader-name
))
477 (c-write out defclass-form
)
478 (c-write out make-defun-form
))))
480 (define-grovel-syntax cstruct
(struct-lisp-name struct-c-name
&rest slots
)
481 (let ((documentation (when (stringp (car slots
)) (pop slots
))))
482 (c-section-header out
"cstruct" struct-lisp-name
)
483 (c-export out struct-lisp-name
)
485 (let ((slot-lisp-name (car slot
)))
486 (c-export out slot-lisp-name
)))
487 (c-format out
"(cffi:defcstruct (")
488 (c-print-symbol out struct-lisp-name t
)
489 (c-printf out
" :size %i)"
490 (format nil
"sizeof(~A)" struct-c-name
))
492 (c-format out
"~% ~S" documentation
))
494 (destructuring-bind (slot-lisp-name slot-c-name
&key type count
)
496 (c-format out
"~% (")
497 (c-print-symbol out slot-lisp-name t
)
501 (format out
"~& SLOT_SIGNED_P(autotype_tmp, ~A, ~A~@[[0]~]);~@*~%~
502 ~& type_name(output, autotype_tmp, sizeofslot(~A, ~A~@[[0]~]));~%"
509 (c-format out
"~A" type
)))
513 (c-format out
" :count ~D" count
))
515 (c-printf out
" :count %i"
516 (format nil
"countofslot(~A, ~A)"
520 (format out
"~&#ifdef ~A~%" count
)
521 (c-printf out
" :count %i"
522 (format nil
"~A" count
))
523 (format out
"~&#endif~%")))
524 (c-printf out
" :offset %li)"
525 (format nil
"offsetof(~A, ~A)"
529 (let ((size-of-constant-name
530 (symbolicate '#:size-of- struct-lisp-name
)))
531 (c-export out size-of-constant-name
)
532 (c-format out
"(cl:defconstant "
533 size-of-constant-name struct-lisp-name
)
534 (c-print-symbol out size-of-constant-name
)
535 (c-format out
" (cffi:foreign-type-size '(:struct ")
536 (c-print-symbol out struct-lisp-name
)
537 (c-format out
")))~%"))))
539 (defmacro define-pseudo-cvar
(str name type
&key read-only
)
540 (let ((c-parse (let ((*read-eval
* nil
)
541 (*readtable
* (copy-readtable nil
)))
542 (setf (readtable-case *readtable
*) :preserve
)
543 (read-from-string str
))))
545 (symbol `(cffi:defcvar
(,(symbol-name c-parse
) ,name
546 :read-only
,read-only
)
548 (list (unless (and (= (length c-parse
) 2)
549 (null (second c-parse
))
550 (symbolp (first c-parse
))
551 (eql #\
* (char (symbol-name (first c-parse
)) 0)))
552 (error "Unable to parse c-string ~s." str
))
553 (let ((func-name (symbolicate "%" name
'#:-accessor
)))
555 (declaim (inline ,func-name
))
556 (cffi:defcfun
(,(string-trim "*" (symbol-name (first c-parse
)))
557 ,func-name
) :pointer
)
558 (define-symbol-macro ,name
559 (cffi:mem-ref
(,func-name
) ',type
)))))
560 (t (error "Unable to parse c-string ~s." str
)))))
562 (defun foreign-name-to-symbol (s)
563 (intern (substitute #\-
#\_
(string-upcase s
))))
565 (defun choose-lisp-and-foreign-names (string-or-list)
566 (etypecase string-or-list
567 (string (values string-or-list
(foreign-name-to-symbol string-or-list
)))
568 (list (destructuring-bind (fname lname
&rest args
) string-or-list
569 (declare (ignore args
))
570 (assert (and (stringp fname
) (symbolp lname
)))
571 (values fname lname
)))))
573 (define-grovel-syntax cvar
(name type
&key read-only
)
574 (multiple-value-bind (c-name lisp-name
)
575 (choose-lisp-and-foreign-names name
)
576 (c-section-header out
"cvar" lisp-name
)
577 (c-export out lisp-name
)
578 (c-printf out
"(cffi-grovel::define-pseudo-cvar \"%s\" "
579 (format nil
"indirect_stringify(~A)" c-name
))
580 (c-print-symbol out lisp-name t
)
584 (c-format out
" :read-only t"))
585 (c-format out
")~%")))
587 ;;; FIXME: where would docs on enum elements go?
588 (define-grovel-syntax cenum
(name &rest enum-list
)
589 (destructuring-bind (name &key base-type define-constants
)
591 (c-section-header out
"cenum" name
)
593 (c-format out
"(cffi:defcenum (")
594 (c-print-symbol out name t
)
597 (c-print-symbol out base-type t
))
599 (dolist (enum enum-list
)
600 (destructuring-bind ((lisp-name &rest c-names
) &key documentation
)
602 (declare (ignore documentation
))
603 (check-type lisp-name keyword
)
604 (loop :for c-name
:in c-names
:do
605 (check-type c-name string
)
607 (c-print-symbol out lisp-name
)
609 (c-printf out
"%i" c-name
)
610 (c-format out
")~%"))))
612 (when define-constants
613 (define-constants-from-enum out enum-list
))))
615 (define-grovel-syntax constantenum
(name &rest enum-list
)
616 (destructuring-bind (name &key base-type define-constants
)
618 (c-section-header out
"constantenum" name
)
620 (c-format out
"(cffi:defcenum (")
621 (c-print-symbol out name t
)
624 (c-print-symbol out base-type t
))
626 (dolist (enum enum-list
)
627 (destructuring-bind ((lisp-name &rest c-names
)
628 &key optional documentation
) enum
629 (declare (ignore documentation
))
630 (check-type lisp-name keyword
)
631 (c-format out
"~% (")
632 (c-print-symbol out lisp-name
)
633 (loop for c-name in c-names do
634 (check-type c-name string
)
635 (format out
"~&#ifdef ~A~%" c-name
)
637 (c-printf out
"%i" c-name
)
638 (format out
"~&#else~%"))
642 (cl:warn 'cffi-grovel:missing-definition :name '~A) ~
645 (dotimes (i (length c-names
))
646 (format out
"~&#endif~%"))
649 (when define-constants
650 (define-constants-from-enum out enum-list
))))
652 (defun define-constants-from-enum (out enum-list
)
653 (dolist (enum enum-list
)
654 (destructuring-bind ((lisp-name &rest c-names
) &rest options
)
656 (%process-grovel-form
658 `((,(intern (string lisp-name
)) ,(car c-names
))
661 (defun foreign-type-to-printf-specification (type)
662 "Return the printf specification associated with the foreign type TYPE."
666 ((:unsigned-char
:uchar
)
670 ((:unsigned-short
:ushort
)
674 ((:unsigned-int
:uint
)
678 ((:unsigned-long
:ulong
)
682 ((:unsigned-long-long
:ullong
)
701 ;; Defines a bitfield, with elements specified as ((LISP-NAME C-NAME)
702 ;; &key DOCUMENTATION). NAME-AND-OPTS can be either a symbol as name,
703 ;; or a list (NAME &key BASE-TYPE).
704 (define-grovel-syntax bitfield
(name-and-opts &rest masks
)
705 (destructuring-bind (name &key base-type
)
706 (ensure-list name-and-opts
)
707 (c-section-header out
"bitfield" name
)
709 (c-format out
"(cffi:defbitfield (")
710 (c-print-symbol out name t
)
713 (c-print-symbol out base-type t
))
716 (destructuring-bind ((lisp-name c-name
) &key documentation
) mask
717 (declare (ignore documentation
))
718 (check-type lisp-name symbol
)
719 (check-type c-name string
)
720 (c-format out
"~% (")
721 (c-print-symbol out lisp-name
)
723 (format out
"~& fprintf(output, ~A, ~A);~%"
724 (foreign-type-to-printf-specification (or base-type
:int
))
727 (c-format out
")~%")))
730 ;;;# Wrapper Generation
732 ;;; Here we generate a C file from a s-exp specification but instead
733 ;;; of compiling and running it, we compile it as a shared library
734 ;;; that can be subsequently loaded with LOAD-FOREIGN-LIBRARY.
736 ;;; Useful to get at macro functionality, errno, system calls,
737 ;;; functions that handle structures by value, etc...
739 ;;; Matching CFFI bindings are generated along with said C file.
741 (defun process-wrapper-form (out form
)
742 (%process-wrapper-form
(form-kind form
) out
(cdr form
)))
744 ;;; The various operators push Lisp forms onto this list which will be
745 ;;; written out by PROCESS-WRAPPER-FILE once everything is processed.
746 (defvar *lisp-forms
*)
748 (defun generate-c-lib-file (input-file output-defaults
)
749 (let ((*lisp-forms
* nil
)
750 (c-file (make-c-file-name output-defaults
)))
751 (with-open-file (out c-file
:direction
:output
:if-exists
:supersede
)
752 (with-open-file (in input-file
:direction
:input
)
753 (write-string *header
* out
)
754 (loop for form
= (read in nil nil
) while form
755 do
(process-wrapper-form out form
))))
756 (values c-file
(nreverse *lisp-forms
*))))
758 (defun lib-filename (defaults)
759 (make-pathname :type
(subseq (cffi::default-library-suffix
) 1)
762 (defun generate-bindings-file (lib-file lib-soname lisp-forms output-defaults
)
763 (let ((lisp-file (tmp-lisp-filename output-defaults
)))
764 (with-open-file (out lisp-file
:direction
:output
:if-exists
:supersede
)
765 (format out
";;;; This file was automatically generated by cffi-grovel.~%~
766 ;;;; Do not edit by hand.~%")
767 (let ((*package
* (find-package '#:cl
))
769 (let ((*package
* (find-package :keyword
))
771 (read-from-string lib-soname
))))
773 (cffi:define-foreign-library
775 :type
:grovel-wrapper
776 :search-path
,(directory-namestring lib-file
))
777 (t ,(namestring (lib-filename lib-soname
))))
778 (cffi:use-foreign-library
,named-library-name
))
781 (dolist (form lisp-forms
)
786 (defun make-soname (lib-soname output-defaults
)
787 (make-pathname :name lib-soname
788 :defaults output-defaults
))
790 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
791 ;;; *the extent of a given wrapper file.
792 (defun process-wrapper-file (input-file output-defaults lib-soname
)
793 (with-standard-io-syntax
795 (lib-filename (make-soname lib-soname output-defaults
))))
796 (multiple-value-bind (c-file lisp-forms
)
797 (generate-c-lib-file input-file output-defaults
)
798 (cc-compile-and-link c-file lib-file
:library t
)
799 ;; FIXME: hardcoded library path.
800 (values (generate-bindings-file lib-file lib-soname lisp-forms output-defaults
)
803 (defgeneric %process-wrapper-form
(name out arguments
)
804 (:method
(name out arguments
)
805 (declare (ignore out arguments
))
806 (error "Unknown Grovel syntax: ~S" name
)))
808 ;;; OUT is lexically bound to the output stream within BODY.
809 (defmacro define-wrapper-syntax
(name lambda-list
&body body
)
810 (with-unique-names (name-var args
)
811 `(defmethod %process-wrapper-form
((,name-var
(eql ',name
)) out
,args
)
812 (declare (ignorable out
))
813 (destructuring-bind ,lambda-list
,args
816 (define-wrapper-syntax progn
(&rest forms
)
818 (process-wrapper-form out form
)))
820 (define-wrapper-syntax in-package
(name)
821 (setq *package
* (find-package name
))
822 (push `(in-package ,name
) *lisp-forms
*))
824 (define-wrapper-syntax c
(&rest strings
)
825 (dolist (string strings
)
826 (write-line string out
)))
828 (define-wrapper-syntax flag
(&rest flags
)
829 (appendf *cc-flags
* (trim-whitespace flags
)))
831 (define-wrapper-syntax proclaim
(&rest proclamations
)
832 (push `(proclaim ,@proclamations
) *lisp-forms
*))
834 (define-wrapper-syntax declaim
(&rest declamations
)
835 (push `(declaim ,@declamations
) *lisp-forms
*))
837 (define-wrapper-syntax define
(name &optional value
)
838 (format out
"#define ~A~@[ ~A~]~%" name value
))
840 (define-wrapper-syntax include
(&rest includes
)
841 (format out
"~{#include <~A>~%~}" includes
))
843 ;;; FIXME: this function is not complete. Should probably follow
844 ;;; typedefs? Should definitely understand pointer types.
845 (defun c-type-name (typespec)
846 (let ((spec (ensure-list typespec
)))
847 (if (stringp (car spec
))
850 ((:uchar
:unsigned-char
) "unsigned char")
851 ((:unsigned-short
:ushort
) "unsigned short")
852 ((:unsigned-int
:uint
) "unsigned int")
853 ((:unsigned-long
:ulong
) "unsigned long")
854 ((:long-long
:llong
) "long long")
855 ((:unsigned-long-long
:ullong
) "unsigned long long")
858 (t (cffi::foreign-name
(car spec
) nil
))))))
860 (defun cffi-type (typespec)
861 (if (and (listp typespec
) (stringp (car typespec
)))
866 (check-type s
(and symbol
(not null
)))
869 (define-wrapper-syntax defwrapper
(name-and-options rettype
&rest args
)
870 (multiple-value-bind (lisp-name foreign-name options
)
871 (cffi::parse-name-and-options name-and-options
)
872 (let* ((foreign-name-wrap (strcat foreign-name
"_cffi_wrap"))
873 (fargs (mapcar (lambda (arg)
874 (list (c-type-name (second arg
))
875 (cffi::foreign-name
(first arg
) nil
)))
877 (fargnames (mapcar #'second fargs
)))
879 (format out
"~A ~A" (c-type-name rettype
) foreign-name-wrap
)
880 (format out
"(~{~{~A ~A~}~^, ~})~%" fargs
)
881 (format out
"{~% return ~A(~{~A~^, ~});~%}~%~%" foreign-name fargnames
)
883 (push `(cffi:defcfun
(,foreign-name-wrap
,lisp-name
,@options
)
885 ,@(mapcar (lambda (arg)
886 (list (symbol* (first arg
))
887 (cffi-type (second arg
))))
891 (define-wrapper-syntax defwrapper
* (name-and-options rettype args
&rest c-lines
)
893 (multiple-value-bind (lisp-name foreign-name options
)
894 (cffi::parse-name-and-options name-and-options
)
895 (let ((foreign-name-wrap (strcat foreign-name
"_cffi_wrap"))
896 (fargs (mapcar (lambda (arg)
897 (list (c-type-name (second arg
))
898 (cffi::foreign-name
(first arg
) nil
)))
900 (format out
"~A ~A" (c-type-name rettype
)
902 (format out
"(~{~{~A ~A~}~^, ~})~%" fargs
)
903 (format out
"{~%~{ ~A~%~}}~%~%" c-lines
)
905 (push `(cffi:defcfun
(,foreign-name-wrap
,lisp-name
,@options
)
907 ,@(mapcar (lambda (arg)
908 (list (symbol* (first arg
))
909 (cffi-type (second arg
))))