PING.

Here's an updated version of my patch, with fixes for monolithic-lib-op.

Should I create a pull request on github instead?

Note that the static linking magic works on Windows and Linux but not BSD
(but that's no regression).

—♯ƒ • François-René ÐVB Rideau •Reflection&Cybernethics• http://fare.tunes.org
Paradoxes only exist in language, not reality. — Eric S. Raymond


On Wed, Sep 16, 2015 at 11:29 PM, Faré <fah...@gmail.com> wrote:
> Here is a patch that will make CFFI play better with ASDF bundles.
> This allows delivery with a single .so and/or .a file for all the
> wrappers in a system, which can be automatically linked into ECL (and
> in the future, in SBCL or other implementations, for application
> delivery).
>
> Also, report for a bug, not fixed in the attached patch: compiling,
> linking, etc., should be atomic, by using temporary pathnames;
> otherwise, there is a race condition when multiple processes try to
> execute a script that depends on a CFFI library. Recent-enough ASDF
> 3.1.2 provides with-temporary-file to help, and 3.1.5 uses it
> internally for its own targets, but some implementations still lag
> behind with ASDF 3.0.x, and ASDF can't backwards-compatibly use
> with-temporary-file for you, so you must use it yourself.
>
> This patch also drops support for ASDF 2 or earlier, because backwards
> compatibility is hard, and not necessary: today, all implementations
> provide ASDF 3.
>
> —♯ƒ • François-René ÐVB Rideau •Reflection&Cybernethics• http://fare.tunes.org
> The least deviation from truth will be multiplied later.
>         — Aristotle
From f97e869f5018bcd5aa1e3f09423754aeb9d070ff Mon Sep 17 00:00:00 2001
From: Francois-Rene Rideau <tu...@google.com>
Date: Wed, 16 Sep 2015 23:17:05 -0400
Subject: [PATCH] Make CFFI-grovel play well with ASDF 3.1.5.12.

Create linkable object files and include them in output-files for
compile-op, so that they can be found and linked by a recent-enough
ASDF (3.1.5.12 or later), allowing for delivery of a system with a
single static and/or dynamic library, and in the future, delivery
of a standalone executable that statically links required extensions.
---
 cffi-grovel.asd     |   7 +-
 grovel/asdf.lisp    | 196 ++++++++++++++++++++++++++--------------------------
 grovel/grovel.lisp  | 122 ++++++++++++++++++++++----------
 grovel/invoke.lisp  | 149 ---------------------------------------
 grovel/package.lisp |   6 +-
 5 files changed, 189 insertions(+), 291 deletions(-)
 delete mode 100644 grovel/invoke.lisp

diff --git a/cffi-grovel.asd b/cffi-grovel.asd
index 11ac4a1..f1ddf8a 100644
--- a/cffi-grovel.asd
+++ b/cffi-grovel.asd
@@ -25,17 +25,16 @@
 ;;; DEALINGS IN THE SOFTWARE.
 ;;;
 
-(asdf:defsystem cffi-grovel
+(defsystem "cffi-grovel"
   :description "The CFFI Groveller"
   :author "Dan Knapp <dan...@accela.net>"
-  :depends-on (cffi alexandria)
+  :depends-on ("cffi" "alexandria" (:version "asdf" "3"))
   :licence "MIT"
   :components
-  ((:module grovel
+  ((:module "grovel"
     :serial t
     :components
     ((:file "package")
-     (:file "invoke")
      (:static-file "common.h")
      (:file "grovel")
      (:file "asdf")))))
diff --git a/grovel/asdf.lisp b/grovel/asdf.lisp
index 506cfba..41018c3 100644
--- a/grovel/asdf.lisp
+++ b/grovel/asdf.lisp
@@ -30,139 +30,139 @@
 
 (in-package #:cffi-grovel)
 
-(defun ensure-pathname (thing)
-  (if (typep thing 'logical-pathname)
-      (translate-logical-pathname thing)
-      (pathname thing)))
-
 (defclass cc-flags-mixin ()
   ((cc-flags :initform nil :accessor cc-flags-of :initarg :cc-flags)))
 
-(defmethod asdf:perform :around ((op asdf:compile-op) (file cc-flags-mixin))
-  (declare (ignorable op))
+(defmethod perform :around ((op compile-op) (file cc-flags-mixin))
   (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 (#-asdf3 asdf:operation
-                        #+asdf3 asdf:downward-operation)
-    ()
-    (:documentation "This ASDF operation performs the steps necessary
+(defclass process-op (downward-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
+(defclass process-op-input (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:input-files ((op process-op) (c process-op-input))
-  (list (asdf:component-pathname c)))
-
-(defmethod asdf:component-depends-on ((op process-op) (c process-op-input))
-  `(#-asdf3 (asdf:load-op ,@(asdf::component-load-dependencies c))
-    #+asdf3 (asdf:prepare-op ,c)
-    ,@(call-next-method)))
-
-(defmethod asdf:component-depends-on ((op asdf:compile-op) (c process-op-input))
-  (declare (ignorable op))
-  `((process-op ,(asdf:component-name c))
-    ,@(call-next-method)))
-
-(defmethod asdf:component-depends-on ((op asdf:load-source-op) (c process-op-input))
-  (declare (ignorable op))
-  `((process-op ,(asdf:component-name c))
-    ,@(call-next-method)))
-
-(defmethod asdf:perform ((op asdf:compile-op) (c process-op-input))
-  (let ((generated-lisp-file (first (asdf:output-files (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 (first (asdf:output-files (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))))
+    PROCESS-OP."))
+
+(defmethod input-files ((op process-op) (c process-op-input))
+  (list (component-pathname c)))
+
+(defmethod input-files ((op compile-op) (c process-op-input))
+  (list (first (output-files 'process-op c))))
+
+(defmethod component-depends-on ((op process-op) (c process-op-input))
+  `((prepare-op ,c) ,@(call-next-method)))
+
+(defmethod component-depends-on ((op compile-op) (c process-op-input))
+  `((process-op ,c) ,@(call-next-method)))
+
+(defmethod component-depends-on ((op load-source-op) (c process-op-input))
+  `((process-op ,c) ,@(call-next-method)))
 
 ;;;# ASDF component: GROVEL-FILE
 
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (defclass grovel-file (process-op-input cc-flags-mixin)
-    ()
-    (:default-initargs
-     :generated-lisp-file-type "processed-grovel-file")
-    (:documentation
-     "This ASDF component represents an input file that is processed
-     by PROCESS-GROVEL-FILE.")))
-
-(defmethod asdf:output-files ((op process-op) (c grovel-file))
-  (let* ((input-file (asdf:component-pathname c))
+(defclass grovel-file (process-op-input cc-flags-mixin)
+  ()
+  (:default-initargs
+   :generated-lisp-file-type "processed-grovel-file")
+  (:documentation
+   "This ASDF component represents an input file that is processed
+    by PROCESS-GROVEL-FILE."))
+
+(defmethod output-files ((op process-op) (c grovel-file))
+  (let* ((input-file (first (input-files op c)))
          (output-file (make-pathname :type (generated-lisp-file-type c)
                                      :defaults input-file))
-         (c-file (make-c-file-name output-file)))
+         (c-file (make-c-file-name output-file "__grovel")))
     (list output-file
           c-file
           (exe-filename c-file))))
 
-(defmethod asdf:perform ((op process-op) (c grovel-file))
-  (let ((output-file (first (asdf:output-files op c)))
-        (input-file (asdf:component-pathname c)))
-    (ensure-directories-exist (directory-namestring output-file))
-    (let ((tmp-file (process-grovel-file input-file output-file)))
-      (unwind-protect
-           (alexandria:copy-file tmp-file output-file :if-to-exists :supersede)
-        (delete-file tmp-file)))))
+(defmethod perform ((op process-op) (c grovel-file))
+  (let* ((output-file (first (output-files op c)))
+         (input-file (first (input-files op c)))
+         (tmp-file (process-grovel-file input-file output-file)))
+    (rename-file-overwriting-target tmp-file output-file)))
+
 
 ;;;# ASDF component: WRAPPER-FILE
 
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (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 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.")))
+(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 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."))
 
 (defun wrapper-soname (c)
   (or (soname-of c)
-      (asdf:component-name c)))
+      (component-name c)))
 
-(defmethod asdf:output-files ((op process-op) (c wrapper-file))
-  (let* ((input-file (asdf:component-pathname c))
+(defmethod output-files ((op process-op) (c wrapper-file))
+  (let* ((input-file (first (input-files op c)))
          (output-file (make-pathname :type (generated-lisp-file-type c)
                                      :defaults input-file))
-         (c-file (make-c-file-name output-file))
+         (c-file (make-c-file-name output-file "__wrapper"))
+         (o-file (make-o-file-name output-file "__wrapper"))
          (lib-soname (wrapper-soname c)))
     (list output-file
+          (lib-filename (make-soname lib-soname output-file))
           c-file
-          (lib-filename (make-soname lib-soname output-file)))))
-
-(defmethod asdf:perform ((op process-op) (c wrapper-file))
-  (let ((output-file (first (asdf:output-files op c)))
-        (input-file  (asdf:component-pathname c)))
-    (ensure-directories-exist (directory-namestring output-file))
-    (let ((tmp-file (process-wrapper-file input-file output-file (wrapper-soname c))))
+          o-file)))
+
+;;; Declare the .o and .so files as compilation outputs,
+;;; so they get picked up by bundle operations.
+#.(when (version<= "3.1.5.12" (asdf-version))
+    '(defmethod output-files ((op compile-op) (c wrapper-file))
+      (destructuring-bind (generated-lisp lib-file c-file o-file) (output-files 'process-op c)
+        (declare (ignore generated-lisp c-file))
+        (multiple-value-bind (files translatedp) (call-next-method)
+          (values (append files (list lib-file o-file)) translatedp)))))
+
+(defmethod perform ((op process-op) (c wrapper-file))
+  (let* ((output-file (first (output-files op c)))
+         (input-file (first (input-files op c)))
+         (tmp-file (process-wrapper-file
+                    input-file
+                    :output-defaults output-file
+                    :lib-soname (wrapper-soname c))))
       (unwind-protect
            (alexandria:copy-file tmp-file output-file :if-to-exists :supersede)
-        (delete-file tmp-file)))))
+        (delete-file tmp-file))))
+
 
 ;; Allow for naked :grovel-file and :wrapper-file in asdf definitions.
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (setf (find-class 'asdf::cffi-grovel-file) (find-class 'grovel-file))
-  (setf (find-class 'asdf::cffi-wrapper-file) (find-class 'wrapper-file)))
+(setf (find-class 'asdf::cffi-grovel-file) (find-class 'grovel-file))
+(setf (find-class 'asdf::cffi-wrapper-file) (find-class 'wrapper-file))
+
+;; Implement link-op on image-based platforms.
+
+#-(or clasp ecl mkcl)
+(defmethod perform ((o asdf/bundle::link-op) (c system))
+  (let* ((inputs (input-files o c))
+         (output (first (output-files o c)))
+         (kind (asdf/bundle::bundle-type o)))
+    (when output ;; some operations skip any output when there is no input
+      (ecase kind
+        (:program (link-executable output inputs))
+        ((:lib :static-library)
+         (if (operation-monolithic-p o)
+             (combine-static-libraries output inputs)
+             (link-static-library output inputs)))
+        ((:dll :shared-library) (link-shared-library output inputs))))))
diff --git a/grovel/grovel.lisp b/grovel/grovel.lisp
index b821841..84df252 100644
--- a/grovel/grovel.lisp
+++ b/grovel/grovel.lisp
@@ -37,6 +37,19 @@
         for trim = (string-trim '(#\Space #\Tab #\Newline) s)
         unless (string= "" trim) collect trim))
 
+(defun program-argument (x)
+  (etypecase x
+    (string x)
+    (pathname (native-namestring x))))
+
+(defun invoke (command &rest args)
+  (when (pathnamep command)
+    (setf command (cffi-sys:native-namestring command)))
+  (format *debug-io* "; ~A~{ ~A~}~%" command args)
+  (uiop:run-program `(,command ,@(mapcar 'program-argument args))
+                    :output :interactive :error-output :interactive))
+
+
 ;;;# Error Conditions
 
 (define-condition grovel-error (simple-error) ())
@@ -194,11 +207,21 @@ int main(int argc, char**argv) {
 (defun header-form-p (form)
   (member (form-kind form) *header-forms*))
 
-(defun make-c-file-name (output-defaults)
-  (make-pathname :type "c" :defaults output-defaults))
+(defun make-c-file-name (output-defaults &optional suffix)
+  (make-pathname :type "c"
+                 :name (format nil "~A~@[~A~]" (pathname-name output-defaults) suffix)
+                 :defaults output-defaults))
+
+(defun o-type ()
+  (uiop:os-cond ((uiop:os-unix-p) "o") ((uiop:os-windows-p) "obj") (t "o")))
+
+(defun make-o-file-name (output-defaults &optional suffix)
+  (make-pathname :type (o-type)
+                 :name (format nil "~A~@[~A~]" (pathname-name output-defaults) suffix)
+                 :defaults output-defaults))
 
 (defun generate-c-file (input-file output-defaults)
-  (let ((c-file (make-c-file-name output-defaults)))
+  (let ((c-file (make-c-file-name output-defaults "__grovel")))
     (with-open-file (out c-file :direction :output :if-exists :supersede)
       (with-open-file (in input-file :direction :input)
         (flet ((read-forms (s)
@@ -263,7 +286,7 @@ int main(int argc, char**argv) {
    #+darwin (list "-I" "/opt/local/include/")
    #-darwin nil
    ;; ECL internal flags
-   #+ecl (list c::*cc-flags*)
+   #+ecl (remove-if 'uiop:emptyp (uiop:split-string c::*cc-flags* :separator " "))
    ;; FreeBSD non-base header files
    #+freebsd (list "-I" "/usr/local/include/")))
 
@@ -277,10 +300,9 @@ int main(int argc, char**argv) {
     (4 (list "-m32"))
     (8 (list "-m64"))))
 
-(defparameter *platform-library-flags*
+(defparameter *shared-library-flags*
   (list #+darwin "-bundle"
-        #-darwin "-shared"
-        #-windows "-fPIC"))
+        #-darwin "-shared"))
 
 (defun host-and-directory-namestring (pathname)
   (namestring
@@ -288,24 +310,46 @@ int main(int argc, char**argv) {
                   :type nil
                   :defaults pathname)))
 
-(defun cc-compile-and-link (input-file output-file &key library)
-  (let ((arglist
-         `(,(or (getenv "CC") *cc*)
-           ,@*cpu-word-size-flags*
-           ,@*cc-flags*
-           ;; add the cffi directory to the include path to make common.h visible
-           ,(format nil "-I~A"
-                    (host-and-directory-namestring
-                     (truename (asdf:system-definition-pathname :cffi-grovel))))
-           ,@(when library *platform-library-flags*)
-           "-o" ,(native-namestring output-file)
-           ,(native-namestring input-file))))
-    (when library
-      ;; if it's a library that may be used, remove it
-      ;; so we won't possibly be overwriting the code of any existing process
-      (ignore-some-conditions (file-error)
-        (delete-file output-file)))
-    (apply #'invoke arglist)))
+(defun cc-invocation ()
+  `(,(or (getenv "CC") *cc*) ,@*cpu-word-size-flags* ,@*cc-flags*))
+
+(defun invoke-cc (&rest args)
+  (apply 'invoke `(,@(cc-invocation) ,@args)))
+
+(defun cc-compile (output-file input-file)
+  (invoke-cc
+   #-windows "-fPIC"
+   (format nil "-I~A" (truename (asdf:system-source-directory :cffi-grovel)))
+   "-o" output-file "-c" input-file))
+
+(defun link-executable (output-file inputs)
+  (apply 'invoke-cc "-o" output-file inputs))
+
+(defun link-static-library (output-file inputs)
+  (apply 'invoke
+         `(;; TODO: make it portable to BSD.
+           ;; D is also on FreeBSD, but not on OpenBSD dunno about NetBSD;
+           ;; T seems to only be on Linux. Sigh.
+           ;; A MRI script might be more portable.
+           ;; I couldn't get libtool to work.
+           ;; This will do for now...
+           #+unix ,@`("ar" "rcsDT" ,output-file)
+           #+windows ,@`("lib" "-nologo" ,(format nil "-out:~A" (native-namestring output-file)))
+           ,@inputs)))
+
+(defun combine-static-libraries (output-file inputs)
+  ;;#+unix
+  ;;(apply 'invoke "libtool" "--mode=link" (or (getenv "CC") *cc*) "-static" "-o" output-file inputs)
+  ;;#+windows
+  (link-static-library output-file inputs))
+
+(defun link-shared-library (output-file inputs)
+  ;; remove the library so we won't possibly be overwriting
+  ;; the code of any existing process
+  (uiop:delete-file-if-exists output-file)
+  (apply 'invoke-cc "-o" output-file
+         `(,@*shared-library-flags*
+           ,@inputs)))
 
 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
 ;;; *the extent of a given grovel file.
@@ -314,7 +358,7 @@ int main(int argc, char**argv) {
     (let* ((c-file (generate-c-file input-file output-defaults))
            (exe-file (exe-filename c-file))
            (lisp-file (tmp-lisp-filename c-file)))
-      (cc-compile-and-link c-file exe-file)
+      (link-executable exe-file (list c-file))
       (invoke exe-file (native-namestring lisp-file))
       lisp-file)))
 
@@ -402,9 +446,9 @@ int main(int argc, char**argv) {
     (ecase type
       (integer
        (format out "~&  if(_64_BIT_VALUE_FITS_SIGNED_P(~A))~%" c-name)
-       (format out "    fprintf(output, \"%lli\", (int64_t) ~A);" c-name)
+       (format out "    fprintf(output, \"%lli\", (long long signed) ~A);" c-name)
        (format out "~&  else~%")
-       (format out "    fprintf(output, \"%llu\", (uint64_t) ~A);" c-name))
+       (format out "    fprintf(output, \"%llu\", (long long unsigned) ~A);" c-name))
       (double-float
        (format out "~&  fprintf(output, \"%s\", print_double_for_lisp((double)~A));~%" c-name)))
     (when documentation
@@ -519,8 +563,8 @@ int main(int argc, char**argv) {
         (c-export out slot-lisp-name)))
     (c-format out "(cffi:defcstruct (")
     (c-print-symbol out struct-lisp-name t)
-    (c-printf out " :size %i)"
-              (format nil "sizeof(~A)" struct-c-name))
+    (c-printf out " :size %llu)"
+              (format nil "(long long unsigned) sizeof(~A)" struct-c-name))
     (when documentation
       (c-format out "~%  ~S" documentation))
     (dolist (slot slots)
@@ -818,7 +862,7 @@ string."
 
 (defun generate-c-lib-file (input-file output-defaults)
   (let ((*lisp-forms* nil)
-        (c-file (make-c-file-name output-defaults)))
+        (c-file (make-c-file-name output-defaults "__wrapper")))
     (with-open-file (out c-file :direction :output :if-exists :supersede)
       (with-open-file (in input-file :direction :input)
         (write-string *header* out)
@@ -860,13 +904,17 @@ string."
 
 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
 ;;; *the extent of a given wrapper file.
-(defun process-wrapper-file (input-file output-defaults lib-soname)
+(defun process-wrapper-file (input-file
+                             &key
+                               (output-defaults (make-pathname :defaults input-file :type "processed"))
+                               lib-soname)
   (with-standard-io-syntax
-    (let ((lib-file
-            (lib-filename (make-soname lib-soname output-defaults))))
-      (multiple-value-bind (c-file lisp-forms)
-          (generate-c-lib-file input-file output-defaults)
-        (cc-compile-and-link c-file lib-file :library t)
+    (multiple-value-bind (c-file lisp-forms)
+        (generate-c-lib-file input-file output-defaults)
+    (let ((lib-file (lib-filename (make-soname lib-soname output-defaults)))
+          (o-file (make-o-file-name output-defaults "__wrapper")))
+        (cc-compile o-file c-file)
+        (link-shared-library lib-file (list o-file))
         ;; FIXME: hardcoded library path.
         (values (generate-bindings-file lib-file lib-soname lisp-forms output-defaults)
                 lib-file)))))
diff --git a/grovel/invoke.lisp b/grovel/invoke.lisp
deleted file mode 100644
index 11fac80..0000000
--- a/grovel/invoke.lisp
+++ /dev/null
@@ -1,149 +0,0 @@
-;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
-;;;
-;;; invoke.lisp --- Half-baked portable run-program.
-;;;
-;;; Copyright (C) 2005-2006, Dan Knap <dan...@accela.net>
-;;; Copyright (C) 2005-2006, Emily Backes <lu...@accela.net>
-;;; Copyright (C) 2007, Stelian Ionescu <sione...@cddr.org>
-;;; Copyright (C) 2007, Luis Oliveira <lolive...@common-lisp.net>
-;;;
-;;; Permission is hereby granted, free of charge, to any person
-;;; obtaining a copy of this software and associated documentation
-;;; files (the "Software"), to deal in the Software without
-;;; restriction, including without limitation the rights to use, copy,
-;;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;;; of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-;;;
-;;; The above copyright notice and this permission notice shall be
-;;; included in all copies or substantial portions of the Software.
-;;;
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;;; NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-;;; DEALINGS IN THE SOFTWARE.
-;;;
-
-(in-package #:cffi-grovel)
-
-;;;# Shell Execution
-
-#-(or abcl allegro clisp cmu ecl lispworks openmcl sbcl scl)
-(grovel-error "%INVOKE is unimplemented for this Lisp.  Patches welcome.")
-
-;; FIXME: doesn't do shell quoting
-#+abcl
-(defun %invoke (command arglist)
-  (let ((cmdline (reduce (lambda (str1 str2)
-                           (concatenate 'string str1 " " str2))
-                         arglist :initial-value command))
-        (stream (make-string-output-stream)))
-    (values (ext:run-shell-command cmdline :output stream)
-            (get-output-stream-string stream))))
-
-(defun process-output (process-stream)
-  (with-open-stream (process-stream process-stream)
-    (with-output-to-string (str)
-      (loop for char = (read-char process-stream nil)
-            while char
-            do (write-char char str)))))
-
-#+clisp
-(defun %invoke (command arglist)
-  (let* ((output-file
-          (make-pathname
-           :name (format nil "clisp-cffi-invoke-~A-~A.tmp"
-                         (get-universal-time)
-                         (random 1000))))
-         (ret (ext:run-program command
-                              :arguments arglist
-                              :output output-file)))
-    (with-open-file (stream output-file :direction :input)
-      (multiple-value-prog1
-          (values (etypecase ret
-                    ((eql nil) 0)
-                    ((eql   t) 1)
-                    (integer   ret))
-                  (process-output stream))
-        (delete-file output-file)))))
-
-#+ecl
-(defun %invoke (command arglist)
-  (multiple-value-bind (output-stream exit-code)
-      (ext:run-program "/bin/sh"
-                       (list "-c"
-                             (format nil "~A~{ ~A~}" command arglist))
-                       :wait t :output :stream :input nil :error nil)
-    (values exit-code
-            (process-output output-stream))))
-
-#+(or cmu scl)
-(defun %invoke (command arglist)
-  (let* ((process (ext:run-program command arglist
-                                   :output :stream
-                                   :wait nil
-                                   :error :output))
-         (output (process-output (ext:process-output process))))
-    (ext:process-wait process)
-    (values (ext:process-exit-code process) output)))
-
-#+sbcl
-(defun %invoke (command arglist)
-  (let* ((process (sb-ext:run-program command arglist
-                                      :output :stream
-                                      :wait nil
-                                      :error :output
-                                      :search t))
-         (output (process-output (sb-ext:process-output process))))
-    (sb-ext:process-wait process)
-    (values (sb-ext:process-exit-code process) output)))
-
-#+openmcl
-(defun %invoke (command arglist)
-  (let* ((exit-code)
-         (output (with-output-to-string (s)
-                   (let ((process (ccl:run-program command arglist
-                                                   :output s
-                                                   :error :output)))
-                     (setq exit-code (nth-value 1 (ccl:external-process-status process)))))))
-    (values exit-code output)))
-
-#+allegro
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (require '#:osi))
-
-#+allegro
-(defun %invoke (command arglist)
-  (let ((cmd #-mswindows (concatenate 'vector (list command command) arglist)
-             #+mswindows (format nil "~A~{ ~A~}" command arglist)))
-    (multiple-value-bind (output error-output exit-code)
-        (excl.osi:command-output cmd :whole t)
-      (declare (ignore error-output))
-      (values exit-code output))))
-
-;;; FIXME: Runs shell, and arguments are unquoted.
-#+lispworks
-(defun %invoke (command arglist)
-  (let ((s (make-string-output-stream)))
-    (values (sys:call-system-showing-output
-             (format nil "~A~{ ~A~}" command arglist)
-             :output-stream s :prefix "" :show-cmd nil)
-            (get-output-stream-string s))))
-
-;;; Do we really want to suppress the output by default?
-(defun invoke (command &rest args)
-  (when (pathnamep command)
-    (setf command (cffi-sys:native-namestring command)))
-  (format *debug-io* "; ~A~{ ~A~}~%" command args)
-  (multiple-value-bind (exit-code output)
-      (%invoke command args)
-    (unless (zerop exit-code)
-      (grovel-error "External process exited with code ~S.~@
-                     Command was: ~S~{ ~S~}~@
-                     Output was:~%~A"
-                    exit-code command args output))
-    output))
diff --git a/grovel/package.lisp b/grovel/package.lisp
index 66262c7..5fe9fd8 100644
--- a/grovel/package.lisp
+++ b/grovel/package.lisp
@@ -23,9 +23,9 @@
 ;;; DEALINGS IN THE SOFTWARE.
 ;;;
 
-(defpackage #:cffi-grovel
-  (:use #:common-lisp #:alexandria)
-  (:import-from #:cffi-sys #:native-namestring)
+(uiop:define-package #:cffi-grovel
+  (:mix #:asdf #:uiop #:alexandria #:common-lisp)
+  (:import-from #:asdf/bundle #:link-op)
   (:export
    ;; Class name
    #:grovel-file
-- 
2.6.0.rc2.230.g3dd15c0

Reply via email to