1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; asdf.lisp --- ASDF components for cffi/c2ffi.
5 ;;; Copyright (C) 2015, Attila Lendvai <attila@lendvai.name>
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:
15 ;;; The above copyright notice and this permission notice shall be
16 ;;; included in all copies or substantial portions of the Software.
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.
28 (in-package #:cffi
/c2ffi
)
30 (defclass c2ffi-file
(cl-source-file)
31 ((package :initarg
:package
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
40 :accessor c2ffi-file
/prelude
)
41 (sys-include-paths :initarg
:sys-include-paths
43 :accessor c2ffi-file
/sys-include-paths
)
44 (exclude-archs :initarg
:exclude-archs
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
61 (foreign-library-spec :initarg
:foreign-library-spec
63 (emit-generated-name-mappings :initarg
:emit-generated-name-mappings
65 (include-sources :initarg
:include-sources
67 (exclude-sources :initarg
:exclude-sources
69 (include-definitions :initarg
:include-definitions
71 (exclude-definitions :initarg
:exclude-definitions
76 "The input of this ASDF component is a C header file, plus configuration for the
77 binding generation process. This header file will define the initial scope of
78 the generation process, which can further be filtered by other configuration
81 An external program based on clang/llvm called 'c2ffi' is then invoked with
82 this header file to generate a json file for various platforms. The generation
83 of the underlying platform's json file must succeed to continue, but the
84 generation for the other platforms is allowed to fail
85 \(see ENSURE-SPEC-FILE-EXISTS).
87 It's advisable to deliver these json files with the project, so that users
88 don't need to have c2ffi installed.
90 Once/if the json file is available for the underlying platform, then the json
91 file is used to generate a lisp file with CFFI definitions (see
92 PROCESS-C2FFI-SPEC-FILE). This file will then be compiled as any other lisp
93 file, except that it's will be stored in the fasl cache."))
95 (defun input-file (operation component
)
96 (let ((files (input-files operation component
)))
97 (assert (length=n-p files
1))
100 (defclass generate-spec-op
(downward-operation)
103 (defmethod input-files ((op generate-spec-op
) (c c2ffi-file
))
104 (list (component-pathname c
)))
106 (defmethod component-depends-on ((op generate-spec-op
) (c c2ffi-file
))
107 `((prepare-op ,c
) ,@(call-next-method)))
109 (defmethod output-files ((op generate-spec-op
) (c c2ffi-file
))
110 (let* ((input-file (input-file op c
))
111 (spec-file (spec-path input-file
)))
114 ;; Tell ASDF not to apply output translation.
117 (defmethod perform ((op generate-spec-op
) (c c2ffi-file
))
118 (let ((input-file (input-file op c
))
119 (*c2ffi-executable
* (if (slot-boundp c
'c2ffi-executable
)
120 (c2ffi-file/c2ffi-executable c
)
122 (*trace-c2ffi
* (if (slot-boundp c
'trace-c2ffi
)
123 (c2ffi-file/trace-c2ffi c
)
125 ;; NOTE: we don't call OUTPUT-FILE here, which may be a violation
126 ;; of the ASDF contract, that promises that OUTPUT-FILE can be
127 ;; customized by users.
128 (ensure-spec-file-exists input-file
129 :exclude-archs
(c2ffi-file/exclude-archs c
)
130 :sys-include-paths
(c2ffi-file/sys-include-paths c
))))
132 (defclass generate-lisp-op
(downward-operation)
135 (defmethod component-depends-on ((op generate-lisp-op
) (c c2ffi-file
))
136 `((generate-spec-op ,c
) ,@(call-next-method)))
138 (defmethod component-depends-on ((op compile-op
) (c c2ffi-file
))
139 `((generate-lisp-op ,c
) ,@(call-next-method)))
141 (defmethod component-depends-on ((op load-source-op
) (c c2ffi-file
))
142 `((generate-lisp-op ,c
) ,@(call-next-method)))
144 (defmethod input-files ((op generate-lisp-op
) (c c2ffi-file
))
145 (list (output-file 'generate-spec-op c
)))
147 (defmethod input-files ((op compile-op
) (c c2ffi-file
))
148 (list (output-file 'generate-lisp-op c
)))
150 (defmethod output-files ((op generate-lisp-op
) (c c2ffi-file
))
151 (let* ((spec-file (input-file op c
))
152 (generated-lisp-file (make-pathname :type
"lisp"
153 :defaults spec-file
)))
155 (list generated-lisp-file
)
156 ;; Tell ASDF not to apply output translation.
159 (defmethod perform ((op generate-lisp-op
) (c c2ffi-file
))
160 (let ((spec-file (input-file op c
))
161 (generated-lisp-file (output-file op c
)))
162 (with-staging-pathname (tmp-output generated-lisp-file
)
163 (format *debug-io
* "~&; CFFI/C2FFI is generating the file ~S~%" generated-lisp-file
)
164 (unless (component-loaded-p :cffi
/c2ffi-generator
)
165 (load-system :cffi
/c2ffi-generator
))
166 (apply 'process-c2ffi-spec-file
167 spec-file
(c2ffi-file/package c
)
169 :output-encoding
(asdf:component-encoding c
)
170 :prelude
(let ((prelude (c2ffi-file/prelude c
)))
171 (if (and (pathnamep prelude
)
172 (not (absolute-pathname-p prelude
)))
173 (merge-pathnames* prelude
(component-pathname c
))
175 ;; The following slots and keyword args have the same name in the ASDF
176 ;; component and in PROCESS-C2FFI-SPEC-FILE, and this loop copies them.
178 :for arg
:in
'(ffi-name-transformer
179 ffi-name-export-predicate
184 emit-generated-name-mappings
189 :append
(list (make-keyword arg
)
190 (slot-value c arg
)))))))
192 ;; Allow for naked :cffi/c2ffi-file in asdf definitions.
193 (setf (find-class 'asdf
::cffi
/c2ffi-file
) (find-class 'c2ffi-file
))