libffi: clean up the ABI enum
[cffi.git] / src / c2ffi / asdf.lisp
blob78919780055eff4ca3c0e303ac2d45a3dd5e93c9
1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
2 ;;;
3 ;;; asdf.lisp --- ASDF components for cffi/c2ffi.
4 ;;;
5 ;;; Copyright (C) 2015, Attila Lendvai <attila@lendvai.name>
6 ;;;
7 ;;; Permission is hereby granted, free of charge, to any person
8 ;;; obtaining a copy of this software and associated documentation
9 ;;; files (the "Software"), to deal in the Software without
10 ;;; restriction, including without limitation the rights to use, copy,
11 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
12 ;;; of the Software, and to permit persons to whom the Software is
13 ;;; furnished to do so, subject to the following conditions:
14 ;;;
15 ;;; The above copyright notice and this permission notice shall be
16 ;;; included in all copies or substantial portions of the Software.
17 ;;;
18 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 ;;; DEALINGS IN THE SOFTWARE.
26 ;;;
28 (in-package #:cffi/c2ffi)
30 (defclass c2ffi-file (cl-source-file)
31 ((package :initarg :package
32 :initform nil
33 :accessor c2ffi-file/package)
34 (c2ffi-executable :initarg :c2ffi-executable
35 :accessor c2ffi-file/c2ffi-executable)
36 (trace-c2ffi :initarg :trace-c2ffi
37 :accessor c2ffi-file/trace-c2ffi)
38 (prelude :initform nil
39 :initarg :prelude
40 :accessor c2ffi-file/prelude)
41 (sys-include-paths :initarg :sys-include-paths
42 :initform nil
43 :accessor c2ffi-file/sys-include-paths)
44 (exclude-archs :initarg :exclude-archs
45 :initform nil
46 :accessor c2ffi-file/exclude-archs)
47 ;; The following slots correspond to an arg of the same name for
48 ;; the generator function. No accessors are needed, they just hold
49 ;; the data until it gets delegated to the generator function using
50 ;; SLOT-VALUE and a LOOP.
51 (ffi-name-transformer :initarg :ffi-name-transformer
52 :initform 'default-ffi-name-transformer)
53 (ffi-name-export-predicate :initarg :ffi-name-export-predicate
54 :initform 'default-ffi-name-export-predicate)
55 (ffi-type-transformer :initarg :ffi-type-transformer
56 :initform 'default-ffi-type-transformer)
57 (callback-factory :initarg :callback-factory
58 :initform 'default-callback-factory)
59 (foreign-library-name :initarg :foreign-library-name
60 :initform nil)
61 (foreign-library-spec :initarg :foreign-library-spec
62 :initform nil)
63 (emit-generated-name-mappings :initarg :emit-generated-name-mappings
64 :initform :t)
65 (include-sources :initarg :include-sources
66 :initform :all)
67 (exclude-sources :initarg :exclude-sources
68 :initform nil)
69 (include-definitions :initarg :include-definitions
70 :initform :all)
71 (exclude-definitions :initarg :exclude-definitions
72 :initform nil))
73 (:default-initargs
74 :type nil)
75 (:documentation
76 "The input of this ASDF component is a C header file and the configuration for
77 the binding generation process. This header file will define the initial scope of
78 the generation process, which can be further filtered by other configuration
79 parameters.
81 A clang/llvm based external tool called 'c2ffi' is used to process this header
82 file and generate a json spec file for each supported architecture triplet. Normally
83 these .spec files are only (re)generated by the author of the lib and are checked into
84 the corresponding source repository. It needs to be done manually by invoking the
85 following command:
87 (cffi/c2ffi:generate-spec :your-system)
89 which is a shorthand for:
91 (asdf:operate 'cffi/c2ffi::generate-spec-op :your-system)
93 The generation of the underlying platform's json file must succeed, but the
94 generation for the other arch's is allowed to fail
95 \(see ENSURE-SPEC-FILE-IS-UP-TO-DATE for details).
97 During the normal build process the json file is used as the input to generate
98 a lisp file containing the CFFI definitions (see PROCESS-C2FFI-SPEC-FILE).
99 This file will be placed next to the .spec file, and will be compiled as any
100 other lisp file. This process requires loading the ASDF system called
101 \"cffi/c2ffi-generator\" that has more dependencies than CFFI itself. If you
102 want to avoid those extra dependencies in your project, then you can check in
103 these generated lisp files into your source repository, but keep in mind that
104 you'll need to manually force their regeneration if CFFI/C2FFI itself gets
105 updated (by e.g. deleting them from the filesystem) ."))
107 (defun input-file (operation component)
108 (let ((files (input-files operation component)))
109 (assert (length=n-p files 1))
110 (first files)))
112 (defclass generate-spec-op (selfward-operation)
113 ((selfward-operation :initform 'prepare-op)))
115 (defun generate-spec (system &key force)
116 (asdf:operate 'generate-spec-op system :force force))
118 (defmethod input-files ((op generate-spec-op) (c c2ffi-file))
119 (list (component-pathname c)))
121 (defmethod output-files ((op generate-spec-op) (c c2ffi-file))
122 (let* ((input-file (input-file op c))
123 (spec-file (spec-path input-file)))
124 (values
125 (list spec-file)
126 ;; Tell ASDF not to apply output translation.
127 t)))
129 (defmethod perform ((op generate-spec-op) (c asdf:component))
130 (values))
132 (defmethod perform ((op generate-spec-op) (c c2ffi-file))
133 (let ((input-file (input-file op c))
134 (*c2ffi-executable* (if (slot-boundp c 'c2ffi-executable)
135 (c2ffi-file/c2ffi-executable c)
136 *c2ffi-executable*))
137 (*trace-c2ffi* (if (slot-boundp c 'trace-c2ffi)
138 (c2ffi-file/trace-c2ffi c)
139 *trace-c2ffi*)))
140 ;; NOTE: we don't call OUTPUT-FILE here, which may be a violation
141 ;; of the ASDF contract, that promises that OUTPUT-FILE can be
142 ;; customized by users.
143 (ensure-spec-file-is-up-to-date
144 input-file
145 :exclude-archs (c2ffi-file/exclude-archs c)
146 :sys-include-paths (c2ffi-file/sys-include-paths c))))
148 (defclass generate-lisp-op (selfward-operation)
149 ((selfward-operation :initform '()))) ; we will specify it in our own COMPONENT-DEPENDS-ON
151 (defmethod component-depends-on ((op generate-lisp-op) (c c2ffi-file))
152 `((load-op ,(find-system "cffi/c2ffi-generator"))
153 ;; Regenerating the spec file is a lot of headache, so we ignore
154 ;; the file modification times, and only communicate the
155 ;; dependency to ASDF if the spec file is missing.
156 ,@(let ((spec-file (input-file op c))) ; TODO is it legal to call ASDF:INPUT-FILES here?
157 (when (or (not (probe-file spec-file))
158 (zerop (with-input-from-file (s spec-file)
159 (file-length s))))
160 `((generate-spec-op ,c))))
161 ,@(call-next-method)))
163 (defmethod component-depends-on ((op compile-op) (c c2ffi-file))
164 `((generate-lisp-op ,c) ,@(call-next-method)))
166 (defmethod component-depends-on ((op load-source-op) (c c2ffi-file))
167 `((generate-lisp-op ,c) ,@(call-next-method)))
169 (defmethod input-files ((op generate-lisp-op) (c c2ffi-file))
170 (list (output-file 'generate-spec-op c)))
172 (defmethod input-files ((op compile-op) (c c2ffi-file))
173 (list (output-file 'generate-lisp-op c)))
175 (defmethod output-files ((op generate-lisp-op) (c c2ffi-file))
176 (let* ((spec-file (input-file op c))
177 (generated-lisp-file (make-pathname :type "lisp"
178 :defaults spec-file)))
179 (values
180 (list generated-lisp-file)
181 ;; Tell ASDF not to apply output translation.
182 t)))
184 (defmethod perform ((op generate-lisp-op) (c c2ffi-file))
185 (let ((spec-file (input-file op c))
186 (generated-lisp-file (output-file op c)))
187 (with-staging-pathname (tmp-output generated-lisp-file)
188 (format *debug-io* "~&; CFFI/C2FFI is generating the file ~S~%" generated-lisp-file)
189 (apply 'process-c2ffi-spec-file
190 spec-file (c2ffi-file/package c)
191 :output tmp-output
192 :output-encoding (asdf:component-encoding c)
193 :prelude (let ((prelude (c2ffi-file/prelude c)))
194 (if (and (pathnamep prelude)
195 (not (absolute-pathname-p prelude)))
196 (merge-pathnames* prelude (component-pathname c))
197 prelude))
198 ;; The following slots and keyword args have the same name in the ASDF
199 ;; component and in PROCESS-C2FFI-SPEC-FILE, and this loop copies them.
200 (loop
201 :for arg :in '(ffi-name-transformer
202 ffi-name-export-predicate
203 ffi-type-transformer
204 callback-factory
205 foreign-library-name
206 foreign-library-spec
207 emit-generated-name-mappings
208 include-sources
209 exclude-sources
210 include-definitions
211 exclude-definitions)
212 :append (list (make-keyword arg)
213 (slot-value c arg)))))))
215 ;; Allow for naked :cffi/c2ffi-file in asdf definitions.
216 (setf (find-class 'asdf::cffi/c2ffi-file) (find-class 'c2ffi-file))