Of course, I forgot to attach the patch. Here it is.
If you think it's satisfactory, I can push.

Hopefully, the added test will convince you that it is all working.

—♯ƒ • François-René ÐVB Rideau •Reflection&Cybernethics• http://fare.tunes.org
To converse at the distance of the Indes by means of sympathetic contrivances
may be as natural to future times as to us is a literary correspondence.
                — Joseph Glanvill, 1661


On Thu, Mar 13, 2014 at 7:11 PM, Faré <fah...@gmail.com> wrote:
> On Thu, Mar 13, 2014 at 4:24 PM, Robert P. Goldman <rpgold...@sift.info> 
> wrote:
>> For the record, it's not that I'm objecting to the build-operation idea.
>>  I'm sorry if you got that idea, and felt that you had to spend a lot of
>> time convincing me!
>>
>> My concern was a much more limited one: that the word "build" doesn't
>> properly convey what is going to happen.
>>
> Well, ASDF itself has long been described as a build system or build tool
> (including in our ILC 2010 article), just like make, ant, etc.
> See also Wikipedia pages for each of these.
> What do these programs do? They build.
> I don't love the word, but I don't know a better one.
>
>> I believe that the operation is "prepare the direct object system for me
>> to use it."
>>
> Here are some suggestion:
>
> prepare (shorthand: p)
> update (shorthand: u)
> build (shorthand: b)
> build-and-update (shorthand: bu)
> build-and-maintain (shorthand: bnm, or |bnm,|, which perfectly fits
> the asdf keyboard pattern)
> build-operation or build-op (shorthand: bo, or bop)
> default-operation (shorthand: do, shadowing cl:do, or dop)
> operate 'build-op (shorthand: op, or ob)
> operate-default (shorthand: od)
> ok (shorthand: o)
> ensure-component-is-built-and-update-current-image-for-component
> (shorthand: ecibaucifc)
>
> If we pick the latter, we have utterly failed the user.
> Actually, I already have failed the user by naming the function build-system,
> which is too long a name, and deceiving because it's not just for systems:
> you can (build-system '("system" "module" "component")) to build a
> targetted component.
> Since obviously no one is using it yet, I propose we remove it from 3.1.
>
>> I don't think "build" is the right word for this, but I am happy to see
>> some sort of "do the default operation," as long as we can come up with
>> a name that conveys the meaning to the user.
>>
>> I think "build" in its normal sense has a connotation much closer to the
>> things that bundle-op or save-image-op would do.
>>
> I don't think it's right, only that it's the rightest available so far.
>
> PS: here is a second patch on top of the former one, that makes string
> designators for operations, so that :build-operation "foo::op" works.
> Added a suitable test for defsystem-depends-on to the test suite.
> Without this feature, build-op is much less useful.
>
> —♯ƒ • François-René ÐVB Rideau •Reflection&Cybernethics• http://fare.tunes.org
> The Party will yield power when and only when it is in the best interest of
> its members. Don't fight the Party: buy It with the profits of Liberty.
From 64c4cb5134d61d9640ff3e37118cc059ff033089 Mon Sep 17 00:00:00 2001
From: Francois-Rene Rideau <tu...@google.com>
Date: Thu, 13 Mar 2014 18:55:17 -0400
Subject: [PATCH 2/2] Accept strings as OPERATION class designators, read in
 package ASDF. Refactor COMPONENT class designators to use the same general
 method. As a substrate, goodbye uiop/utility:find-class*, hello
 uiop/utility:coerce-class.

In the operate upgrade handler, assume there is
no uninterning and renaming packages away anymore going forward.

Add tests for strings as class designators using defsystem-depends-on.
---
 action.lisp                           |  5 ++++-
 asdf.asd                              |  2 +-
 operate.lisp                          | 28 +++++++++++++---------------
 operation.lisp                        | 10 +++++++---
 parse-defsystem.lisp                  | 24 ++++++++----------------
 test/l-file.lisp                      | 10 ++++++++++
 test/l-operation.lisp                 | 12 ++++++++++++
 test/test-defsystem-depends-on.script | 30 ++++++++++++++++++++++++++++++
 test/test-extension.l                 |  0
 uiop/utility.lisp                     | 30 +++++++++++++++++++++++++-----
 10 files changed, 110 insertions(+), 41 deletions(-)
 create mode 100644 test/l-file.lisp
 create mode 100644 test/l-operation.lisp
 create mode 100644 test/test-defsystem-depends-on.script
 create mode 100644 test/test-extension.l

diff --git a/action.lisp b/action.lisp
index 93da2ee..76106f8 100644
--- a/action.lisp
+++ b/action.lisp
@@ -66,7 +66,10 @@
                    `(apply ',function ,@prefix ,o ,c ,@suffix ,rest)
                    `(,function ,@prefix ,o ,c ,@suffix))))
         `(progn
-           (defmethod ,function (,@prefix (,operation symbol) component ,@suffix ,@more-args)
+           (defmethod ,function (,@prefix (,operation string) ,component ,@suffix ,@more-args)
+             (let ((,component (find-component () ,component))) ;; do it first, for defsystem-depends-on
+               ,(next-method `(safe-read-from-string ,operation :package :asdf/interface) component)))
+           (defmethod ,function (,@prefix (,operation symbol) ,component ,@suffix ,@more-args)
              (if ,operation
                  ,(next-method
                    (if operation-initargs ;backward-compatibility with ASDF1's operate. Yuck.
diff --git a/asdf.asd b/asdf.asd
index 5e3ca5e..9bd9977 100644
--- a/asdf.asd
+++ b/asdf.asd
@@ -49,7 +49,7 @@
    (:file "cache" :depends-on ("upgrade"))
    (:file "find-system" :depends-on ("system" "cache"))
    (:file "find-component" :depends-on ("find-system"))
-   (:file "operation" :depends-on ("upgrade"))
+   (:file "operation" :depends-on ("find-system"))
    (:file "action" :depends-on ("find-component" "operation"))
    (:file "lisp-action" :depends-on ("action"))
    (:file "plan" :depends-on ("lisp-action"))
diff --git a/operate.lisp b/operate.lisp
index 1fc00a5..5aa4692 100644
--- a/operate.lisp
+++ b/operate.lisp
@@ -55,11 +55,13 @@ The :FORCE or :FORCE-NOT argument to OPERATE can be:
                                 (on-failure *compile-file-failure-behaviour*) &allow-other-keys)
     (let* ((systems-being-operated *systems-being-operated*)
            (*systems-being-operated* (or systems-being-operated (make-hash-table :test 'equal)))
-           (operation-name (reify-symbol (etypecase operation
-                                           (operation (type-of operation))
-                                           (symbol operation))))
-           (operation-initargs (operation-original-initargs operation))
-           (component-path (typecase component
+           (operation-remaker ;; how to remake the operation after ASDF was upgraded (if it was)
+             (etypecase operation
+               (operation (let ((name (type-of operation))
+                                (initargs (operation-original-initargs operation)))
+                            #'(lambda () (make-operation name :original-initargs initargs initargs))))
+               ((or symbol string) (constantly operation))))
+           (component-path (typecase component ;; to remake the component after ASDF upgrade
                              (component (component-find-path component))
                              (t component))))
       ;; Before we operate on any system, make sure ASDF is up-to-date,
@@ -69,11 +71,7 @@ The :FORCE or :FORCE-NOT argument to OPERATE can be:
           ;; If we were upgraded, restart OPERATE the hardest of ways, for
           ;; its function may have been redefined, its symbol uninterned, its package deleted.
           (return-from operate
-            (apply (find-symbol* 'operate :asdf)
-                   (apply (find-symbol* 'make-operation :asdf)
-                          (unreify-symbol operation-name)
-                          :original-initargs operation-initargs operation-initargs)
-                   component-path keys))))
+            (apply 'operate (funcall operation-remaker) component-path keys))))
       ;; Setup proper bindings around any operate call.
       (with-system-definitions ()
         (let* ((*verbose-out* (and verbose *standard-output*))
@@ -114,15 +112,15 @@ for how to load or compile stuff")
 
   (defclass build-op (non-propagating-operation) ()
     (:documentation "Since ASDF3, BUILD-OP is the recommended 'master' operation,
-to operate by default on a system or component.
-Its meaning is configurable via the :BUILD-OPERATION option of a component,
-which is typically the name of a specific operation to which to delegate the build,
-and may be NIL, which designates the *LOAD-SYSTEM-OPERATION*
+to operate by default on a system or component, via the function BUILD.
+Its meaning is configurable via the :BUILD-OPERATION option of a component.
+which typically specifies the name of a specific operation to which to delegate the build,
+as a symbol or as a string later read as a symbol (after loading the defsystem-depends-on);
+if NIL is specified (the default), BUILD-OP falls back to the *LOAD-SYSTEM-OPERATION*
 that will load the system in the current image, and its typically LOAD-OP."))
   (defmethod component-depends-on ((o build-op) (c component))
     `((,(or (component-build-operation c) *load-system-operation*) ,c)))
 
-
   (defun build (system &rest keys)
     "The recommended way to interact with ASDF3.1 is via (ASDF:BUILD :FOO).
 It will build system FOO using the operation BUILD-OP,
diff --git a/operation.lisp b/operation.lisp
index 9521bcc..a192a95 100644
--- a/operation.lisp
+++ b/operation.lisp
@@ -3,7 +3,7 @@
 
 (uiop/package:define-package :asdf/operation
   (:recycle :asdf/operation :asdf/action :asdf) ;; asdf/action for FEATURE pre 2.31.5.
-  (:use :uiop/common-lisp :uiop :asdf/upgrade)
+  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/find-system)
   (:export
    #:operation
    #:operation-original-initargs #:original-initargs ;; backward-compatibility only. DO NOT USE.
@@ -42,8 +42,10 @@
   (defparameter* *operations* (make-hash-table :test 'equal))
 
   (defun make-operation (operation-class &rest initargs)
-    (ensure-gethash (cons operation-class initargs) *operations*
-                    (list* 'make-instance operation-class initargs)))
+    (let ((class (coerce-class operation-class
+                               :package :asdf/interface :super 'operation :error 'sysdef-error)))
+      (ensure-gethash (cons class initargs) *operations*
+                      (list* 'make-instance class initargs))))
 
   (defgeneric find-operation (context spec)
     (:documentation "Find an operation by resolving the SPEC in the CONTEXT"))
@@ -52,6 +54,8 @@
   (defmethod find-operation (context (spec symbol))
     (when spec ;; NIL designates itself, i.e. absence of operation
       (apply 'make-operation spec (operation-original-initargs context))))
+  (defmethod find-operation (context (spec string))
+    (apply 'make-operation spec (operation-original-initargs context)))
   (defmethod operation-original-initargs ((context symbol))
     (declare (ignorable context))
     nil))
diff --git a/parse-defsystem.lisp b/parse-defsystem.lisp
index 2cf2b02..fed65fa 100644
--- a/parse-defsystem.lisp
+++ b/parse-defsystem.lisp
@@ -48,22 +48,14 @@
   (defvar *default-component-class* 'cl-source-file)
 
   (defun class-for-type (parent type)
-    (or (loop :for symbol :in (list
-                               type
-                               (find-symbol* type *package* nil)
-                               (find-symbol* type :asdf/interface nil)
-                               (and (stringp type) (safe-read-from-string type :package :asdf/interface)))
-              :for class = (and symbol (symbolp symbol) (find-class* symbol nil))
-              :when (and class
-                         (#-cormanlisp subtypep #+cormanlisp cl::subclassp
-                          class (find-class* 'component)))
-                :return class)
-        (and (eq type :file)
-             (find-class*
-              (or (loop :for p = parent :then (component-parent p) :while p
-                        :thereis (module-default-component-class p))
-                  *default-component-class*) nil))
-        (sysdef-error "don't recognize component type ~A" type))))
+      (or (coerce-class type :package :asdf/interface :super 'component :error nil)
+          (and (eq type :file)
+               (coerce-class
+                (or (loop :for p = parent :then (component-parent p) :while p
+                            :thereis (module-default-component-class p))
+                    *default-component-class*)
+                :package :asdf/interface :super 'component :error nil))
+          (sysdef-error "don't recognize component type ~S" type))))
 
 
 ;;; Check inputs
diff --git a/test/l-file.lisp b/test/l-file.lisp
new file mode 100644
index 0000000..2e39c99
--- /dev/null
+++ b/test/l-file.lisp
@@ -0,0 +1,10 @@
+(defpackage :l-file
+  (:use :asdf :uiop :cl) ;; asdf/package-system dependencies
+  (:export #:cl-source-file.l))
+
+(in-package :l-file)
+
+(defclass cl-source-file.l (cl-source-file)
+  ((type :initform "l")))
+
+(defclass asdf::cl-source-file.l (cl-source-file.cl) ())
diff --git a/test/l-operation.lisp b/test/l-operation.lisp
new file mode 100644
index 0000000..b901756
--- /dev/null
+++ b/test/l-operation.lisp
@@ -0,0 +1,12 @@
+(cl:defpackage :l-operation
+  (:use :asdf :uiop :cl) ;; asdf/package-system dependencies
+  (:export #:op #:*x*))
+
+(cl:in-package :l-operation)
+
+(defparameter *x* 0)
+
+(defclass op (load-op) ())
+
+(defmethod perform :after ((o op) (c t))
+  (incf *x*))
diff --git a/test/test-defsystem-depends-on.script b/test/test-defsystem-depends-on.script
new file mode 100644
index 0000000..9a60ad9
--- /dev/null
+++ b/test/test-defsystem-depends-on.script
@@ -0,0 +1,30 @@
+;;-*- Lisp -*-
+
+(def-test-system test-defsystem-depends-on-1
+  :defsystem-depends-on (test-asdf/l-file)
+  :default-component-class "l-file:cl-source-file.l"
+  :components
+  ((:cl-source-file.l "file1" :type "lisp")
+   (:cl-source-file "file2" :depends-on ("file1"))
+   (:file "test-extension")))
+
+(load-system :test-defsystem-depends-on-1)
+
+(def-test-system test-defsystem-depends-on-2
+  :defsystem-depends-on (test-asdf/l-operation)
+  :build-operation "l-operation:op"
+  :components
+  ((:file "file3")))
+
+(assert-equal 0 l-operation:*x*)
+
+(build :test-defsystem-depends-on-2)
+
+(assert-equal 2 l-operation:*x*) ;; perform called twice, on file and on system.
+
+(def-test-system test-defsystem-depends-on-3
+  :defsystem-depends-on (test-asdf/l-operation)
+  :build-operation "does-not-exist"
+  :components ((:file "file3")))
+
+(signals asdf::formatted-system-definition-error (build :test-defsystem-depends-on-3))
diff --git a/test/test-extension.l b/test/test-extension.l
new file mode 100644
index 0000000..e69de29
diff --git a/uiop/utility.lisp b/uiop/utility.lisp
index 230c0c7..c8fa387 100644
--- a/uiop/utility.lisp
+++ b/uiop/utility.lisp
@@ -24,7 +24,7 @@
    #:base-string-p #:strings-common-element-type #:reduce/strcat #:strcat ;; strings
    #:first-char #:last-char #:split-string #:stripln #:+cr+ #:+lf+ #:+crlf+
    #:string-prefix-p #:string-enclosed-p #:string-suffix-p
-   #:find-class* ;; CLOS
+   #:coerce-class ;; CLOS
    #:stamp< #:stamps< #:stamp*< #:stamp<= ;; stamps
    #:earlier-stamp #:stamps-earliest #:earliest-stamp
    #:later-stamp #:stamps-latest #:latest-stamp #:latest-stamp-f
@@ -333,10 +333,30 @@ the two results passed to STRCAT always reconstitute the original string"
 
 ;;; CLOS
 (with-upgradability ()
-  (defun find-class* (x &optional (errorp t) environment)
-    (etypecase x
-      ((or standard-class built-in-class) x)
-      (symbol (find-class x errorp environment)))))
+  (defun coerce-class (class &key (package :cl) (super t) (error 'error))
+    "Coerce CLASS to a class that is subclass of SUPER if specified,
+or invoke ERROR handler as per CALL-FUNCTION.
+
+A keyword designates the name a symbol, which when found in PACKAGE, designates a class.
+A string is read as a symbol while in PACKAGE, the symbol designates a class.
+
+A class object designates itself.
+NIL designates itself (no class).
+A symbol otherwise designates a class by name."
+    (let* ((normalized
+             (typecase class
+              (keyword (find-symbol* class package nil))
+              (string (symbol-call :uiop :safe-read-from-string class :package package))
+              (t class)))
+           (found
+             (etypecase normalized
+               ((or standard-class built-in-class) normalized)
+               ((or null keyword) nil)
+               (symbol (find-class normalized nil nil)))))
+      (or (and found
+               (or (eq super t) (#-cormanlisp subtypep #+cormanlisp cl::subclassp found super))
+               found)
+          (call-function error "Can't coerce ~S to a ~@[class~;subclass of ~:*~S]" class super)))))
 
 
 ;;; stamps: a REAL or a boolean where NIL=-infinity, T=+infinity
-- 
1.9.0.279.gdc9e3eb

Reply via email to