Skip to content
cffi-grovel-support.lisp 10.5 KiB
Newer Older
  (:depends-on
   ("makefile-backend" "static-traversal" "computations" "target-lisp-commands"
    "grain-implementation" "asdf-backend" "dependencies-interpreter")))
(defclass cffi-grovel-specification-grain (lisp-module-grain)
  ((fullname :initarg :fullname)
   (cc-flags :initarg :cc-flags :initform nil)))
(defclass object-grain (file-grain)
  ((parent :initarg :parent)
   (fullname :initarg :fullname)))
(define-handle-extension-form :cffi-grovel (build name &rest keys &key
                                            cc-flags depends-on build-depends-on
                                            compile-depends-on cload-depends-on load-depends-on)
  (declare (ignorable cc-flags depends-on build-depends-on
                      compile-depends-on cload-depends-on load-depends-on))
  (let* ((bname (fullname build))
         (prefix (strcat bname "/"))
         (suffix (cond
                   ((eql #\/ (first-char name))
                    (or (string-strip-prefix prefix name)
                        (error ":cffi-grovel module name ~A not under build name ~A"
                               name bname)))
                   (t
                    name)))
         (fname (strcat prefix suffix))
         (grain
          (apply #'make-instance 'cffi-grovel-specification-grain
                 :fullname `(:cffi-grovel ,fname)
                 :parent build
                 :vp (make-vp :src bname "/" suffix "." "lisp")
                 keys)))
    (setf (registered-grain `(:cffi-grovel ,fname)) grain)
    (with-slots (fullname
                 parent cc-flags
                 build-depends-on compile-depends-on
                 cload-depends-on load-depends-on) grain
      (let* ((lisp
              (apply 'make-instance
               'lisp-file-grain
               :fullname `(:lisp ,fname)
               :vp (make-vp :obj bname "/" suffix "." "grovel-tmp" "." "lisp")
               :parent build
               :computation nil
               :extension-forms nil
               :depends-on (append depends-on '((:build "/cffi")))
               keys))
             (c
              (make-instance
               'object-grain
               :fullname `(:cffi-grovel-c ,fname)
               :vp (make-vp :obj bname "/" suffix "." "c")
               :parent build))
             (exe
              (make-instance
               'object-grain
               :fullname `(:cffi-grovel-exe ,fname)
               :vp (make-vp :obj bname "/" suffix "." "exe")
               :parent build))
             (targets (list lisp c exe))
             (build-depends-on
              (cond
                ((slot-boundp grain 'build-depends-on) build-depends-on)
                ((slot-boundp build 'build-depends-on) (slot-value build 'build-depends-on))
                (t nil)))
             (generator
              (make-instance
               'lisp-generator
               :build build
               :targets targets
               :dependencies
               `(,@(normalize-dependencies build build-depends-on :build-depends-on)
                   (:build "/cffi/grovel")
                   ,@(normalize-dependencies build compile-depends-on :compile-depends-on)
                   ,@(normalize-dependencies build depends-on :depends-on)
                   (:process-cffi-grovel-file ,fname ,@(when cc-flags `(:cc-flags ,cc-flags)))))))
    (loop :for target :in targets :do
      (setf (registered-grain (fullname target)) target)
      (setf (grain-generator target) generator)))))
  (values))

(defgeneric build-command-for-process-cffi-grovel-file (env name &key cc-flags))
(define-build-command-for :process-cffi-grovel-file (env name &key cc-flags)
  (issue-build-command env
    `(:process-cffi-grovel-file ,name ,@(when cc-flags `(:cc-flags ,cc-flags)))))

(defmethod compute-fullname ((grain cffi-grovel-specification-grain))
  nil)

(defmethod asdf-spec (env (grain cffi-grovel-specification-grain))
  `(:cffi-grovel
    ,(asdf-dependency-grovel::strip-extension
      (enough-namestring (grain-namestring env grain))
      "lisp")))

(define-text-for-xcvb-driver-command :process-cffi-grovel-file (env name &key cc-flags)
  (let ((spec-path (effective-namestring env `(:cffi-grovel ,name)))
        (c-path (effective-namestring env `(:cffi-grovel-c ,name)))
        (exe-path (effective-namestring env `(:cffi-grovel-exe ,name)))
        (lisp-path (effective-namestring env `(:lisp ,name))))
    (format nil "(:process-cffi-grovel-file ~S ~S ~S ~S~@[ :cc-flags '~S~])"
            spec-path c-path exe-path lisp-path cc-flags)))
(defmethod validate-fullname ((grain cffi-grovel-specification-grain))
  (ensure-valid-fullname (fullname grain) :type :cffi-grovel))
(define-normalize-dependency :cffi-grovel (grain name)
  (normalize-dependency-lisp* :cffi-grovel grain name))

(dolist (spec
          '((:cffi-grovel . cffi-grovel-specification-grain)
            (:cffi-grovel-exe . object-grain)
            (:cffi-grovel-c . object-grain)
            (:cffi-wrapper . cffi-wrapper-specification-grain)
            (:cffi-wrapper-so . object-grain)
            (:cffi-wrapper-c . object-grain)))
  (pushnew spec +dependency-type+))

(define-linkable-dependency :cffi-grovel (x) `(:fasl ,x))
(define-compiled-dependency :cffi-grovel (x) (list (compile-time-fasl-type) x))

;; From cffi
(defparameter *cffi-feature-suffix-map*
  '((:windows . ".dll")
    (:darwin . ".dylib")
    (:unix . ".so")
    (t . ".so"))
  "Mapping of OS feature keywords to shared library suffixes.")
(defun default-library-suffix ()
  "Return a string to use as default library suffix based on the
operating system.  This is used to implement the :DEFAULT option.
This will need to be extended as we test on more OSes."
  (or (cdr (assoc-if #'target-feature-p *cffi-feature-suffix-map*))
      (error "Unable to determine the default library suffix on this OS.")))


(defclass cffi-wrapper-specification-grain (lisp-module-grain)
  ((fullname :initarg :fullname)
   (cc-flags :initarg :cc-flags :initform nil)
   (soname :initarg :soname :initform nil)))

(define-handle-extension-form :cffi-wrapper (build name &rest keys &key
                                             cc-flags depends-on build-depends-on
                                             compile-depends-on cload-depends-on load-depends-on)
  (declare (ignorable cc-flags depends-on build-depends-on
                      compile-depends-on cload-depends-on load-depends-on))
  (let* ((bname (fullname build))
         (prefix (strcat bname "/"))
         (suffix (cond
                   ((eql #\/ (first-char name))
                    (or (string-strip-prefix prefix name)
                        (error ":cffi-wrapper module name ~A not under build name ~A"
                               name bname)))
                   (t
                    name)))
         (fname (strcat prefix suffix))
         (grain
          (apply #'make-instance 'cffi-wrapper-specification-grain
                 :fullname `(:cffi-wrapper ,fname)
                 :parent build
                 :vp (make-vp :src bname "/" suffix "." "lisp")
                 keys)))
    (setf (registered-grain `(:cffi-wrapper ,fname)) grain)
    (with-slots (fullname
                 parent cc-flags
                 build-depends-on compile-depends-on
                 cload-depends-on load-depends-on) grain
      (let* ((lisp
              (apply 'make-instance
               'lisp-file-grain
               :fullname `(:lisp ,fname)
               :vp (make-vp :obj bname "/" suffix "." "grovel-tmp" "." "lisp")
               :parent build
               :computation nil
               :extension-forms nil
               :depends-on (append depends-on '((:build "/cffi")))
               keys))
             (c
              (make-instance
               'object-grain
               :fullname `(:cffi-wrapper-c ,fname)
               :vp (make-vp :obj bname "/" suffix "." "c")
               :parent build))
             (so
              (make-instance
               'object-grain
               :fullname `(:cffi-wrapper-so ,fname)
               :vp (make-vp :obj bname "/" suffix "."
                            (subseq (default-library-suffix) 1))
               :parent build))
             (targets (list lisp c so))
             (build-depends-on
              (cond
                ((slot-boundp grain 'build-depends-on) build-depends-on)
                ((slot-boundp build 'build-depends-on) (slot-value build 'build-depends-on))
                (t nil)))
             (generator
              (make-instance
               'lisp-generator
               :build build
               :targets targets
               :dependencies
               `(,@(normalize-dependencies build build-depends-on :build-depends-on)
                   (:build "/cffi/grovel")
                   ,@(normalize-dependencies build compile-depends-on :compile-depends-on)
                   ,@(normalize-dependencies build depends-on :depends-on)
                   (:process-cffi-wrapper-file ,fname ,@(when cc-flags `(:cc-flags ,cc-flags)))))))
    (loop :for target :in targets :do
      (setf (registered-grain (fullname target)) target)
      (setf (grain-generator target) generator)))))
  (values))

(defgeneric build-command-for-process-cffi-wrapper-file (env name &key cc-flags))
(define-build-command-for :process-cffi-wrapper-file (env name &key cc-flags)
  (issue-build-command env
    `(:process-cffi-wrapper-file ,name ,@(when cc-flags `(:cc-flags ,cc-flags)))))

(defmethod compute-fullname ((grain cffi-wrapper-specification-grain))
  nil)

(defmethod asdf-spec (env (grain cffi-wrapper-specification-grain))
  `(:cffi-wrapper
    ,(asdf-dependency-grovel::strip-extension
      (enough-namestring (grain-namestring env grain))
      "lisp")))

(define-text-for-xcvb-driver-command :process-cffi-wrapper-file (env name &key cc-flags)
  (let ((spec-path (effective-namestring env `(:cffi-wrapper ,name)))
        (c-path (effective-namestring env `(:cffi-wrapper-c ,name)))
        (so-path (effective-namestring env `(:cffi-wrapper-so ,name)))
        (lisp-path (effective-namestring env `(:lisp ,name))))
    (format nil "(:process-cffi-wrapper-file ~S ~S ~S ~S~@[ :cc-flags '~S~])"
            spec-path c-path so-path lisp-path cc-flags)))

(defmethod validate-fullname ((grain cffi-wrapper-specification-grain))
  (ensure-valid-fullname (fullname grain) :type :cffi-wrapper))
(define-normalize-dependency :cffi-wrapper (grain name)
  (normalize-dependency-lisp* :cffi-wrapper grain name))

(define-linkable-dependency :cffi-wrapper (x) `(:fasl ,x))
(define-compiled-dependency :cffi-wrapper (x) (list (compile-time-fasl-type) x))