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, whereby each file is its own system, and dependencies are deduced from the defpackage form (or its variant uiop:define-package).

In this style, packages refer to a system with the same name (downcased); 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.

This style was made popular by faslpath and quick-build before, and at the cost of a stricter package discipline, seems to make for more maintainable code. It is used by 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 with the :class :package-inferred-system option in its defsystem. For instance:

#-asdf3.1 (error "my-lib requires ASDF 3.1")
(defsystem "my-lib"
  :class :package-inferred-system
  :depends-on ("my-lib/interface/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))

 '(:c2mop :closer-common-lisp :c2cl :closer-common-lisp-user :c2cl-user))

In the code above, the first line checks that we are using ASDF 3.1, which provides package-inferred-system.

The function register-system-packages has to be called to register packages used or provided by your system and its components where the name of the system that provides the package is not the downcase of the package name.

Then, file interface/order.lisp under the lil hierarchy, that defines abstract interfaces for order comparisons, starts with the following form, dependencies being trivially computed from the :use and :mix clauses:

(uiop:define-package :lil/interface/order
  (:use :closer-common-lisp
   :lil/interface/eq :lil/interface/group)
  (:mix :fare-utils :uiop :alexandria)
  (:export ...))

ASDF can tell that this file depends on system closer-mop (registered above), lil/interface/definition, lil/interface/base, lil/interface/eq, and lil/interface/group (package and system names match, and they will be looked up hierarchically).

ASDF also detects dependencies from :import-from clauses. You may thus import a well-defined set of symbols from an existing package as loaded from suitably named system; or 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, as in:

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

The form uiop:define-package is supported as well as defpackage, and has many options that prove useful in this context, such as :use-reexport and :mix-reexport that allow for “inheritance” of symbols being exported.

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

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