1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; c-toolchain.lisp --- Generic support compiling and linking C code.
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-toolchain
)
35 (defun parse-command-flags (flags)
36 (let ((separators '(#\Space
#\Tab
#\Newline
#\Return
)))
37 (remove-if 'emptyp
(split-string flags
:separator separators
))))
39 (defun parse-command-flags-list (strings)
40 (loop for flags in strings append
(parse-command-flags flags
)))
42 (defun program-argument (x)
45 (pathname (native-namestring x
))))
47 (defun invoke (command &rest args
)
48 (when (pathnamep command
)
49 (setf command
(native-namestring command
))
51 (unless (absolute-pathname-p command
)
52 (setf command
(strcat "./" command
))))
53 (let ((cmd (cons command
(mapcar 'program-argument args
))))
54 (safe-format! *debug-io
* "; ~A~%" (escape-command cmd
))
55 (run-program cmd
:output
:interactive
:error-output
:interactive
)))
60 (defparameter *cc
* nil
"C compiler")
61 (defparameter *cc-flags
* nil
"flags for the C compiler")
62 (defparameter *ld
* nil
"object linker") ;; NB: can actually be the same as *cc*
63 (defparameter *ld-exe-flags
* nil
"flags for linking executables via *ld*")
64 (defparameter *ld-dll-flags
* nil
"flags for linking shared library via *ld*")
65 (defparameter *linkkit-start
* nil
"flags for the implementation linkkit, start")
66 (defparameter *linkkit-end
* nil
"flags for the implementation linkkit, end")
68 (defun link-all-library (lib)
69 ;; Flags to provide to cc to link a whole library into an executable
71 (if (featurep :darwin
) ;; actually, LLVM ld vs GNU ld
72 `("-Wl,-force_load" ,lib
)
73 `("-Wl,--whole-archive" ,lib
"-Wl,--no-whole-archive"))))
75 (defun normalize-flags (directory flags
)
76 (loop for val in
(parse-command-flags flags
) collect
78 ((find (first-char val
) "-+/") val
)
79 ((probe-file* (subpathname directory val
)))
82 (defun implementation-file (file &optional type
)
83 (subpathname (lisp-implementation-directory) file
84 :type
(bundle-pathname-type type
)))
86 ;; TODO: on CCL, extract data from
87 ;; (pathname (strcat "ccl:lisp-kernel/" (ccl::kernel-build-directory) "/Makefile")) ?
91 (defparameter *clisp-toolchain-parameters
*
93 ("CFLAGS" *cc-flags
* t
)
94 ("CLFLAGS" *cc-exe-flags
* t
)
95 ("LIBS" *linkkit-start
* t
)
96 ("X_LIBS" *linkkit-end
* t
)))
97 (defun clisp-toolchain-parameters (&optional linkset
)
99 (let* ((linkset (ensure-pathname
101 :defaults
(lisp-implementation-directory)
105 (makevars (subpathname linkset
"makevars"))))
106 (with-input-file (params makevars
:if-does-not-exist nil
))
108 (loop for l
= (read-line params nil nil
) while l
109 finally
(appendf *linkkit-start
* (normalize-flags linkset
"modules.o")) do
)
110 (if-let (p (position #\
= l
)))
111 (let ((var (subseq l
0 p
))
112 ;; strip the start and end quote characters
113 (val (subseq l
(+ p
2) (- (length l
) 1)))))
114 (if-let (param (cdr (assoc var
*clisp-toolchain-parameters
* :test
'equal
))))
115 (destructuring-bind (sym &optional normalizep
) param
116 (setf (symbol-value sym
)
117 (if normalizep
(normalize-flags linkset val
) val
))))
119 *ld-exe-flags
* `(,@*cc-flags
* #-
(or sunos darwin
) "-Wl,--export-dynamic")
120 *ld-dll-flags
* (list* #+darwin
"-dynamiclib" ;; -bundle ?
124 ;; TODO: for CMUCL, see whatever uses its linker.sh,
125 ;; and teach it to accept additional objects / libraries
126 ;; as it links a runtime plus a core into an executable
129 (defun ecl-toolchain-parameters ()
131 *cc-flags
* `(,@(parse-command-flags c
::*cc-flags
*)
132 ,@(parse-command-flags c
:*user-cc-flags
*))
133 ;; For the below, we just use c::build-FOO
135 *ld-exe-flags
* *cc-flags
*
136 *ld-dll-flags
* *cc-flags
*
141 (defun mkcl-toolchain-parameters ()
142 (setf *cc
* compiler
::*cc
*
143 *cc-flags
* (parse-command-flags compiler
::*cc-flags
*)
144 ;; For the below, we just use compiler::build-FOO
146 *ld-exe-flags
* *cc-flags
*
147 *ld-dll-flags
* *cc-flags
*
153 (defparameter *sbcl-toolchain-parameters
*
155 ("CFLAGS" *cc-flags
* t
)
156 ("LINKFLAGS" *ld-exe-flags
* t
)
157 ("USE_LIBSBCL" *linkkit-start
* t
)
158 ("LIBS" *linkkit-end
* t
)))
159 (defun sbcl-toolchain-parameters ()
161 (let* ((sbcl-home (lisp-implementation-directory))
162 (sbcl.mk
(subpathname sbcl-home
"sbcl.mk"))))
163 (with-input-file (params sbcl.mk
:if-does-not-exist nil
))
165 (loop for l
= (read-line params nil nil
) while l
166 finally
(appendf *linkkit-end
* '("-lm")) do
)
167 (if-let (p (position #\
= l
)))
168 (let ((var (subseq l
0 p
))
169 (val (subseq l
(1+ p
)))))
170 (if-let (param (cdr (assoc var
*sbcl-toolchain-parameters
* :test
'equal
))))
171 (destructuring-bind (sym &optional normalizep
) param
172 (setf (symbol-value sym
)
173 (if normalizep
(normalize-flags sbcl-home val
) val
))))
175 *ld-dll-flags
* (list* #+darwin
"-dynamiclib" #-darwin
"-shared"
178 ;;; Taken from sb-grovel
179 (defun split-cflags (string)
180 (remove-if (lambda (flag)
181 (zerop (length flag
)))
183 for start
= 0 then
(if end
(1+ end
) nil
)
184 for end
= (and start
(position #\Space string
:start start
))
186 collect
(subseq string start end
))))
188 (defun default-toolchain-parameters ()
189 ;; The values below are legacy guesses from previous versions of CFFI.
190 ;; It would be nice to clean them up, remove unneeded guesses,
191 ;; annotate every guess with some comment explaining the context.
192 ;; TODO: have proper implementation-provided linkkit parameters
193 ;; for all implementations as above, and delete the below altogether.
195 ;; Former *cpu-word-size-flags*
199 (ecase (cffi:foreign-type-size
:pointer
)
204 #+(or cygwin
(not windows
)) "cc"
210 #+darwin
(list "-I" "/opt/local/include/")
211 ;; ECL internal flags
212 #+ecl
(parse-command-flags c
::*cc-flags
*)
213 ;; FreeBSD non-base header files
214 #+freebsd
(list "-I" "/usr/local/include/")
215 (split-cflags (getenv "CFLAGS")))
217 *ld-exe-flags
* `(,@arch-flags
#-
(or sunos darwin
) "-Wl,--export-dynamic")
218 *ld-dll-flags
* (list* #+darwin
"-dynamiclib" ;; -bundle ?
224 (defun ensure-toolchain-parameters ()
225 #+clisp
(unless *cc
* (clisp-toolchain-parameters))
226 #+ecl
(unless *cc
* (ecl-toolchain-parameters))
227 #+mkcl
(unless *cc
* (mkcl-toolchain-parameters))
228 #+sbcl
(unless *cc
* (sbcl-toolchain-parameters))
229 (unless *cc
* (default-toolchain-parameters)))
231 ;; Actually initialize toolchain parameters
232 (ignore-errors (ensure-toolchain-parameters))
235 (defun call-with-temporary-output (output-file fun
)
236 (let ((output-file (ensure-pathname output-file
:want-file t
:ensure-absolute t
:truenamize t
)))
238 (:pathname tmp
:direction
:output
239 :prefix
(strcat (native-namestring (pathname-directory-pathname output-file
))
240 (pathname-name output-file
) "-tmp")
242 :type
(pathname-type output-file
))
244 (rename-file-overwriting-target tmp output-file
))))
246 (defmacro with-temporary-output
((output-file-var &optional
(output-file-val output-file-var
))
248 "Create an output file atomically, by executing the BODY while OUTPUT-FILE-VAR
249 is bound to a temporary file name, then atomically renaming that temporary file to OUTPUT-FILE-VAL."
250 `(call-with-temporary-output ,output-file-val
(lambda (,output-file-var
) ,@body
)))
252 (defun invoke-builder (builder output-file
&rest args
)
253 "Invoke the C Compiler with given OUTPUT-FILE and arguments ARGS"
254 (with-temporary-output (output-file)
255 (apply 'invoke
`(,@builder
,output-file
,@args
))))
257 (defun cc-compile (output-file inputs
&optional cflags
)
258 (apply 'invoke-builder
(list *cc
* "-o") output-file
259 "-c" (append *cc-flags
* cflags
#-windows
'("-fPIC") inputs
)))
261 (defun link-executable (output-file inputs
)
262 (apply 'invoke-builder
(list *ld
* "-o") output-file
263 (append *ld-exe-flags
* inputs
)))
265 (defun link-lisp-executable (output-file inputs
)
268 (format nil
"-Wl,--export-dynamic ~@[ ~A~]"
270 (c::build-program output-file
:lisp-files inputs
))
271 #+mkcl
(compiler::build-program
272 output-file
:lisp-object-files
(mapcar 'program-argument inputs
)
273 :on-missing-lisp-object-initializer nil
)
275 (link-executable output-file
`(,@*linkkit-start
* ,@inputs
,@*linkkit-end
*)))
277 (defun link-static-library (output-file inputs
)
278 #+ecl
(c::build-static-library output-file
:lisp-files inputs
)
279 #+mkcl
(compiler::build-static-library
280 output-file
:lisp-object-files
(mapcar 'program-argument inputs
)
281 :on-missing-lisp-object-initializer nil
)
283 (with-temporary-output (output-file)
284 (delete-file-if-exists output-file
)
285 #+(or bsd linux windows
)
287 `(;; TODO: make it portable to BSD.
288 ;; ar D is also on FreeBSD, but not on OpenBSD or Darwin, dunno about NetBSD;
289 ;; ar T seems to only be on Linux (means something different on Darwin). Sigh.
290 ;; A MRI script might be more portable... not, only supported by GNU binutils.
291 ;; I couldn't get libtool to work, and it's not ubiquitous anyway.
292 ;; ,@`("libtool" "--mode=link" ,*cc* ,@*cc-flags* "-static" "-o" ,output-file)
293 ;; "Solution": never link .a's into further .a's, only link .o's into .a's,
294 ;; which implied changes that are now the case in ASDF 3.2.0.
295 #+darwin
,@`("libtool" "-static" "-o" ,output-file
)
296 #+(:and bsd
(:not darwin
)) ,@`("ar" "rcs" ,output-file
)
297 #+linux
,@`("ar" "rcsDT" ,output-file
)
298 #+windows
,@`("lib" "-nologo" ,(strcat "-out:" (native-namestring output-file
)))
300 #-
(or bsd linux windows
)
301 (error "Not implemented on your system")))
303 (defun link-shared-library (output-file inputs
)
304 ;; remove the library so we won't possibly be overwriting
305 ;; the code of any existing process
306 (delete-file-if-exists output-file
)
307 #+ecl
(c::build-shared-library output-file
:lisp-files inputs
)
308 #+mkcl
(compiler::build-shared-library
309 output-file
:lisp-object-files
(mapcar 'program-argument inputs
)
310 :on-missing-lisp-object-initializer nil
)
312 ;; Don't use a temporary file, because linking is sensitive to the output file name :-/ (or put it in a temporary directory?)
313 (apply 'invoke
*ld
* "-o" output-file
314 (append *ld-dll-flags
* inputs
)))
317 ;;; Computing file names
319 (defun make-c-file-name (output-defaults &optional suffix
)
320 (make-pathname :type
"c"
321 :name
(strcat (pathname-name output-defaults
) suffix
)
322 :defaults output-defaults
))
324 (defun make-o-file-name (output-defaults &optional suffix
)
325 (make-pathname :type
(bundle-pathname-type :object
)
326 :name
(format nil
"~A~@[~A~]" (pathname-name output-defaults
) suffix
)
327 :defaults output-defaults
))
329 (defun make-so-file-name (defaults)
330 (make-pathname :type
(bundle-pathname-type :shared-library
)
333 (defun make-exe-file-name (defaults)
334 (make-pathname :type
(bundle-pathname-type :program
)
338 ;;; Implement link-op on image-based platforms.
339 #-
(or clasp ecl mkcl
)
340 (defmethod perform ((o link-op
) (c system
))
341 (let* ((inputs (input-files o c
))
342 (output (first (output-files o c
)))
343 (kind (bundle-type o
)))
344 (when output
;; some operations skip any output when there is no input
346 (:program
(link-executable output inputs
))
347 ((:lib
:static-library
) (link-static-library output inputs
))
348 ((:dll
:shared-library
) (link-shared-library output inputs
))))))
350 (defclass c-file
(source-file)
351 ((cflags :initarg
:cflags
:initform nil
)
352 (type :initform
"c")))
354 (defmethod output-files ((o compile-op
) (c c-file
))
355 (let* ((i (first (input-files o c
)))
356 (base (format nil
"~(~{~a~^__~}~)"
357 (mapcar (lambda (x) (substitute-if #\_
(complement #'alphanumericp
) x
))
358 (component-find-path c
))))
359 (path (make-pathname :defaults i
:name base
)))
360 (list (make-o-file-name path
)
361 (make-so-file-name path
))))
363 (defmethod perform ((o compile-op
) (c c-file
))
364 (let ((i (first (input-files o c
))))
365 (destructuring-bind (.o .so
) (output-files o c
)
366 (cc-compile .o
(list i
) (slot-value c
'cflags
))
367 (link-shared-library .so
(list .o
)))))
369 (defmethod perform ((o load-op
) (c c-file
))
370 (let ((o (second (input-files o c
))))
371 (cffi:load-foreign-library
(file-namestring o
) :search-path
(list (pathname-directory-pathname o
)))))
373 (setf (find-class 'asdf
::c-file
) (find-class 'c-file
))
375 (defclass o-file
(source-file)
376 ((type :initform
(bundle-pathname-type :object
)))
377 (:documentation
"class for pre-compile object components"))
379 (defmethod output-files ((op compile-op
) (c o-file
))
380 (let* ((o (first (input-files op c
)))
381 (so (apply-output-translations (make-so-file-name o
))))
382 (values (list o so
) t
)))
384 (defmethod perform ((o load-op
) (c o-file
))
385 (let ((so (second (input-files o c
))))
386 (cffi:load-foreign-library
(file-namestring so
) :search-path
(list (pathname-directory-pathname so
)))))
388 (setf (find-class 'asdf
::o-file
) (find-class 'o-file
))