Next: , Previous: , Up: Defining systems with defsystem   [Contents][Index]


6.3 The defsystem grammar

system-definition := ( defsystem system-designator system-option* )

system-designator := simple-component-name
                   | complex-component-name

# NOTE: Underscores are not permitted.
# see Simple component names
simple-component-name := lower-case string | symbol

# see Complex component names
complex-component-name := string | symbol

system-option := :defsystem-depends-on dependency-def
               | :weakly-depends-on system-list
               | :class class-name # see System class names
               | :build-pathname pathname-specifier
               | :build-operation operation-name
               | system-option/asdf3
               | module-option
               | option

# These are only available since ASDF 3 (actually its alpha release
# 2.27)
system-option/asdf3 := :homepage string
                     | :bug-tracker string
                     | :mailto string
                     | :long-name string
                     | :source-control source-control
                     | :version version-specifier
                     | :entry-point object # see Entry point

source-control := ( keyword string )

module-option := :components component-list
               | :serial [ t | nil ]

option := :description string
        | :long-description string
        | :author person-or-persons
        | :maintainer person-or-persons
        | :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+ )

person-or-persons := string | ( string+ )

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-expression dependency-def ) # see Feature dependencies
                | ( :version simple-component-name version-specifier )
                | ( :require module-name )

# "dependency" is used in :in-order-to, as opposed to "dependency-def"
dependency := ( dependent-op requirement+ )
requirement := ( required-op required-component+ )
dependent-op := operation-name
required-op := operation-name

# NOTE: pathnames should be all lower case, and have no underscores,
# although hyphens are permitted.
pathname-specifier := pathname | string | symbol

version-specifier := string
                   | ( :read-file-form pathname-specifier form-specifier? )
                   | ( :read-file-line pathname-specifier line-specifier? )
line-specifier := :at integer # base zero
form-specifier := :at [ integer | ( integer+ ) ]

method-form := ( operation-name qual lambda-list &rest body )
qual := method-qualifier?
method-qualifier := :before | :after | :around

feature-expression := keyword
                    | ( :and feature-expression* )
                    | ( :or feature-expression* )
                    | ( :not feature-expression )

operation-name := symbol

6.3.1 System designators

System designators are either simple component names, or complex (“slashy”) component names.

6.3.2 Simple component names (simple-component-name)

Simple component names may be written as either strings or symbols.

When using strings, use lower case exclusively.

Symbols will be interpreted as convenient shorthand for the string that is their symbol-name, converted to lower case. Put differently, a symbol may be a simple component name designator, but the simple component name itself is the string.

Never use underscores in component names, whether written as strings or symbols.

Never use slashes (“/”) in simple component names. A slash indicates a complex component name; see below. Using a slash improperly will cause ASDF to issue a warning.

Violating these constraints by mixing case, or including underscores in component names, may lead to systems or components being impossible to find, because component names are interpreted as file names. These problems will definitely occur for users who have configured ASDF using logical pathnames.

6.3.3 Complex component names

A complex component name is a master name followed by a slash, followed by a subsidiary name. This allows programmers to put multiple system definitions in a single .asd file, while still allowing ASDF to find these systems.

The master name of a complex system must be the same as the name of the .asd file.

The file foo.asd will contain the definition of the system "foo". However, it may also contain definitions of subsidiary systems, such as "foo/test", "foo/docs", and so forth. ASDF will “know” that if you ask it to load system "foo/test" it should look for that system’s definition in foo.asd.

6.3.4 Component types

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.

6.3.5 System class names

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.

6.3.6 Defsystem depends on

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.

6.3.7 Build-operation

The :build-operation option to defsystem allows the programmer to specify an operation that will be applied, in place of load-op when make (see make) is run on the system. The option value should be the name of an operation. E.g., :build-operation doc-op

This feature is experimental and largely untested. Use at your own risk.

6.3.8 Weakly depends on

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 behaviour; 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 behaviour may be removed without warning.

6.3.9 Pathname specifiers

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 interprets the string ".." as the pathname directory component word :back, which when merged, goes back one level in the directory hierarchy.

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. The file systems we 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.

Please avoid using underscores in system names, or component (module or file) names, since underscores are not compatible with logical pathnames (see Using logical pathnames).

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 behaviour of parse-namestring is completely non-portable, unless you are using Common Lisp logical-pathnames, which themselves involve other non-portable behaviour (see Using logical pathnames). 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 used not to 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.

6.3.10 Version specifiers

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.

Instead of a string representing the version, the :version argument can be an expression that is resolved to such a string using the following trivial domain-specific language: in addition to being a literal string, it can be an expression of the form (:read-file-form <pathname-or-string> [:at <access-at-specifier>]), or (:read-file-line <pathname-or-string> [:at <access-at-specifier>]). As the name suggests, the former will be resolved by reading a form in the specified pathname (read as a subpathname of the current system if relative or a unix-namestring), and the latter by reading a line. You may use a uiop:access-at specifier with the :at keyword, by default the specifier is 0, meaning the first form/line is returned. For :read-file-form, subforms can also be specified, with e.g. (1 2 2) specifying “the third subform (index 2) of the third subform (index 2) of the second form (index 1)” in the file (mind the off-by-one error in the English language).

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.

6.3.11 Require

Use the implementation’s own require to load the module-name.

It is good taste to use (:feature :implementation-name (:require module-name)) rather than #+implementation-name (:require module-name) to only depend on the specified module on the specific implementation that provides it. See Feature dependencies.

6.3.12 Feature dependencies

A feature dependency is of the form (:feature feature-expression dependency) If the feature-expression is satisfied by the running lisp at the time the system definition is parsed, then the dependency will be added to the system’s dependencies. If the feature-expression is not satisfied, then the feature dependency form is ignored.

Note that this means that :feature cannot be used to enforce a feature dependency for the system in question. I.e., it cannot be used to require that a feature hold in order for the system definition to be loaded. E.g., one cannot use (:feature :sbcl) to require that a system only be used on SBCL.

Feature dependencies are not to be confused with the obsolete feature requirement (see feature requirement), or with if-feature.

6.3.13 Using logical pathnames

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.

6.3.14 Serial dependencies

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")))

6.3.15 Source location (:pathname)

The :pathname option is optional in all cases for systems defined via defsystem, and generally is unnecessary. In the simple case, source files will be found in the same directory as the system or, in the case of modules, in a subdirectory with the same name as the module.

More specifically, ASDF follows a hairy set of rules that are designed so that

  1. find-system will load a system from disk and have its pathname default to the right place.
  2. This pathname information will not be overwritten with *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:

If a system is being redefined, the top-level pathname will be

6.3.16 if-feature option

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, and that is accessible to outside code that wishes to reason about system structure.

Programmers should be careful to consider when the :if-feature is evaluated. Recall that ASDF first computes a build plan, and then executes that plan. ASDF will check to see whether or not a feature is present at planning time, not during the build. It follows that one cannot use :if-feature to check features that are set during the course of the build. It can only be used to check the state of features before any build operations have been performed.

This option was added in ASDF 3. For more information, See Required features.

6.3.17 Entry point

The :entry-point option allows a developer to specify the entry point of an executable program created by program-op.

When program-op is invoked, the form passed to this option is converted to a function by uiop:ensure-function and bound to uiop:*image-entry-point*. Typically one will specify a string, e.g. "foo:main", so that the executable starts with the foo:main function. Note that using the symbol foo:main instead might not work because the foo package doesn’t necessarily exist when ASDF reads the defsystem form. For more information on program-op, see Predefined operations of ASDF.

6.3.18 feature requirement

This requirement was removed in ASDF 3.1. Please do not use it. In most cases, :if-feature (see if-feature option) will provide an adequate substitute.

The feature requirement used to ensure that a chain of component dependencies would fail when a key feature was absent. Used in conjunction with :if-component-dep-fails this provided a roundabout way to express conditional compilation.


Next: Other code in .asd files, Previous: A more involved example, Up: Defining systems with defsystem   [Contents][Index]