Commit 58dfd53f authored by Robert P. Goldman's avatar Robert P. Goldman
Browse files
parents a661e90f 1aa0ea0f
?
??
build
asdf.lisp
......
......@@ -10,13 +10,12 @@ sourceDirectory := $(shell pwd)
ifdef ASDF_TEST_LISPS
lisps ?= ${ASDF_TEST_LISPS}
else
lisps ?= ccl clisp sbcl ecl ecl_bytecodes cmucl abcl scl allegro lispworks allegromodern xcl gcl
lisps ?= ccl clisp sbcl ecl ecl_bytecodes cmucl abcl scl allegro lispworks allegromodern gcl xcl
endif
## grep for #+/#- features in the test/ directory to see plenty of disabled tests on some platforms
## NOT SUPPORTED BY OUR AUTOMATED TESTS:
## cormancl genera lispworks-personal-edition mkcl rmcl
## Some are manually tested once in a while.
## MAJOR FAIL: gclcvs -- Compiler bug fixed upstream, but gcl fails to compile on modern Linuxen.
## grep for #+/#- features in the test/ directory to see plenty of disabled tests.
ifdef ASDF_TEST_SYSTEMS
s ?= ${ASDF_TEST_SYSTEMS}
endif
......@@ -41,11 +40,9 @@ SBCL ?= sbcl
SCL ?= scl
XCL ?= xcl
# website, tag, install
header_lisp := header.lisp
driver_lisp := uiop/package.lisp uiop/common-lisp.lisp uiop/utility.lisp uiop/os.lisp uiop/pathname.lisp uiop/filesystem.lisp uiop/stream.lisp uiop/image.lisp uiop/run-program.lisp uiop/lisp-build.lisp uiop/configuration.lisp uiop/backward-driver.lisp uiop/driver.lisp
defsystem_lisp := upgrade.lisp component.lisp system.lisp cache.lisp find-system.lisp find-component.lisp operation.lisp action.lisp lisp-action.lisp plan.lisp operate.lisp backward-internals.lisp defsystem.lisp bundle.lisp concatenate-source.lisp output-translations.lisp backward-interface.lisp source-registry.lisp interface.lisp user.lisp footer.lisp
defsystem_lisp := upgrade.lisp component.lisp system.lisp cache.lisp find-system.lisp find-component.lisp operation.lisp action.lisp lisp-action.lisp plan.lisp operate.lisp output-translations.lisp source-registry.lisp backward-internals.lisp parse-defsystem.lisp bundle.lisp concatenate-source.lisp backward-interface.lisp package-system.lisp interface.lisp user.lisp footer.lisp
all_lisp := $(header_lisp) $(driver_lisp) $(defsystem_lisp)
# Making ASDF itself should be our first, default, target:
......@@ -60,6 +57,8 @@ load: build/asdf.lisp
install: archive
bump: bump-version
git commit -a -m "Bump version to $$(eval a=$$(cat version.lisp-expr) ; echo $$a)"
bump-version: build/asdf.lisp
./bin/asdf-builder bump-version ${v}
......@@ -131,27 +130,53 @@ test: test-lisp test-clean-load test-load-systems doc
test-load-systems: build/asdf.lisp
./test/run-tests.sh -l ${l} ${s}
test-all-lisps:
${MAKE} test-load-systems
@for lisp in ${lisps} ; do \
${MAKE} test-lisp test-upgrade test-clean-load l=$$lisp || exit 1 ; \
done
test-all-lisps: test-load-systems test-all-clean-load test-all-lisp test-all-upgrade
# test upgrade is a very long run... This does just the regression tests
test-all-no-upgrade:
@for lisp in ${lisps} ; do \
${MAKE} test-lisp test-clean-load l=$$lisp || exit 1 ; \
done
test-all-clean-load:
@for lisp in ${lisps} ; do ${MAKE} test-clean-load l=$$lisp || exit 1 ; done
test-all-lisp:
@for lisp in ${lisps} ; do ${MAKE} test-lisp l=$$lisp || exit 1 ; done
test-all-upgrade:
@for lisp in ${lisps} ; do \
${MAKE} test-upgrade l=$$lisp || exit 1 ; \
done
@for lisp in ${lisps} ; do ${MAKE} test-upgrade l=$$lisp || exit 1 ; done
test-all-no-upgrade: doc test-load-systems test-all-clean-load test-all-lisp
test-all: test-all-no-upgrade test-all-upgrade
test-all-lisp-no-stop:
@for lisp in ${lisps} ; do ${MAKE} test-lisp l=$$lisp ; done ; :
test-all-upgrade-no-stop:
@for lisp in ${lisps} ; do ${MAKE} test-upgrade l=$$lisp ; done ; :
test-all-no-upgrade-no-stop: doc test-load-systems test-all-clean-load test-all-lisp-no-stop
make --quiet check-all-test-results
test-all-no-stop: doc test-load-systems test-all-clean-load test-all-lisp-no-stop test-all-upgrade-no-stop
make --quiet check-all-results
check-all-test-results:
@A="`grep -L '[5-9][0-9] passing and 0 failing' build/results/*-test.text`" ; \
if [ -n "$$A" ] ; then \
echo "Unexpected test failures on these implementations:" ; \
echo "$$A" ; \
exit 1 ; \
fi
test-all: doc test-all-lisps
check-all-upgrade-results:
@A="$$(for i in build/results/*-upgrade.text ; do \
case "$$(tail -1 < $$i)" in "Upgrade test succeeded for "*) ;; \
*) echo $$i ; esac ; done)" ; \
if [ -n "$$A" ] ; then \
echo "Unexpected upgrade failures on these implementations:" ; \
echo "$$A" ; \
exit 1 ; \
fi
test-all-no-stop:
-make doc ; for l in ${lisps} ; do make t l=$$l ; make u l=$$l ; done ; true
check-all-results:
@r=0 ; make check-all-test-results || r=1 ; make check-all-upgrade-results || r=1 ; exit $r
extract: extract-all-tagged-asdf
extract-all-tagged-asdf: build/asdf.lisp
......
ASDF: another system definition facility
ASDF: Another System Definition Facility
========================================
If you cloned our git repository, bootstrap a copy of build/asdf.lisp with:
make
Quick Summary
-------------
1- Build it with::
make
2- Make sure you put it under a path registered by the source-registry,
if that isn't the case yet (see the manual). One place would be::
~/.local/share/common-lisp/source/asdf/
To use ASDF, read our manual:
What is ASDF?
-------------
ASDF is the de facto standard build facility for Common Lisp.
If you come from the C/C++ world, the function ASDF covers a bit of what
each of Make, autoconf, Make, dlopen and libc do for C programs:
it orchestrates the compilation and dependency management,
handles some of the portability issues, dynamically finds and loads code,
and offers some portable system access.
Except everything is different in Common Lisp, and ultimately much simpler,
though it requires acquiring some basic concepts;
notably, ASDF tries to make it all happen in the current Lisp image.
To use ASDF, read our manual::
http://common-lisp.net/project/asdf/asdf.html
The first few sections, Loading ASDF, Configuring ASDF and Using ASDF,
will get you started as a simple user.
If you want to define your own systems, further read the section
Defining systems with defsystem.
ASDF 3 now includes an extensive runtime support library:
UIOP, the Utilities for Implementation- and OS- Portability.
Its documentation unhappily lies mainly in the source code and docstrings.
See uiop/README for an introduction.
More information and additional links can be found on ASDF's home page at::
More information and additional links can be found on ASDF's home page at:
http://common-lisp.net/project/asdf/
Last updated Thursday, April 10th, 2013.
Building and testing it
-----------------------
If you cloned our git repository, bootstrap a copy of build/asdf.lisp with::
make
To run all the tests on your favorite Lisp implementation $L,
choose your most elaborate installed system $S, and try::
make t u l=$L s=$S
Debugging tip
-------------
To load ASDF in such a way that M-. will work, install the source code, and run::
(asdf:load-system :uiop) ;; for uiop
(map () 'load ;; for asdf/defsystem
(mapcar 'asdf:component-pathname
(asdf::required-components :asdf/defsystem :keep-component 'asdf:cl-source-file)))
Last updated Wednesday, January 31st, 2014.
* Find out why ABCL fails the timestamp propagation test.
* Decree that versions older than 2 years are not supported;
yet, keep supporting an upgrade from whatever vendors provide
— so push them to provide recent stuff.
Already, swank-asdf doesn't support anything older than 2.014.6
(as provided by the first 2011 iteration of Quicklisp), and
I (fare) am pushing for swank-asdf to not support anything older
than 2.019 (still used by LispWorks 6.1).
Hopefully, sometime in 2014, everyone will have adopted ASDF 3.
Maybe remove old versions from upgrade tests; or maybe not:
if we support old versions in any way,
it should be for upgrade only, and "punt" is acceptable.
Upgrade tests might be split in two test sets: short and long.
* Moving toward cross-compilation (ASDF 3.2? 4?)
This would allow to get most of the benefits of XCVB
(reproducibility, scalability, applicability to mobile platforms, etc.),
except without the backward incompatibility of XCVB.
NB: the MOCL author would benefit a lot, and might chip in.
** Need to further split asdf:perform in two parts:
A part that runs on the host and specifies (as SEXP or string to read)
forms that must be evaluated on the target machine, not the host:
(asdf:perform-forms target operation component)
The default implementation can fallback to running the
(asdf:perform operation component) on the target,
which if it is on specially crafted operation and component objects
that do not require recreating the component hierarchy and plan,
but still require loading the defsystem-depends-on dependencies:
on the target:
;; null means the target is the current host.
;; Or should the base class be plan-traversal or such?
(defmethod perform-form ((target null) operation component)
;; :host means execute on the host. () is empty options.
`(:host () (asdf:perform ',op ',comp)))
;; external-target means a cross compiler that we spawn.
(defmethod perform-form ((target external-target) operation component)
`(:target ;; who is to execute that? the target Lisp.
;; options for the performing system:
(:depends-on ,(defsystem-dependencies
(component-system component)))
;; The actual forms to evaluate
(asdf:perform ,(fake-operation-form operation)
,(fake-component-form component))))
By specifying :host-common-lisp you can have forms run on the host.
By specifying :run-program, you can spawn a subprocess without
having to first spawn a Lisp that will call run-program. etc.
The other way around the default perform method could be:
(defmethod perform ((o t) (c t))
(let ((forms (perform-forms nil o c)))
(if (equal forms
`((asdf:perform ',o ',c)))
(error "Missing perform method on ..." ...)
(map () 'eval-thunk forms))))
Note that in the general case, there are multiple forms,
that must be provided on the command line somehow
(see the lisp-invocation system and also how cl-launch does it),
with packages necessary to read latter forms being
created by former forms, so you cannot read everything in one go.
ASDF or at least UIOP should probably be provided in the target
image by default, anyway, without needing to be explicitly specified.
A more declarative interface to needing the defsystem-deps
is probably useful.
** A real cross-compiling backend that works the way of XCVB
would need to compute a complete graph for the plan,
which could be achieved by inheriting from the code in POIU.
Thinking about it, the cross-compiling target object
could actually be the same as the plan object.
The basic case of in-image execution would need to be in ASDF,
while the actual cross-compilation case would be in an extension.
** Audit all the current specializers of perform, and offer them a new way;
they can keep the perform method to run on older versions of ASDF.
** Modify SBCL and other implementations so they provide UIOP
independently from ASDF?
** The default perform-plan method could iterate over pre-computed forms,
in a way that does not involve the ASDF object model anymore,
making ASDF friendlier to self-upgrades.
Then, ASDF wouldn't need to be concatenated during upgrade
— but should still concatenate for bootstrap purposes,
when require'ing it as an implementation-provided module,
compiling from ASDF 2, or otherwise distributing as a single file.
* Implementation bugs
** CMUCL has bugs:
*** In ASDF 2.32.13, sibling-dependencies is renamed to sideway-dependencies
in defclass component, a superclass of system, and upgrade from
2.32 or earlier thus fails with
Error in function KERNEL:CLASS-TYPEP: Class is currently invalid:
#<KERNEL::STANDARD-CLASS ASDF/SYSTEM:SYSTEM {58398145}>
in a direct call from ASDF/FIND-SYSTEM:LOCATE-SYSTEM,
which suggests CMUCL's PCL fails to finalize classes properly.
*** Non-default optimize settings may or may not subtly break PCL.
For the longest time (from 2.26.136 to 3.1.0.42), ASDF had:
(declaim (optimize (speed 1) (safety 3) (debug 3)))
This form may or may not work around that breakage in some cases:
(setf c::top-level-lambda-max 0)
*** At some point there was massive breakage due to uninitialized slots
as revealed in 3.1.0.37 with
make t l=cmucl t=test-encodings.script
which was a different PCL bug from the above (without same workaround)
but possibly also triggered by the non-standard declaim.
** XCL has bad bugs:
make-pathname doesn't handle :type nil properly and
has massive lossage in logical-pathname support.
If using block () and return in search-for-system-definition
instead of return-from, XCL mistakenly has the return jump
to the end of the try function or something.
It also takes a long time for errors to propagate into a debugging REPL —
Maybe O(n^3) behavior or worse in walking the C++ stack?
The backtrace frames may contains unprintable objects.
make-pathname fails to copy the version from the defaults.
(make-pathname :host "ASDFTEST" :directory '(:absolute "system2" "module4")
:name nil :type nil) fails to handle the logical-pathname-ness of ASDFTEST.
** GCL is almost working again; but implementation bugs remain.
See November 2013 discussion on gcl-devel.
* Another GCL compiler bug:
when I changed the definition of getcwd from
(let ((*default-pathname-defaults* #p"")) (truename #p"")) to
(let ((*default-pathname-defaults* *nil-pathname*)) (truename *nil-pathname*))
to guard against e.g. a logical-pathname context while loading asdf
and parsing #p"", calls to getcwd result in a segfault.
* An another bug: gcl refuses dynamic-extent declaration on functions.
uiop/stream.lisp: #-gcl (declare (dynamic-extent ,@(when
before `(#',beforef)) ,@(when after `(#',afterf))))
* (typep p 'logical-pathname) should be T if p has a logical-pathname host.
* apropos is case-sensitive and returns a same symbol many times
* compile-file fails to return proper secondary values in case of non-style WARNING.
* (pathname-directory #p"foo/") is incorrectly ("foo") instead of (:RELATIVE "foo")
* Missing: chdir, combine-fasls, and plenty more UIOP functions.
* Do whatever it takes to pass the asdf tests, add the above?
* Have (require "asdf") and (require "ASDF") both work.
* Trying to uiop:slurp-stream-forms from a stream with #+(or) :foo
(or read-file-forms from an file with same) results in an error,
rather than nil. This is probably a bug in #+ processing.
Unhappily, debian creates such a file in
/etc/common-lisp/asdf-output-translations.conf.d/01-common-lisp-controller.conf
* Tests that try to catch an error fail (but catching a warning succeeds),
which suggests brokenness in handler-bind and/or error.
** Find out why ABCL fails the timestamp propagation test.
** ECL has issues with its bundles.
On MacOS X, test-bundle.script fails.
On Linux, test-program.script fails with recent ECL 237af2e,
but used to work quite fine earlier in 2013.
* Find an easier way to bind variables around compilation and loading of files
* Design & Implement some out-of-line configuration mechanism for various options?
i.e. let configuration files override some variables around some actions.
* Could be used to override new backward-incompatible defaults
(e.g. for operation propagation, deferred warnings, etc.)
* Use the above variable binding mechanism.
* Fix bugs on http://bugs.launchpad.net/asdf/+bugs
* fix directory-files to not return directories on CCL, etc. Add tests.
* use cl-test-grid to see if asdf-user can :use most (all?) of UIOP.
Unhappily, on SBCL, sb-grovel uses asdf and sb-ext,
which causes a clash with run-program.
Or has this been changed long enough ago? Can it be special-cased?
** fix directory-files to not return directories on CCL, etc. Add tests.
* Learn to use cl-grid-test
** So we can easily make sure ASDF changes don't break stuff,
and that breakage gets fixed quickly.
** So we see if ASDF-USER can :use most (all?) of UIOP.
SB-GROVEL used (until 25c1769 from 2013-02-23) to :use both ASDF and
SB-EXT, which prevents ASDF from exporting RUN-PROGRAM, but
should not be a problem with ASDF-USER :use'ing UIOP.
A run of quicklisp is necessary, with an eye also for warnings
from .asd's that might be overriding definitions in UIOP(!)
* have a mode to explain WHY a component needs to be recompiled.
* have a better defsystem form verifier - see lp#1007335
* have a function verify-strict-asd that can verify a asd is pure lp#541562
* Improve robustness
** have a better defsystem form verifier - see lp#1007335
** have a function verify-strict-asd that can verify a asd is pure lp#541562
Then if it passes, use load-strict-asd.
If not, optionally issue a warning. Start migrating people toward that.
* have with-input-file use the encodings mechanism?
** Import asdf-encodings into UIOP? Sounds a lot of code for no users.
* have a single test .asd that tests as many features as possible, use it for upgrade test.
* implement deferred warnings support on lispworks
* work with cl-test-grid to eliminate deferred warnings from quicklisp libraries
* fix upgrade on clisp, cmucl, abcl (if still broken)
** Extract minimal test case, build a battery of automated tests
* Bug found by fe[nl]ix: infinite loop if the definitions in an asd file
are not in strict defsystem-depends-on dependency order.
** At least document the issue.
** Real solution: defer parsing and evaluation of defsystem forms.
* Learn to use cl-grid-test, to make sure ASDF changes don't break stuff,
and that breakage gets fixed quickly.
* Include some ABL test for stassats's thing:
* Include some ABL test for stassats's (now obsolete?) thing:
(asdf:enable-asdf-binary-locations-compatibility
:centralize-lisp-binaries t :default-toplevel-directory *fasl-dir*)
:centralize-lisp-binaries t :default-toplevel-directory
*fasl-dir*)
** Or deprecate and remove ABL compatibility altogether?
No one has been using ASDF1 for years.
There is no ABL to be compatible with —
is anyone still using the compatibility mode?
Maybe issue a warning, then a cerror, before to remove the code?
* Make load-op a generic operation that selects the proper strategy
for each system, module or file, according to component properties and user-configuration:
compile the lisp file then load the fasl (load-fasl-op on a cl-source-file),
......@@ -35,9 +209,38 @@
concatenate all files for the system, then load the result (load-concatenated-source-op),
concatenate all files for the system, then link them and load the result (load-compiled-concatenated-op),
concatenate all files for the system *and* its dependencies, link and load (monolithic-load-compiled-concatenated-op).
* operation cleanup?
** Kill backward-compat functions after all clients have moved on.
** Kill original-initargs -- BEWARE, it currently has clients!
** To allow semantically distinct operations of the same class:
You'd need to have a protocol to canonicalize them
in the *OPERATIONS* memoization table, not by class name,
but by CONS of the class name and some CANONICAL-OPERATION-INITARGS.
The latter would be a generic function called on the initargs,
where any parasite initargs such as FORCE and FORCE-NOT have been removed,
since they below to the PLAN, not the OPERATION:
the OPERATE protocol would be refined to explicit split
arguments to be passed to MAKE-PLAN or to MAKE-OPERATION.
The default method for CANONICAL-OPERATION-INITARGS
would SORT (a plist->alist of) the initargs,
and that would replace the current ORIGINAL-INITARGS slot.
For this scheme to work even in presence of undisciplined users
using MAKE-INSTANCE on an operation class,
the OPERATION class would have an extra slot EFFECTIVE-OPERATION,
uninitialized by default (nil or unbound), whose accessor initializes it
if it's uninitialized, by looking up a canonical instance in *OPERATIONS*,
and if unfound registering the current operation as canonical.
Then, each component's COMPONENT-OPERATION-TIME hash-table
would be indexed by canonicalized operation object
rather than by operation class,
and POIU would have to be changed accordingly.
Of course, this entire cleanup is incompatible
with how SWANK and GBBopen currently abuse slots of operation,
so these would have to be fixed first.
And that's why I didn't do it.
It looks like SWANK can be fixed soon, though, so we'll see.
* Get rid of component-properties
** directly use component-properties:
*** yaclml.asd => misguided :features for version comparison
......@@ -109,6 +312,19 @@
*** com.clearly-useful.generic-collection-interface => :com.clearly-useful
*** metatilities
=> :ait-timeout :system-applicable-p
*** ucw ucw-core
*** ucw ucw-core
=>
version
* ASDF4: search for this tag, rename things (incompatibly, thus) and cleanup code.
* See message from 2014-01-27 05:26:44 GMT for stuff to document.
http://thread.gmane.org/gmane.lisp.asdf.devel/3675/focus=3695
* deferred-warnings support probably needs to be redone better.
* implement deferred warnings support on lispworks
* turn undefined function style-warnings into full warnings(?)
* work with cl-test-grid to eliminate deferred warnings from quicklisp libraries
* https://www.mail-archive.com/asdf-devel@common-lisp.net/msg03608.html
* czak@google.com rewrote part of deferred warnings support.
Ask him for the code he implemented at Google.
;;;; -------------------------------------------------------------------------
;;;; Actions
(asdf/package:define-package :asdf/action
(uiop/package:define-package :asdf/action
(:nicknames :asdf-action)
(:recycle :asdf/action :asdf)
(:use :uiop/common-lisp :uiop :asdf/upgrade
......@@ -9,18 +9,27 @@
(:export
#:action #:define-convenience-action-methods
#:explain #:action-description
#:downward-operation #:upward-operation #:sideway-operation #:selfward-operation
#:downward-operation #:upward-operation #:sideway-operation #:selfward-operation #:non-propagating-operation
#:component-depends-on
#:input-files #:output-files #:output-file #:operation-done-p
#:action-status #:action-stamp #:action-done-p
#:component-operation-time #:mark-operation-done #:compute-action-stamp
#:perform #:perform-with-restarts #:retry #:accept
#:traverse-actions #:traverse-sub-actions #:required-components ;; in plan
#:action-path #:find-action #:stamp #:done-p))
#:action-path #:find-action #:stamp #:done-p
#:operation-definition-warning #:operation-definition-error ;; condition
#:build-op ;; THE generic operation
))
(in-package :asdf/action)
(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute)
(deftype action () '(cons operation component))) ;; a step to be performed while building
(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute) ;; LispWorks issues spurious warning
(deftype action () '(cons operation component)) ;; a step to be performed while building
(deftype operation-designator ()
;; an operation designates itself,
;; nil designates a context-dependent current operation, and
;; class-name or class designates an instance of the designated class.
'(or operation null symbol class)))
(with-upgradability ()
(defgeneric traverse-actions (actions &key &allow-other-keys))
......@@ -106,7 +115,14 @@ You can put together sentences using this phrase."))
and each <component> is a component designator with respect to
FIND-COMPONENT in the context of the COMPONENT argument,
and means that the component depends on
<operation> having been performed on each <component>; or
<operation> having been performed on each <component>;
[Note: an <operation> is an operation designator -- it can be either an
operation name or an operation object. Similarly, a <component> may be
a component name or a component object. Also note that, the degenerate
case of (<operation>) is a no-op.]
or
(FEATURE <feature>), which means that the component depends
on the <feature> expression satisfying FEATUREP.
......@@ -118,54 +134,135 @@ You can put together sentences using this phrase."))
(defmethod component-depends-on :around ((o operation) (c component))
(do-asdf-cache `(component-depends-on ,o ,c)
(call-next-method)))
(defmethod component-depends-on ((o operation) (c component))
(cdr (assoc (type-of o) (component-in-order-to c))))) ; User-specified in-order dependencies
(call-next-method))))
;;;; upward-operation, downward-operation
;; These together handle actions that propagate along the component hierarchy.
;; Downward operations like load-op or compile-op propagate down the hierarchy:
;; operation on a parent depends-on operation on its children.
;; By default, an operation propagates itself, but it may propagate another one instead.
;;;; upward-operation, downward-operation, sideway-operation, selfward-operation
;; These together handle actions that propagate along the component hierarchy or operation universe.
(with-upgradability ()
(defclass downward-operation (operation)
((downward-operation
:initform nil :initarg :downward-operation :reader downward-operation :allocation :class)))
:initform nil :reader downward-operation
:type operation-designator :allocation :class))
(:documentation "A DOWNWARD-OPERATION's dependencies propagate down the component hierarchy.
I.e., if O is a DOWNWARD-OPERATION and its DOWNWARD-OPERATION slot designates operation D, then
the action (O . M) of O on module M will depends on each of (D . C) for each child C of module M.
The default value for slot DOWNWARD-OPERATION is NIL, which designates the operation O itself.
E.g. in order for a MODULE to be loaded with LOAD-OP (resp. compiled with COMPILE-OP), all the
children of the MODULE must have been loaded with LOAD-OP (resp. compiled with COMPILE-OP."))
(defun downward-operation-depends-on (o c)
`((,(or (downward-operation o) o) ,@(component-children c))))
(defmethod component-depends-on ((o downward-operation) (c parent-component))
`((,(or (downward-operation o) o) ,@(component-children c)) ,@(call-next-method)))
;; Upward operations like prepare-op propagate up the component hierarchy:
;; operation on a child depends-on operation on its parent.
;; By default, an operation propagates itself, but it may propagate another one instead.
`(,@(downward-operation-depends-on o c) ,@(call-next-method)))
(defclass upward-operation (operation)
((upward-operation
:initform nil :initarg :downward-operation :reader upward-operation :allocation :class)))
:initform nil :reader upward-operation
:type operation-designator :allocation :class))
(:documentation "An UPWARD-OPERATION has dependencies that propagate up the component hierarchy.
I.e., if O is an instance of UPWARD-OPERATION, and its UPWARD-OPERATION slot designates operation U,
then the action (O . C) of O on a component C that has the parent P will depends on (U . P).
The default value for slot UPWARD-OPERATION is NIL, which designates the operation O itself.
E.g. in order for a COMPONENT to be prepared for loading or compiling with PREPARE-OP, its PARENT
must first be prepared for loading or compiling with PREPARE-OP."))
;; For backward-compatibility reasons, a system inherits from module and is a child-component
;; so we must guard against this case. ASDF4: remove that.
(defun upward-operation-depends-on (o c)
(if-let (p (component-parent c)) `((,(or (upward-operation o) o) ,p))))
(defmethod component-depends-on ((o upward-operation) (c child-component))
`(,@(if-let (p (component-parent c))
`((,(or (upward-operation o) o) ,p))) ,@(call-next-method)))
;; Sibling operations propagate to siblings in the component hierarchy:
;; operation on a child depends-on operation on its parent.
;; By default, an operation propagates itself, but it may propagate another one instead.
`(,@(upward-operation-depends-on o c) ,@(call-next-method)))
(defclass sideway-operation (operation)
((sideway-operation
:initform nil :initarg :sideway-operation :reader sideway-operation :allocation :class)))
(defmethod component-depends-on ((o sideway-operation) (c component))
:initform nil :reader sideway-operation
:type operation-designator :allocation :class))
(:documentation "A SIDEWAY-OPERATION has dependencies that propagate \"sideway\" to siblings
that a component depends on. I.e. if O is a SIDEWAY-OPERATION, and its SIDEWAY-OPERATION slot
designates operation S (where NIL designates O itself), then the action (O . C) of O on component C
depends on each of (S . D) where D is a declared dependency of C.
E.g. in order for a COMPONENT to be prepared for loading or compiling with PREPARE-OP,
each of its declared dependencies must first be loaded as by LOAD-OP."))
(defun sideway-operation-depends-on (o c)
`((,(or (sideway-operation o) o)
,@(loop :for dep :in (component-sideway-dependencies c)
:collect (resolve-dependency-spec c dep)))
,@(call-next-method)))
;; Selfward operations propagate to themselves a sub-operation:
;; they depend on some other operation being acted on the same component.
:collect (resolve-dependency-spec c dep)))))
(defmethod component-depends-on ((o sideway-operation) (c component))
`(,@(sideway-operation-depends-on o c) ,@(call-next-method)))
(defclass selfward-operation (operation)
((selfward-operation
:initform nil :initarg :selfward-operation :reader selfward-operation :allocation :class)))
;; NB: no :initform -- if an operation depends on others, it must explicitly specify which
:type (or operation-designator list) :reader selfward-operation :allocation :class))
(:documentation "A SELFWARD-OPERATION depends on another operation on the same component.
I.e., if O is a SELFWARD-OPERATION, and its SELFWARD-OPERATION designates a list of operations L,
then the action (O . C) of O on component C depends on each (S . C) for S in L.
E.g. before a component may be loaded by LOAD-OP, it must have been compiled by COMPILE-OP.
A operation-designator designates a singleton list of the designated operation;
a list of operation-designators designates the list of designated operations;
NIL is not a valid operation designator in that context. Note that any dependency
ordering between the operations in a list of SELFWARD-OPERATION should be specified separately
in the respective operation's COMPONENT-DEPENDS-ON methods so that they be scheduled properly."))
(defun selfward-operation-depends-on (o c)
(loop :for op :in (ensure-list (selfward-operation o)) :collect `(,op ,c)))
(defmethod component-depends-on ((o selfward-operation) (c component))
`(,@(loop :for op :in (ensure-list (selfward-operation o))
:collect `(,op ,c))
,@(call-next-method))))
`(,@(selfward-operation-depends-on o c) ,@(call-next-method)))
(defclass non-propagating-operation (operation)
()
(:documentation "A NON-PROPAGATING-OPERATION is an operation that propagates
no dependencies whatsoever. It is supplied in order that the programmer be able
to specify that s/he is intentionally specifying an operation which invokes no