Next: Other code in .asd files, Previous: A more involved example, Up: Defining systems with defsystem
system-definition := ( defsystem system-designator system-option* ) system-option := :defsystem-depends-on system-list | :weakly-depends-on system-list | :class class-name (see discussion below) | module-option | option module-option := :components component-list | :serial [ t | nil ] option := | :pathname pathname-specifier | :default-component-class class-name | :perform method-form | :explain method-form | :output-files method-form | :operation-done-p method-form | :if-feature feature-expression | :depends-on ( dependency-def* ) | :in-order-to ( dependency+ ) system-list := ( simple-component-name* ) component-list := ( component-def* ) component-def := ( component-type simple-component-name option* ) component-type := :module | :file | :static-file | other-component-type other-component-type := symbol-by-name (see Component types) # This is used in :depends-on, as opposed to ``dependency,'' # which is used in :in-order-to dependency-def := simple-component-name | (feature feature-name) | ( :version simple-component-name version-specifier) # ``dependency'' is used in :in-order-to, as opposed to # ``dependency-def'' dependency := (dependent-op requirement+) requirement := (required-op required-component+) | (:feature feature-name) dependent-op := operation-name required-op := operation-name simple-component-name := string | symbol pathname-specifier := pathname | string | symbol method-form := (operation-name qual lambda-list &rest body) qual := method qualifier component-dep-fail-option := :fail | :try-next | :ignore feature-expression := keyword | (:and feature-expression*) | (:or feature-expression*) | (:not feature-expression)
Component names (simple-component-name
)
may be either strings or symbols.
Component type names, even if expressed as keywords, will be looked up
by name in the current package and in the asdf package, if not found in
the current package. So a component type my-component-type
, in
the current package my-system-asd
can be specified as
:my-component-type
, or my-component-type
.
system
and its subclasses are not
allowed as component types for such children components.
A system class name will be looked up
in the same way as a Component type (see above),
except that only system
and its subclasses are allowed.
Typically, one will not need to specify a system
class name, unless using a non-standard system class defined in some
ASDF extension, typically loaded through DEFSYSTEM-DEPENDS-ON
,
see below. For such class names in the ASDF package, we recommend that
the :class
option be specified using a keyword symbol, such as
:class :MY-NEW-SYSTEM-SUBCLASS
This practice will ensure that package name conflicts are avoided.
Otherwise, the symbol MY-NEW-SYSTEM-SUBCLASS
will be read into
the current package before it has been exported from the ASDF
extension loaded by :defsystem-depends-on
, causing a name
conflict in the current package.
The :defsystem-depends-on
option to defsystem
allows the
programmer to specify another ASDF-defined system or set of systems that
must be loaded before the system definition is processed.
Typically this is used to load an ASDF extension that is used in the
system definition.
We do NOT recommend you use this feature. If you are tempted to write a system foo that weakly-depends-on a system bar, we recommend that you should instead write system foo in a parametric way, and offer some special variable and/or some hook to specialize its behavior; then you should write a system foo+bar that does the hooking of things together.
The (deprecated) :weakly-depends-on
option to defsystem
allows the programmer to specify another ASDF-defined system or set of systems
that ASDF should try to load,
but need not load in order to be successful.
Typically this is used if there are a number of systems
that, if present, could provide additional functionality,
but which are not necessary for basic function.
Currently, although it is specified to be an option only to defsystem
,
this option is accepted at any component, but it probably
only makes sense at the defsystem
level.
Programmers are cautioned not
to use this component option except at the defsystem
level, as
this anomalous behavior may be removed without warning.
Finally, you might look into the asdf-system-connections
extension,
that will let you define additional code to be loaded
when two systems are simultaneously loaded.
It may or may not be considered good style, but at least it can be used
in a way that has deterministic behavior independent of load order,
unlike weakly-depends-on
.
A pathname specifier (pathname-specifier
)
may be a pathname, a string or a symbol.
When no pathname specifier is given for a component,
which is the usual case, the component name itself is used.
If a string is given, which is the usual case,
the string will be interpreted as a Unix-style pathname
where /
characters will be interpreted as directory separators.
Usually, Unix-style relative pathnames are used
(i.e. not starting with /
, as opposed to absolute pathnames);
they are relative to the path of the parent component.
Finally, depending on the component-type
,
the pathname may be interpreted as either a file or a directory,
and if it's a file,
a file type may be added corresponding to the component-type
,
or else it will be extracted from the string itself (if applicable).
For instance, the component-type
:module
wants a directory pathname, and so a string "foo/bar"
will be interpreted as the pathname #p"foo/bar/".
On the other hand, the component-type
:file
wants a file of type lisp
, and so a string "foo/bar"
will be interpreted as the pathname #p"foo/bar.lisp",
and a string "foo/bar.quux"
will be interpreted as the pathname #p"foo/bar.quux.lisp".
Finally, the component-type
:static-file
wants a file without specifying a type, and so a string "foo/bar"
will be interpreted as the pathname #p"foo/bar",
and a string "foo/bar.quux"
will be interpreted as the pathname #p"foo/bar.quux".
ASDF does not interpret the string ".."
to designate the parent
directory. This string will be passed through to the underlying
operating system for interpretation. We believe that this will
work on all platforms where ASDF is deployed, but do not guarantee this
behavior. A pathname object with a relative directory component of
:up
or :back
is the only guaranteed way to specify a
parent directory.
If a symbol is given, it will be translated into a string,
and downcased in the process.
The downcasing of symbols is unconventional,
but was selected after some consideration.
Observations suggest that the type of systems we want to support
either have lowercase as customary case (Unix, Mac, windows)
or silently convert lowercase to uppercase (lpns),
so this makes more sense than attempting to use :case :common
as argument to make-pathname
,
which is reported not to work on some implementations.
Pathname objects may be given to override the path for a component.
Such objects are typically specified using reader macros such as #p
or #.(make-pathname ...)
.
Note however, that #p...
is a shorthand for #.(parse-namestring ...)
and that the behavior of parse-namestring
is completely non-portable,
unless you are using Common Lisp logical-pathname
s
(see Using logical pathnames, below).
Pathnames made with #.(make-pathname ...)
can usually be done more easily with the string syntax above.
The only case that you really need a pathname object is to override
the component-type default file type for a given component.
Therefore, pathname objects should only rarely be used.
Unhappily, ASDF 1 didn't properly support
parsing component names as strings specifying paths with directories,
and the cumbersome #.(make-pathname ...)
syntax had to be used.
An alternative to #.
read-time evaluation is to use
(eval `(defsystem ... ,pathname ...))
.
Note that when specifying pathname objects, ASDF does not do any special interpretation of the pathname influenced by the component type, unlike the procedure for pathname-specifying strings. On the one hand, you have to be careful to provide a pathname that correctly fulfills whatever constraints are required from that component type (e.g. naming a directory or a file with appropriate type); on the other hand, you can circumvent the file type that would otherwise be forced upon you if you were specifying a string.
Version specifiers are strings to be parsed as period-separated lists of integers.
I.e., in the example, "0.2.1"
is to be interpreted,
roughly speaking, as (0 2 1)
.
In particular, version "0.2.1"
is interpreted the same as "0.0002.1"
,
though the latter is not canonical and may lead to a warning being issued.
Also, "1.3"
and "1.4"
are both strictly uiop:version<
to "1.30"
,
quite unlike what would have happened
had the version strings been interpreted as decimal fractions.
System definers are encouraged to use version identifiers of the form x.y.z for major version, minor version and patch level, where significant API incompatibilities are signaled by an increased major number.
See Common attributes of components.
We do not generally recommend the use of logical pathnames, especially not so to newcomers to Common Lisp. However, we do support the use of logical pathnames by old timers, when such is their preference.
To use logical pathnames,
you will have to provide a pathname object as a :pathname
specifier
to components that use it, using such syntax as
#p"LOGICAL-HOST:absolute;path;to;component.lisp"
.
You only have to specify such logical pathname for your system or some top-level component. Sub-components' relative pathnames, specified using the string syntax for names, will be properly merged with the pathnames of their parents. The specification of a logical pathname host however is not otherwise directly supported in the ASDF syntax for pathname specifiers as strings.
The asdf-output-translation
layer will
avoid trying to resolve and translate logical pathnames.
The advantage of this is that
you can define yourself what translations you want to use
with the logical pathname facility.
The disadvantage is that if you do not define such translations,
any system that uses logical pathnames will behave differently under
asdf-output-translations than other systems you use.
If you wish to use logical pathnames you will have to configure the translations yourself before they may be used. ASDF currently provides no specific support for defining logical pathname translations.
Note that the reasons we do not recommend logical pathnames are that (1) there is no portable way to set up logical pathnames before they are used, (2) logical pathnames are limited to only portably use a single character case, digits and hyphens. While you can solve the first issue on your own, describing how to do it on each of fifteen implementations supported by ASDF is more than we can document. As for the second issue, mind that the limitation is notably enforced on SBCL, and that you therefore can't portably violate the limitations but must instead define some encoding of your own and add individual mappings to name physical pathnames that do not fit the restrictions. This can notably be a problem when your Lisp files are part of a larger project in which it is common to name files or directories in a way that includes the version numbers of supported protocols, or in which files are shared with software written in different programming languages where conventions include the use of underscores, dots or CamelCase in pathnames.
If the :serial t
option is specified for a module,
ASDF will add dependencies for each child component,
on all the children textually preceding it.
This is done as if by :depends-on
.
:serial t :components ((:file "a") (:file "b") (:file "c"))
is equivalent to
:components ((:file "a") (:file "b" :depends-on ("a")) (:file "c" :depends-on ("a" "b")))
The :pathname
option is optional in all cases for systems
defined via defsystem
,
and in the usual case the user is recommended not to supply it.
Instead, ASDF follows a hairy set of rules that are designed so that
find-system
will load a system from disk
and have its pathname default to the right place.
*default-pathname-defaults*
(which could be somewhere else altogether)
if the user loads up the .asd file into his editor
and interactively re-evaluates that form.
If a system is being loaded for the first time, its top-level pathname will be set to:
*load-truename*
,
if it is bound.
*default-pathname-defaults*
, otherwise.
If a system is being redefined, the top-level pathname will be
*load-truename*
(so that an updated source location is reflected in the system definition)
*default-pathname-defaults*
*load-truename*
and *load-truename*
is currently unbound
(so that a developer can evaluate a defsystem
form
from within an editor without clobbering its source location)
This option allows you to specify a feature expression to be evaluated
as if by #+
to conditionally include a component in your build.
If the expression is false, the component is dropped
as well as any dependency pointing to it.
As compared to using #+
which is expanded at read-time,
this allows you to have an object in your component hierarchy
that can be used for manipulations beside building your project.
This option was added in ASDF 3.
This option was removed in ASDF 3.
Its semantics was limited in purpose and dubious to explain,
and its implementation was breaking a hole into the ASDF object model.
Please use the if-feature
option instead.