c-toolchain.lisp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
  2. ;;;
  3. ;;; c-toolchain.lisp --- Generic support compiling and linking C code.
  4. ;;;
  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>
  9. ;;;
  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:
  17. ;;;
  18. ;;; The above copyright notice and this permission notice shall be
  19. ;;; included in all copies or substantial portions of the Software.
  20. ;;;
  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.
  29. ;;;
  30. (in-package #:cffi-toolchain)
  31. ;;; Utils
  32. (defun parse-command-flags (flags)
  33. (let ((separators '(#\Space #\Tab #\Newline #\Return)))
  34. (remove-if 'emptyp (split-string flags :separator separators))))
  35. (defun parse-command-flags-list (strings)
  36. (loop for flags in strings append (parse-command-flags flags)))
  37. (defun program-argument (x)
  38. (etypecase x
  39. (string x)
  40. (pathname (native-namestring x))))
  41. (defun invoke (command &rest args)
  42. (when (pathnamep command)
  43. (setf command (native-namestring command))
  44. #+os-unix
  45. (unless (absolute-pathname-p command)
  46. (setf command (strcat "./" command))))
  47. (let ((cmd (cons command (mapcar 'program-argument args))))
  48. (safe-format! *debug-io* "; ~A~%" (escape-command cmd))
  49. (run-program cmd :output :interactive :error-output :interactive)))
  50. ;;; C support
  51. (defparameter *cc* nil "C compiler")
  52. (defparameter *cc-flags* nil "flags for the C compiler")
  53. (defparameter *ld* nil "object linker") ;; NB: can actually be the same as *cc*
  54. (defparameter *ld-exe-flags* nil "flags for linking executables via *ld*")
  55. (defparameter *ld-dll-flags* nil "flags for linking shared library via *ld*")
  56. (defparameter *linkkit-start* nil "flags for the implementation linkkit, start")
  57. (defparameter *linkkit-end* nil "flags for the implementation linkkit, end")
  58. (defun link-all-library (lib)
  59. ;; Flags to provide to cc to link a whole library into an executable
  60. (when lib
  61. (if (featurep :darwin) ;; actually, LLVM ld vs GNU ld
  62. `("-Wl,-force_load" ,lib)
  63. `("-Wl,--whole-archive" ,lib "-Wl,--no-whole-archive"))))
  64. (defun normalize-flags (directory flags)
  65. (loop for val in (parse-command-flags flags) collect
  66. (cond
  67. ((find (first-char val) "-+/") val)
  68. ((probe-file* (subpathname directory val)))
  69. (t val))))
  70. (defun implementation-file (file &optional type)
  71. (subpathname (lisp-implementation-directory) file
  72. :type (bundle-pathname-type type)))
  73. ;; TODO: on CCL, extract data from
  74. ;; (pathname (strcat "ccl:lisp-kernel/" (ccl::kernel-build-directory) "/Makefile")) ?
  75. #+clisp
  76. (progn
  77. (defparameter *clisp-toolchain-parameters*
  78. '(("CC" *cc*)
  79. ("CFLAGS" *cc-flags* t)
  80. ("CLFLAGS" *cc-exe-flags* t)
  81. ("LIBS" *linkkit-start* t)
  82. ("X_LIBS" *linkkit-end* t)))
  83. (defun clisp-toolchain-parameters (&optional linkset)
  84. (nest
  85. (let* ((linkset (ensure-pathname
  86. (or linkset "base")
  87. :defaults (lisp-implementation-directory)
  88. :ensure-absolute t
  89. :ensure-directory t
  90. :want-existing t))
  91. (makevars (subpathname linkset "makevars"))))
  92. (with-input-file (params makevars :if-does-not-exist nil))
  93. (when params)
  94. (loop for l = (read-line params nil nil) while l
  95. finally (appendf *linkkit-start* (normalize-flags linkset "modules.o")) do)
  96. (if-let (p (position #\= l)))
  97. (let ((var (subseq l 0 p))
  98. ;; strip the start and end quote characters
  99. (val (subseq l (+ p 2) (- (length l) 1)))))
  100. (if-let (param (cdr (assoc var *clisp-toolchain-parameters* :test 'equal))))
  101. (destructuring-bind (sym &optional normalizep) param
  102. (setf (symbol-value sym)
  103. (if normalizep (normalize-flags linkset val) val))))
  104. (setf *ld* *cc*
  105. *ld-exe-flags* `(,@*cc-flags* #-(or sunos darwin) "-Wl,--export-dynamic")
  106. *ld-dll-flags* (list* #+darwin "-dynamiclib" ;; -bundle ?
  107. #-darwin "-shared"
  108. *cc-flags*))))
  109. ;; TODO: for CMUCL, see whatever uses its linker.sh,
  110. ;; and teach it to accept additional objects / libraries
  111. ;; as it links a runtime plus a core into an executable
  112. #+ecl
  113. (defun ecl-toolchain-parameters ()
  114. (setf *cc* c:*cc*
  115. *cc-flags* `(,@(parse-command-flags c::*cc-flags*)
  116. ,@(parse-command-flags c:*user-cc-flags*))
  117. ;; For the below, we just use c::build-FOO
  118. *ld* *cc*
  119. *ld-exe-flags* *cc-flags*
  120. *ld-dll-flags* *cc-flags*
  121. *linkkit-start* nil
  122. *linkkit-end* nil))
  123. #+mkcl
  124. (defun mkcl-toolchain-parameters ()
  125. (setf *cc* compiler::*cc*
  126. *cc-flags* (parse-command-flags compiler::*cc-flags*)
  127. ;; For the below, we just use compiler::build-FOO
  128. *ld* *cc*
  129. *ld-exe-flags* *cc-flags*
  130. *ld-dll-flags* *cc-flags*
  131. *linkkit-start* nil
  132. *linkkit-end* nil))
  133. #+sbcl
  134. (progn
  135. (defparameter *sbcl-toolchain-parameters*
  136. '(("CC" *cc*)
  137. ("CFLAGS" *cc-flags* t)
  138. ("LINKFLAGS" *ld-exe-flags* t)
  139. ("USE_LIBSBCL" *linkkit-start* t)
  140. ("LIBS" *linkkit-end* t)))
  141. (defun sbcl-toolchain-parameters ()
  142. (nest
  143. (let* ((sbcl-home (lisp-implementation-directory))
  144. (sbcl.mk (subpathname sbcl-home "sbcl.mk"))))
  145. (with-input-file (params sbcl.mk :if-does-not-exist nil))
  146. (when params)
  147. (loop for l = (read-line params nil nil) while l
  148. finally (appendf *linkkit-end* '("-lm")) do)
  149. (if-let (p (position #\= l)))
  150. (let ((var (subseq l 0 p))
  151. (val (subseq l (1+ p)))))
  152. (if-let (param (cdr (assoc var *sbcl-toolchain-parameters* :test 'equal))))
  153. (destructuring-bind (sym &optional normalizep) param
  154. (setf (symbol-value sym)
  155. (if normalizep (normalize-flags sbcl-home val) val))))
  156. (setf *ld* *cc* ;; !
  157. *ld-dll-flags* (list* #+darwin "-dynamiclib" #-darwin "-shared"
  158. *cc-flags*))))
  159. ;;; Taken from sb-grovel
  160. (defun split-cflags (string)
  161. (remove-if (lambda (flag)
  162. (zerop (length flag)))
  163. (loop
  164. for start = 0 then (if end (1+ end) nil)
  165. for end = (and start (position #\Space string :start start))
  166. while start
  167. collect (subseq string start end))))
  168. (defun default-toolchain-parameters ()
  169. ;; The values below are legacy guesses from previous versions of CFFI.
  170. ;; It would be nice to clean them up, remove unneeded guesses,
  171. ;; annotate every guess with some comment explaining the context.
  172. ;; TODO: have proper implementation-provided linkkit parameters
  173. ;; for all implementations as above, and delete the below altogether.
  174. (let ((arch-flags
  175. ;; Former *cpu-word-size-flags*
  176. #+arm '("-marm")
  177. #+arm64 '()
  178. #-(or arm arm64)
  179. (ecase (cffi:foreign-type-size :pointer)
  180. (4 '("-m32"))
  181. (8 '("-m64")))))
  182. (setf *cc*
  183. (or (getenvp "CC")
  184. #+(or cygwin (not windows)) "cc"
  185. "gcc")
  186. *cc-flags*
  187. (append
  188. arch-flags
  189. ;; For MacPorts
  190. #+darwin (list "-I" "/opt/local/include/")
  191. ;; ECL internal flags
  192. #+ecl (parse-command-flags c::*cc-flags*)
  193. ;; FreeBSD non-base header files
  194. #+freebsd (list "-I" "/usr/local/include/")
  195. (split-cflags (getenv "CFLAGS")))
  196. *ld* *cc*
  197. *ld-exe-flags* `(,@arch-flags #-(or sunos darwin) "-Wl,--export-dynamic")
  198. *ld-dll-flags* (list* #+darwin "-dynamiclib" ;; -bundle ?
  199. #-darwin "-shared"
  200. *cc-flags*)
  201. *linkkit-start* nil
  202. *linkkit-end* nil)))
  203. (defun ensure-toolchain-parameters ()
  204. #+clisp (unless *cc* (clisp-toolchain-parameters))
  205. #+ecl (unless *cc* (ecl-toolchain-parameters))
  206. #+mkcl (unless *cc* (mkcl-toolchain-parameters))
  207. #+sbcl (unless *cc* (sbcl-toolchain-parameters))
  208. (unless *cc* (default-toolchain-parameters)))
  209. ;; Actually initialize toolchain parameters
  210. (ignore-errors (ensure-toolchain-parameters))
  211. (defun call-with-temporary-output (output-file fun)
  212. (let ((output-file (ensure-pathname output-file :want-file t :ensure-absolute t :truenamize t)))
  213. (with-temporary-file
  214. (:pathname tmp :direction :output
  215. :prefix (strcat (native-namestring (pathname-directory-pathname output-file))
  216. (pathname-name output-file) "-tmp")
  217. :suffix ""
  218. :type (pathname-type output-file))
  219. (funcall fun tmp)
  220. (rename-file-overwriting-target tmp output-file))))
  221. (defmacro with-temporary-output ((output-file-var &optional (output-file-val output-file-var))
  222. &body body)
  223. "Create an output file atomically, by executing the BODY while OUTPUT-FILE-VAR
  224. is bound to a temporary file name, then atomically renaming that temporary file to OUTPUT-FILE-VAL."
  225. `(call-with-temporary-output ,output-file-val (lambda (,output-file-var) ,@body)))
  226. (defun invoke-builder (builder output-file &rest args)
  227. "Invoke the C Compiler with given OUTPUT-FILE and arguments ARGS"
  228. (with-temporary-output (output-file)
  229. (apply 'invoke `(,@builder ,output-file ,@args))))
  230. (defun cc-compile (output-file inputs &optional cflags)
  231. (apply 'invoke-builder (list *cc* "-o") output-file
  232. "-c" (append *cc-flags* cflags #-windows '("-fPIC") inputs)))
  233. (defun link-executable (output-file inputs)
  234. (apply 'invoke-builder (list *ld* "-o") output-file
  235. (append *ld-exe-flags* inputs)))
  236. (defun link-lisp-executable (output-file inputs)
  237. #+ecl
  238. (let ((c::*ld-flags*
  239. (format nil "-Wl,--export-dynamic ~@[ ~A~]"
  240. c::*ld-flags*)))
  241. (c::build-program output-file :lisp-files inputs))
  242. #+mkcl (compiler::build-program
  243. output-file :lisp-object-files (mapcar 'program-argument inputs)
  244. :on-missing-lisp-object-initializer nil)
  245. #-(or ecl mkcl)
  246. (link-executable output-file `(,@*linkkit-start* ,@inputs ,@*linkkit-end*)))
  247. (defun link-static-library (output-file inputs)
  248. #+ecl (c::build-static-library output-file :lisp-files inputs)
  249. #+mkcl (compiler::build-static-library
  250. output-file :lisp-object-files (mapcar 'program-argument inputs)
  251. :on-missing-lisp-object-initializer nil)
  252. #-(or ecl mkcl)
  253. (with-temporary-output (output-file)
  254. (delete-file-if-exists output-file)
  255. #+(or bsd linux windows)
  256. (apply 'invoke
  257. `(;; TODO: make it portable to BSD.
  258. ;; ar D is also on FreeBSD, but not on OpenBSD or Darwin, dunno about NetBSD;
  259. ;; ar T seems to only be on Linux (means something different on Darwin). Sigh.
  260. ;; A MRI script might be more portable... not, only supported by GNU binutils.
  261. ;; I couldn't get libtool to work, and it's not ubiquitous anyway.
  262. ;; ,@`("libtool" "--mode=link" ,*cc* ,@*cc-flags* "-static" "-o" ,output-file)
  263. ;; "Solution": never link .a's into further .a's, only link .o's into .a's,
  264. ;; which implied changes that are now the case in ASDF 3.2.0.
  265. #+darwin ,@`("libtool" "-static" "-o" ,output-file)
  266. #+(:and bsd (:not darwin)) ,@`("ar" "rcs" ,output-file)
  267. #+linux ,@`("ar" "rcsDT" ,output-file)
  268. #+windows ,@`("lib" "-nologo" ,(strcat "-out:" (native-namestring output-file)))
  269. ,@inputs))
  270. #-(or bsd linux windows)
  271. (error "Not implemented on your system")))
  272. (defun link-shared-library (output-file inputs)
  273. ;; remove the library so we won't possibly be overwriting
  274. ;; the code of any existing process
  275. (delete-file-if-exists output-file)
  276. #+ecl (c::build-shared-library output-file :lisp-files inputs)
  277. #+mkcl (compiler::build-shared-library
  278. output-file :lisp-object-files (mapcar 'program-argument inputs)
  279. :on-missing-lisp-object-initializer nil)
  280. #-(or ecl mkcl)
  281. ;; Don't use a temporary file, because linking is sensitive to the output file name :-/ (or put it in a temporary directory?)
  282. (apply 'invoke *ld* "-o" output-file
  283. (append *ld-dll-flags* inputs)))
  284. ;;; Computing file names
  285. (defun make-c-file-name (output-defaults &optional suffix)
  286. (make-pathname :type "c"
  287. :name (strcat (pathname-name output-defaults) suffix)
  288. :defaults output-defaults))
  289. (defun make-o-file-name (output-defaults &optional suffix)
  290. (make-pathname :type (bundle-pathname-type :object)
  291. :name (format nil "~A~@[~A~]" (pathname-name output-defaults) suffix)
  292. :defaults output-defaults))
  293. (defun make-so-file-name (defaults)
  294. (make-pathname :type (bundle-pathname-type :shared-library)
  295. :defaults defaults))
  296. (defun make-exe-file-name (defaults)
  297. (make-pathname :type (bundle-pathname-type :program)
  298. :defaults defaults))
  299. ;;; Implement link-op on image-based platforms.
  300. #-(or clasp ecl mkcl)
  301. (defmethod perform ((o link-op) (c system))
  302. (let* ((inputs (input-files o c))
  303. (output (first (output-files o c)))
  304. (kind (bundle-type o)))
  305. (when output ;; some operations skip any output when there is no input
  306. (ecase kind
  307. (:program (link-executable output inputs))
  308. ((:lib :static-library) (link-static-library output inputs))
  309. ((:dll :shared-library) (link-shared-library output inputs))))))
  310. (defclass c-file (source-file)
  311. ((cflags :initarg :cflags :initform nil)
  312. (type :initform "c")))
  313. (defmethod output-files ((o compile-op) (c c-file))
  314. (let* ((i (first (input-files o c)))
  315. (base (format nil "~(~{~a~^__~}~)"
  316. (mapcar (lambda (x) (substitute-if #\_ (complement #'alphanumericp) x))
  317. (component-find-path c))))
  318. (path (make-pathname :defaults i :name base)))
  319. (list (make-o-file-name path)
  320. (make-so-file-name path))))
  321. (defmethod perform ((o compile-op) (c c-file))
  322. (let ((i (first (input-files o c))))
  323. (destructuring-bind (.o .so) (output-files o c)
  324. (cc-compile .o (list i) (slot-value c 'cflags))
  325. (link-shared-library .so (list .o)))))
  326. (defmethod perform ((o load-op) (c c-file))
  327. (let ((o (second (input-files o c))))
  328. (cffi:load-foreign-library (file-namestring o) :search-path (list (pathname-directory-pathname o)))))
  329. (setf (find-class 'asdf::c-file) (find-class 'c-file))
  330. (defclass o-file (source-file)
  331. ((type :initform (bundle-pathname-type :object)))
  332. (:documentation "class for pre-compile object components"))
  333. (defmethod output-files ((op compile-op) (c o-file))
  334. (let* ((o (first (input-files op c)))
  335. (so (apply-output-translations (make-so-file-name o))))
  336. (values (list o so) t)))
  337. (defmethod perform ((o load-op) (c o-file))
  338. (let ((so (second (input-files o c))))
  339. (cffi:load-foreign-library (file-namestring so) :search-path (list (pathname-directory-pathname so)))))
  340. (setf (find-class 'asdf::o-file) (find-class 'o-file))