From 4a5572dd30fceacc5a1fbac6ae8d942757903753 Mon Sep 17 00:00:00 2001
From: Mark Cox <markcox80@gmail.com>
Date: Sat, 10 Nov 2012 18:11:17 +1000
Subject: [PATCH] CFFI-GROVEL now uses ASDF operations to compile and load
 processed grovel and wrapper files.

---
 grovel/asdf.lisp | 118 +++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 79 insertions(+), 39 deletions(-)

diff --git a/grovel/asdf.lisp b/grovel/asdf.lisp
index a02ff4c..25fb1d9 100644
--- a/grovel/asdf.lisp
+++ b/grovel/asdf.lisp
@@ -39,60 +39,100 @@
   ((cc-flags :initform nil :accessor cc-flags-of :initarg :cc-flags)))
 
 (defmethod asdf:perform :around ((op asdf:compile-op) (file cc-flags-mixin))
+  (declare (ignore op))
   (let ((*cc-flags* (append (ensure-list (cc-flags-of file))
                             *cc-flags*)))
     (call-next-method)))
 
+(eval-when (:compile-toplevel :load-toplevel :execute) 
+  (defclass process-op (asdf:operation)
+    ()
+    (:documentation "This ASDF operation performs the steps necessary
+  to generate a compilable and loadable lisp file from a
+  PROCESS-OP-INPUT component."))
+
+  (defclass process-op-input (asdf:cl-source-file)
+    ((generated-lisp-file-type
+      :initarg :generated-lisp-file-type
+      :accessor generated-lisp-file-type
+      :documentation "The :TYPE argument to use for the generated lisp file."))
+    (:default-initargs
+     :generated-lisp-file-type "generated-lisp-file")
+    (:documentation "This ASDF component represents a file that is
+    used as input to a function that generates lisp source file. This
+    component acts as if it is a CL-SOURCE-FILE by applying the
+    COMPILE-OP and LOAD-SOURCE-OP operations to the file generated by
+    PROCESS-OP.")))
+
+(defmethod asdf:output-files ((op process-op) (c process-op-input))
+  (declare (ignore op))
+  (list (make-pathname :type (generated-lisp-file-type c)
+                       :defaults (asdf:component-pathname c))))
+
+(defmethod asdf:component-depends-on ((op asdf:compile-op) (c process-op-input))
+  (declare (ignore op))
+  (append (call-next-method)
+          (list (list 'process-op (asdf:component-name c)))))
+
+(defmethod asdf:component-depends-on ((op asdf:load-source-op) (c process-op-input))
+  (declare (ignore op))
+  (append (call-next-method)
+          (list (list 'process-op (asdf:component-name c)))))
+
+(defmethod asdf:perform ((op asdf:compile-op) (c process-op-input))
+  (let ((generated-lisp-file (asdf:output-file (make-instance 'process-op) c)))
+    (asdf:perform op (make-instance 'asdf:cl-source-file
+                                    :name (asdf:component-name c)
+                                    :parent (asdf:component-parent c)
+                                    :pathname generated-lisp-file))))
+
+(defmethod asdf:perform ((op asdf:load-source-op) (c process-op-input))
+  (let ((generated-lisp-file (asdf:output-file (make-instance 'process-op) c)))
+    (asdf:perform op (make-instance 'asdf:cl-source-file
+                                    :name (asdf:component-name c)
+                                    :parent (asdf:component-parent c)
+                                    :pathname generated-lisp-file))))
+
 ;;;# ASDF component: GROVEL-FILE
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
-  (defclass grovel-file (asdf:cl-source-file cc-flags-mixin)
+  (defclass grovel-file (process-op-input cc-flags-mixin)
     ()
+    (:default-initargs
+     :generated-lisp-file-type "processed-grovel-file")
     (:documentation
-      "This ASDF component defines COMPILE-OP and LOAD-SOURCE-OP
-operations that take care of calling PROCESS-GROVEL-FILE in order
-to generate a Lisp file that is subsequently compiled and/or
-loaded.")))
-
-(defmethod asdf:perform ((op asdf:compile-op) (c grovel-file))
-  (let ((output-file (ensure-pathname (car (asdf:output-files op c)))))
-    (compile-file (process-grovel-file (asdf:component-pathname c) output-file)
-                  :output-file output-file
-                  #+ecl :system-p #+ecl t)))
-
-(defmethod asdf:perform ((op asdf:load-source-op) (c grovel-file))
-  (load (process-grovel-file
-         (asdf:component-pathname c)
-         (ensure-pathname (car (asdf:output-files op c))))))
+     "This ASDF component represents an input file that is processed
+     by PROCESS-GROVEL-FILE.")))
+
+(defmethod asdf:perform ((op process-op) (c grovel-file))
+  (let* ((output-file (asdf:output-file op c))
+         (input-file  (asdf:component-pathname c))
+         (tmp-file    (process-grovel-file input-file (ensure-pathname output-file))))
+    (unwind-protect
+         (alexandria:copy-file tmp-file output-file :if-to-exists :supersede)
+      (delete-file tmp-file))))
 
 ;;;# ASDF component: WRAPPER-FILE
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
-  (defclass wrapper-file (asdf:cl-source-file cc-flags-mixin)
+  (defclass wrapper-file (process-op-input cc-flags-mixin)
     ((soname :initform nil :initarg :soname :accessor soname-of))
+    (:default-initargs
+     :generated-lisp-file-type "processed-wrapper-file")
     (:documentation
-      "This ASDF component defines COMPILE-OP and LOAD-SOURCE-OP
-operations that take care of calling PROCESS-WRAPPER-FILE in
-order to generate a foreign library and matching CFFI bindings
-that are subsequently compiled and/or loaded.")))
-
-(defun %perform-process-wrapper-file (op c)
-  (let ((fasl-file (ensure-pathname (car (asdf:output-files op c)))))
-    (values (process-wrapper-file (asdf:component-pathname c)
-                                  fasl-file
-                                  (or (soname-of c)
-                                      (asdf:component-name c)))
-            fasl-file)))
-
-(defmethod asdf:perform ((op asdf:compile-op) (c wrapper-file))
-  (multiple-value-bind (generated-source-file fasl-file)
-      (%perform-process-wrapper-file op c)
-    (compile-file generated-source-file
-                  :output-file fasl-file
-                  #+ecl :system-p #+ecl t)))
-
-(defmethod asdf:perform ((op asdf:load-source-op) (c wrapper-file))
-  (load (%perform-process-wrapper-file op c)))
+      "This ASDF component represents an input file that is processed
+      by PROCESS-WRAPPER-FILE. This generates a foreign library and
+      matching CFFI bindings that are subsequently compiled and
+      loaded.")))
+
+(defmethod asdf:perform ((op process-op) (c wrapper-file))
+  (let* ((output-file (asdf:output-file op c))
+         (input-file  (asdf:component-pathname c))
+         (tmp-file    (process-wrapper-file input-file output-file (or (soname-of c)
+                                                                       (asdf:component-name c)))))
+    (unwind-protect
+         (alexandria:copy-file tmp-file output-file :if-to-exists :supersede)
+      (delete-file tmp-file))))
 
 ;; Allow for naked :grovel-file and :wrapper-file in asdf definitions.
 (eval-when (:compile-toplevel :load-toplevel :execute)
-- 
1.8.0

