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


6.5 The package-inferred-system extension

Starting with release 3.1.2, ASDF supports a one-package-per-file style of programming, in which each file is its own system, and dependencies are deduced from the defpackage form or its variant, uiop:define-package.

In this style of system definition, package names map to systems with the same name (in lower case letters), and if a system is defined with :class package-inferred-system, then system names that start with that name (using the slash / separator) refer to files under the filesystem hierarchy where the system is defined. For instance, if system my-lib is defined in /foo/bar/my-lib/my-lib.asd, then system my-lib/src/utility will be found in file /foo/bar/my-lib/src/utility.lisp.

One package per file style was made popular by faslpath and quick-build, and at the cost of stricter package discipline, may yield more maintainable code. This style is used in ASDF itself (starting with ASDF 3), by lisp-interface-library, and a few other libraries.

To use this style, choose a toplevel system name, e.g. my-lib, and create a file my-lib.asd. Define my-lib using the :class :package-inferred-system option in its defsystem. For instance:

;; This example is based on lil.asd of LISP-INTERFACE-LIBRARY.

#-asdf3.1 (error "MY-LIB requires ASDF 3.1 or later.")
(defsystem "my-lib"
  :class :package-inferred-system
  :depends-on ("my-lib/interface/all"
                        "my-lib/src/all"
                        "my-lib/extras/all")
  :in-order-to ((test-op (load-op "my-lib/test/all")))
  :perform (test-op (o c) (symbol-call :my-lib/test/all :test-suite)))

(defsystem "my-lib/test" :depends-on ("my-lib/test/all"))

(register-system-packages "my-lib/interface/all" '(:my-lib-interface))
(register-system-packages "my-lib/src/all" '(:my-lib-implementation))
(register-system-packages "my-lib/test/all" '(:my-lib-test))

(register-system-packages
 "closer-mop"
 '(:c2mop
   :closer-common-lisp
   :c2cl
   :closer-common-lisp-user
   :c2cl-user))

In the code above, the first form checks that we are using ASDF 3.1 or later, which provides package-inferred-system. This is probably no longer necessary, since none of the major lisp implementations provides an older version of ASDF.

The function register-system-packages must be called to register packages used or provided by your system when the name of the system/file that provides the package is not the same as the package name (converted to lower case).

Each file under the my-lib hierarchy will start with a package definition. The form uiop:define-package is supported as well as defpackage. ASDF will compute dependencies from the :use, :mix, and other importation clauses of this package definition. Take the file interface/order.lisp as an example:

(uiop:define-package :my-lib/interface/order
  (:use :closer-common-lisp
   :my-lib/interface/definition
   :my-lib/interface/base)
  (:mix :fare-utils :uiop :alexandria)
  (:export ...))

ASDF can tell that this file/system depends on system closer-mop (registered above), my-lib/interface/definition, and my-lib/interface/base.

How can ASDF find the file interface/order.lisp from the toplevel system my-lib, however? In the example above, interface/all.lisp (and other all.lisp) reexport all the symbols exported from the packages at the same or lower levels of the hierarchy. This can be easily done with uiop:define-package, which has many options that prove useful in this context. For example:

(uiop:define-package :my-lib/interface/all
  (:nicknames :my-lib-interface)
  (:use :closer-common-lisp)
  (:mix :fare-utils :uiop :alexandria)
  (:use-reexport
   :my-lib/interface/definition
   :my-lib/interface/base
   :my-lib/interface/order
   :my-lib/interface/monad/continuation))

Thus the top level system need only depend on the my-lib/.../all systems because ASDF detects interface/order.lisp and all other dependencies from all systems’ :use-reexport clauses, which effectively allow for “inheritance” of symbols being exported.

ASDF also detects dependencies from :import-from clauses. You may thus import a well-defined set of symbols from an existing package, and ASDF will know to load the system that provides that package. In the following example, ASDF will infer that the current system depends on foo/baz from the first :import-from. If you prefer to use any such symbol fully qualified by a package prefix, you may declare a dependency on such a package and its corresponding system via an :import-from clause with an empty list of symbols. For example, if we preferred to use the name ‘foo/quux:bletch‘, the second, empty, :import-from form would cause ASDF to load foo/quux.

(defpackage :foo/bar
  (:use :cl)
  (:import-from :foo/baz #:sym1 #:sym2)
  (:import-from :foo/quux)
  (:export ...))

Note that starting with ASDF 3.1.5.6 only, ASDF will look for source files under the component-pathname (specified via the :pathname option), whereas earlier versions ignore this option and use the system-source-directory where the .asd file resides.


Previous: Other code in .asd files, Up: Defining systems with defsystem   [Contents][Index]