1 (in-package #:sb-grovel)
3 (defvar *default-c-stream* nil)
5 (defun escape-for-string (string)
8 (defun c-escape (string &optional (dangerous-chars '(#\")) (escape-char #\\))
9 "Escape DANGEROUS-CHARS in STRING, with ESCAPE-CHAR."
10 (coerce (loop for c across string
11 if (member c dangerous-chars) collect escape-char
15 (defun as-c (&rest args)
16 "Pretty-print ARGS into the C source file, separated by #\Space"
17 (format *default-c-stream* "~A~{ ~A~}~%" (first args) (rest args)))
19 (defun printf (formatter &rest args)
20 "Emit C code to printf the quoted code, via FORMAT.
21 The first argument is the C string that should be passed to
24 The rest of the arguments are consumed by FORMAT clauses, until
25 there are no more FORMAT clauses to fill. If there are more
26 arguments, they are emitted as printf arguments.
28 There is no error checking done, unless you pass too few FORMAT
29 clause args. I recommend using this formatting convention in
32 (printf \"string ~A ~S %d %d\" format-arg-1 format-arg-2
33 printf-arg-1 printf-arg-2)"
34 (let ((*print-pretty* nil))
35 (apply #'format *default-c-stream*
36 " printf (\"~@?\\n\"~@{, ~A~});~%"
40 (defun c-for-structure (lispname cstruct)
41 (destructuring-bind (cname &rest elements) cstruct
42 (printf "(cl:eval-when (:compile-toplevel :load-toplevel :execute) (sb-grovel::define-c-struct ~A %d" lispname
43 (format nil "sizeof(~A)" cname))
45 (destructuring-bind (lisp-type lisp-el-name c-type c-el-name &key distrust-length) e
46 (printf " (~A ~A \"~A\"" lisp-el-name lisp-type c-type)
50 (format nil "((unsigned long)&(t.~A)) - ((unsigned long)&(t))" c-el-name))
58 (format nil "sizeof(t.~A)" c-el-name))
62 (defun print-c-source (stream headers definitions package-name)
63 (declare (ignorable definitions package-name))
64 (let ((*default-c-stream* stream)
65 (*print-right-margin* nil))
66 (loop for i in (cons "stdio.h" headers)
67 do (format stream "#include <~A>~%" i))
68 (as-c "#define SIGNEDP(x) (((x)-1)<0)")
69 (as-c "#define SIGNED_(x) (SIGNEDP(x)?\"\":\"un\")")
71 (printf "(cl:in-package #:~A)" package-name)
72 (printf "(cl:eval-when (:compile-toplevel)")
73 (printf " (cl:defparameter *integer-sizes* (cl:make-hash-table))")
74 (dolist (type '("char" "short" "long" "int"
75 #+nil"long long" ; TODO: doesn't exist in sb-alien yet
77 (printf " (cl:setf (cl:gethash %d *integer-sizes*) 'sb-alien:~A)" (substitute #\- #\Space type)
78 (format nil "sizeof(~A)" type)))
80 (dolist (def definitions)
81 (destructuring-bind (type lispname cname &optional doc export) def
85 (printf "(cl:defconstant ~A %d \"~A\")" lispname doc
88 (printf "(sb-int:style-warn \"Couldn't grovel for ~A (unknown to the C compiler).\")" cname)
91 (printf "(cl:eval-when (:compile-toplevel :load-toplevel :execute) (sb-alien:define-alien-type ~A (sb-alien:%ssigned %d)))" lispname
92 (format nil "SIGNED_(~A)" cname)
93 (format nil "(8*sizeof(~A))" cname)))
95 (printf "(cl:defparameter ~A %s \"~A\"" lispname doc
98 (printf "(cl:declaim (cl:inline ~A))" lispname)
99 (destructuring-bind (f-cname &rest definition) cname
100 (printf "(sb-grovel::define-foreign-routine (\"~A\" ~A)" f-cname lispname)
101 (printf "~{ ~W~^\\n~})" definition)))
103 (c-for-structure lispname cname))
105 ;; should we really not sprechen espagnol, monsieurs?
106 (error "Unknown grovel keyword encountered: ~A" type)))
108 (printf "(cl:export '~A)" lispname))))
112 (defun c-constants-extract (filename output-file package)
113 (with-open-file (f output-file :direction :output :if-exists :supersede)
114 (with-open-file (i filename :direction :input)
115 (let* ((headers (read i))
116 (definitions (read i)))
117 (print-c-source f headers definitions package)))))
119 (defclass grovel-constants-file (asdf:cl-source-file)
120 ((package :accessor constants-package :initarg :package)))
122 (define-condition c-compile-failed (compile-failed) ()
123 (:report (lambda (c s)
124 (format s "~@<C compiler failed when performing ~A on ~A.~@:>"
125 (error-operation c) (error-component c)))))
126 (define-condition a-dot-out-failed (compile-failed) ()
127 (:report (lambda (c s)
128 (format s "~@<a.out failed when performing ~A on ~A.~@:>"
129 (error-operation c) (error-component c)))))
131 (defmethod asdf:perform ((op asdf:compile-op)
132 (component grovel-constants-file))
133 ;; we want to generate all our temporary files in the fasl directory
134 ;; because that's where we have write permission. Can't use /tmp;
135 ;; it's insecure (these files will later be owned by root)
136 (let* ((output-file (car (output-files op component)))
137 (filename (component-pathname component))
139 (if (typep output-file 'logical-pathname)
140 (translate-logical-pathname output-file)
141 (pathname output-file)))
142 (tmp-c-source (merge-pathnames #p"foo.c" real-output-file))
143 (tmp-a-dot-out (merge-pathnames #p"a.out" real-output-file))
144 (tmp-constants (merge-pathnames #p"constants.lisp-temp"
146 (princ (list filename output-file real-output-file
147 tmp-c-source tmp-a-dot-out tmp-constants))
149 (funcall (intern "C-CONSTANTS-EXTRACT" (find-package "SB-GROVEL"))
150 filename tmp-c-source (constants-package component))
151 (let ((code (run-shell-command "gcc ~A -o ~S ~S"
152 (if (sb-ext:posix-getenv "EXTRA_CFLAGS")
153 (sb-ext:posix-getenv "EXTRA_CFLAGS")
155 (namestring tmp-a-dot-out)
156 (namestring tmp-c-source))))
158 (case (operation-on-failure op)
159 (:warn (warn "~@<C compiler failure when performing ~A on ~A.~@:>"
162 (error 'c-compile-failed :operation op :component component)))))
163 (let ((code (run-shell-command "~A >~A"
164 (namestring tmp-a-dot-out)
165 (namestring tmp-constants))))
167 (case (operation-on-failure op)
168 (:warn (warn "~@<a.out failure when performing ~A on ~A.~@:>"
171 (error 'a-dot-out-failed :operation op :component component)))))
172 (multiple-value-bind (output warnings-p failure-p)
173 (compile-file tmp-constants :output-file output-file)
175 (case (operation-on-warnings op)
177 (formatter "~@<COMPILE-FILE warned while ~
178 performing ~A on ~A.~@:>")
180 (:error (error 'compile-warned :component component :operation op))
183 (case (operation-on-failure op)
185 (formatter "~@<COMPILE-FILE failed while ~
186 performing ~A on ~A.~@:>")
188 (:error (error 'compile-failed :component component :operation op))
191 (error 'compile-error :component component :operation op)))))