ASDF was designed to be extensible in an object-oriented fashion.
To teach ASDF new tricks, a programmer can implement the behaviour he wants
by creating a subclass of operation
.
ASDF's pre-defined operations are in no way “privileged”,
but it is requested that developers never use the asdf
package
for operations they develop themselves.
The rationale for this rule is that we don't want to establish a
“global asdf operation name registry”,
but also want to avoid name clashes.
An operation must provide methods for the following generic functions
when invoked with an object of type source-file
:
FIXME describe this better
input-files
ASDF has a pretty clever default input-files
mechanism.
You only need create a method if there are multiple ultimate input files,
and/or the bottom one doesn't depend
on the component-pathname
of the component.
output-files
The output-files
method determines where the method will put its files.
It returns two values, a list of pathnames, and a boolean.
If the boolean is T
then the pathnames are marked
not be translated by enclosing :around
methods.
If the boolean is NIL
then enclosing :around
methods
may translate these pathnames, e.g. to ensure object files
are somehow stored in some implementation-dependent cache.
perform
The perform
method must call output-files
to find out where to put its files,
because the user is allowed to override.
output-files
for local policy explain
operation-done-p
You only need to define a method on that function
if you can detect conditions that invalidate previous runs of the operation,
even though no filesystem timestamp has changed,
in which case you return nil
(the default is t
).
For instance, the method for test-op
always returns nil
,
so that tests are always run afresh.
Of course, the test-op
for your system could depend
on a deterministically repeatable test-report-op
,
and just read the results from the report files.
component-depends-on
When you add new operations, you probably need to explain
how they relate to loading, compiling, testing, etc.,
in terms of dependencies between actions.
That's where you typically define methods on component-depends-on
.
Your method will take as arguments
some properly specialized operation
and a component denoting a current action,
and return a list of entries,
denoting the children actions that the current action depends on.
The format of entries is described below.
It is strongly advised that
you should always append the results of (call-next-method)
to the results of your method,
or “interesting” failures will likely occur,
unless you're a true specialist of ASDF internals.
Each entry returned by component-depends-on
is itself a list.
The first element of an entry is the name of an operation:
a symbol that you can use with make-instance
(ASDF will instead use with asdf::make-sub-operation
),
to create a related operation for use in a build plan.
For instance, load-op
and compile-op
are common such names, denoting the respective operations.
The rest of an entry is a list of identifiers each denote a component such that the pair of the previous operation and this component is a children action of current action.
Identifiers follow the defsystem
grammar
previously documented.
The main format for identifiers is a string or symbol
(that will be downcase as per coerce-name
),
and looked up against the sibling list of the parent module's children components,
as per find-component
.
As a special case, nil
denotes the parent itself.
Other syntaxes are allowed, for instance to specify a component with a version.
Operations that print output should send that output to the standard
CL stream *standard-output*
, as the Lisp compiler and loader do.