Remove some test noise. A drop in the ocean unfortunately.
[sbcl.git] / contrib / sb-grovel / def-to-lisp.lisp
blob2a5d9c316a276e254852a8a2847e1a56407dc9d4
1 (in-package #:sb-grovel)
3 (defvar *default-c-stream* nil)
5 (defun escape-for-string (string)
6 (c-escape string))
8 (defun split-cflags (string)
9 (remove-if (lambda (flag)
10 (zerop (length flag)))
11 (loop
12 for start = 0 then (if end (1+ end) nil)
13 for end = (and start (position #\Space string :start start))
14 while start
15 collect (subseq string start end))))
17 (defun c-escape (string &optional (dangerous-chars '(#\")) (escape-char #\\))
18 "Escape DANGEROUS-CHARS in STRING, with ESCAPE-CHAR."
19 (declare (simple-string string))
20 (coerce (loop for c across string
21 if (member c dangerous-chars) collect escape-char
22 collect c)
23 'string))
25 (defun as-c (&rest args)
26 "Pretty-print ARGS into the C source file, separated by #\Space"
27 (format *default-c-stream* "~A~{ ~A~}~%" (first args) (rest args)))
29 (defun printf (formatter &rest args)
30 "Emit C code to fprintf the quoted code, via FORMAT.
31 The first argument is the C string that should be passed to
32 printf.
34 The rest of the arguments are consumed by FORMAT clauses, until
35 there are no more FORMAT clauses to fill. If there are more
36 arguments, they are emitted as printf arguments.
38 There is no error checking done, unless you pass too few FORMAT
39 clause args. I recommend using this formatting convention in
40 code:
42 (printf \"string ~A ~S %d %d\" format-arg-1 format-arg-2
43 printf-arg-1 printf-arg-2)"
44 (let ((*print-pretty* nil))
45 (apply #'format *default-c-stream*
46 " fprintf (out, \"~@?\\n\"~@{, ~A~});~%"
47 (c-escape formatter)
48 args)))
50 (defun c-for-enum (lispname elements export)
51 (printf "(cl:eval-when (:compile-toplevel :load-toplevel :execute) (sb-alien:define-alien-type ~A (sb-alien:enum nil" lispname)
52 (dolist (element elements)
53 (destructuring-bind (lisp-element-name c-element-name) element
54 (printf " (~S %d)" lisp-element-name c-element-name)))
55 (printf ")))")
56 (when export
57 (dolist (element elements)
58 (destructuring-bind (lisp-element-name c-element-name) element
59 (declare (ignore c-element-name))
60 (unless (keywordp lisp-element-name)
61 (printf "(export '~S)" lisp-element-name))))))
63 (defun c-for-structure (lispname cstruct)
64 (destructuring-bind (cname &rest elements) cstruct
65 (printf "(cl:eval-when (:compile-toplevel :load-toplevel :execute) (sb-grovel::define-c-struct ~A %d" lispname
66 (format nil "sizeof(~A)" cname))
67 (dolist (e elements)
68 (destructuring-bind (lisp-type lisp-el-name c-type c-el-name &key distrust-length) e
69 (printf " (~A ~A \"~A\"" lisp-el-name lisp-type c-type)
70 ;; offset
71 (as-c "{" cname "t;")
72 (printf " %d"
73 (format nil "((unsigned long)&(t.~A)) - ((unsigned long)&(t))" c-el-name))
74 (as-c "}")
75 ;; length
76 (if distrust-length
77 (printf " 0)")
78 (progn
79 (as-c "{" cname "t;")
80 (printf " %d)"
81 (format nil "sizeof(t.~A)" c-el-name))
82 (as-c "}")))))
83 (printf "))")))
85 (defun print-c-source (stream headers definitions package-name)
86 (declare (ignorable definitions package-name))
87 (let ((*default-c-stream* stream)
88 (*print-right-margin* nil))
89 (loop for i in (cons "stdio.h" headers)
90 do (format stream "#include <~A>~%" i))
91 (as-c "#define SIGNEDP(x) (((x)-1)<0)")
92 (as-c "#define SIGNED_(x) (SIGNEDP(x)?\"\":\"un\")")
93 (as-c "int main(int argc, char *argv[]) {")
94 (as-c " FILE *out;")
95 (as-c " if (argc != 2) {")
96 (as-c " printf(\"Invalid argcount!\");")
97 (as-c " return 1;")
98 (as-c " } else")
99 (as-c " out = fopen(argv[1], \"w\");")
100 (as-c " if (!out) {")
101 (as-c " printf(\"Error opening output file!\");")
102 (as-c " return 1;")
103 (as-c " }")
104 (printf "(cl:in-package #:~A)" package-name)
105 (printf "(cl:eval-when (:compile-toplevel)")
106 (printf " (cl:defparameter *integer-sizes* (cl:make-hash-table))")
107 (dolist (type '("char" "short" "long" "int"
108 #+nil"long long" ; TODO: doesn't exist in sb-alien yet
110 (printf " (cl:setf (cl:gethash %d *integer-sizes*) 'sb-alien:~A)" (substitute #\- #\Space type)
111 (format nil "sizeof(~A)" type)))
112 (printf ")")
113 (dolist (def definitions)
114 (destructuring-bind (type lispname cname &optional doc export) def
115 (case type
116 ((:integer :errno)
117 (as-c "#ifdef" cname)
118 (printf "(cl:defconstant ~A %d \"~A\")" lispname doc
119 cname)
120 (when (eql type :errno)
121 (printf "(cl:setf (get '~A 'errno) t)" lispname))
122 (as-c "#else")
123 (printf "(sb-int:style-warn \"Couldn't grovel for ~~A (unknown to the C compiler).\" \"~A\")" cname)
124 (as-c "#endif"))
125 ((:integer-no-check)
126 (printf "(cl:defconstant ~A %d \"~A\")" lispname doc cname))
127 (:enum
128 (c-for-enum lispname cname export))
129 (:type
130 (printf "(cl:eval-when (:compile-toplevel :load-toplevel :execute) (sb-alien:define-alien-type ~A (sb-alien:%ssigned %d)))" lispname
131 (format nil "SIGNED_(~A)" cname)
132 (format nil "(8*sizeof(~A))" cname)))
133 (:string
134 (printf "(cl:defparameter ~A %s \"~A\"" lispname doc
135 cname))
136 (:function
137 (printf "(cl:declaim (cl:inline ~A))" lispname)
138 (destructuring-bind (f-cname &rest definition) cname
139 (printf "(sb-grovel::define-foreign-routine (\"~A\" ~A)" f-cname lispname)
140 (printf "~{ ~W~^\\n~})" definition)))
141 (:structure
142 (c-for-structure lispname cname))
143 (otherwise
144 ;; should we really not sprechen espagnol, monsieurs?
145 (error "Unknown grovel keyword encountered: ~A" type)))
146 (when export
147 (printf "(cl:export '~A)" lispname))))
148 (as-c "return 0;")
149 (as-c "}")))
151 (defun c-constants-extract (filename output-file package)
152 (with-open-file (f output-file :direction :output :if-exists :supersede)
153 (with-open-file (i filename :direction :input)
154 (let* ((headers (read i))
155 (definitions (read i)))
156 (print-c-source f headers definitions package)))))
158 (defclass grovel-constants-file (cl-source-file)
159 ((package :accessor constants-package :initarg :package)
160 (do-not-grovel :accessor do-not-grovel
161 :initform nil
162 :initarg :do-not-grovel)))
163 (defclass asdf::sb-grovel-constants-file (grovel-constants-file) ())
165 (define-condition c-compile-failed (compile-file-error)
166 ((description :initform "C compiler failed")))
167 (define-condition a-dot-out-failed (compile-file-error)
168 ((description :initform "a.out failed")))
170 (defmethod perform ((op compile-op)
171 (component grovel-constants-file))
172 ;; we want to generate all our temporary files in the fasl directory
173 ;; because that's where we have write permission. Can't use /tmp;
174 ;; it's insecure (these files will later be owned by root)
175 (let* ((output-files (output-files op component))
176 (output-file (first output-files))
177 (warnings-file (second output-files))
178 (filename (component-pathname component))
179 (context-format "~/asdf-action::format-action/")
180 (context-arguments `((,op . ,component)))
181 (condition-arguments `(:context-format ,context-format
182 :context-arguments ,context-arguments))
183 (real-output-file
184 (if (typep output-file 'logical-pathname)
185 (translate-logical-pathname output-file)
186 (pathname output-file)))
187 (tmp-c-source (merge-pathnames #p"foo.c" real-output-file))
188 (tmp-a-dot-out (merge-pathnames #-win32 #p"a.out" #+win32 #p"a.exe"
189 real-output-file))
190 (tmp-constants (merge-pathnames #p"constants.lisp-temp"
191 real-output-file)))
192 (princ (list filename output-file real-output-file
193 tmp-c-source tmp-a-dot-out tmp-constants))
194 (terpri)
195 (funcall (intern "C-CONSTANTS-EXTRACT" (find-package "SB-GROVEL"))
196 filename tmp-c-source (constants-package component))
197 (unless (do-not-grovel component)
198 (let* ((cc (or (and (string/= (sb-ext:posix-getenv "CC") "")
199 (sb-ext:posix-getenv "CC"))
200 (if (member :sb-building-contrib *features*)
201 (error "~@<The CC environment variable not set during ~
202 SB-GROVEL build.~:@>")
203 (sb-int:style-warn
204 "CC environment variable not set, SB-GROVEL falling back to \"cc\"."))
205 "cc"))
206 (code (sb-ext:process-exit-code
207 (sb-ext:run-program
209 (append
210 (split-cflags (sb-ext:posix-getenv "EXTRA_CFLAGS"))
211 #+(and linux largefile)
212 '("-D_LARGEFILE_SOURCE"
213 "-D_LARGEFILE64_SOURCE"
214 "-D_FILE_OFFSET_BITS=64")
215 #+(and (or x86 ppc) (or linux freebsd)) '("-m32")
216 #+(and x86-64 darwin inode64)
217 '("-arch" "x86_64"
218 "-mmacosx-version-min=10.5"
219 "-D_DARWIN_USE_64_BIT_INODE")
220 #+(and x86-64 darwin (not inode64))
221 '("-arch" "x86_64"
222 "-mmacosx-version-min=10.4")
223 #+(and x86 darwin)
224 '("-arch" "i386"
225 "-mmacosx-version-min=10.4")
226 #+(and x86-64 sunos) '("-m64")
227 (list "-o"
228 (namestring tmp-a-dot-out)
229 (namestring tmp-c-source)))
230 :search t
231 :input nil
232 :output *trace-output*))))
233 (unless (= code 0)
234 (apply 'error 'c-compile-failed condition-arguments)))
235 (let ((code (sb-ext:process-exit-code
236 (sb-ext:run-program (namestring tmp-a-dot-out)
237 (list (namestring tmp-constants))
238 :search nil
239 :input nil
240 :output *trace-output*))))
241 (unless (= code 0)
242 (apply 'error 'a-dot-out-failed condition-arguments)))
243 (multiple-value-bind (output warnings-p failure-p)
244 (compile-file* tmp-constants :output-file output-file :warnings-file warnings-file)
245 (check-lisp-compile-results output warnings-p failure-p context-format context-arguments)))))