Quicklisp library bundles

Overview

Quicklisp library bundles are self-contained sets of systems that are exported from Quicklisp and loadable without involving Quicklisp. A library bundle has a directory structure that includes software archives, an index file, and a Lisp file that can be loaded to configure ASDF to understand how to load systems from the bundle. It also includes a directory, initially empty, that works like the local-projects mechanism, whereby any system in the bundle's local-projects directory takes precedence over other systems in the bundle.

Bundling works only for systems available through Quicklisp. It cannot find and bundle external ASDF systems and their sources.

Creating a bundle

Bundles are created with the QL:BUNDLE-SYSTEMS function.

 * (ql:bundle-systems '("vecto" "zpb-exif") :to "my-bundle/")
=> #P"/Users/xach/my-bundle/bundle.lisp",
   #<QL-BUNDLE:BUNDLE 6 releases, 10 systems>
  

When creating a bundle for systems foo, bar, and baz, the directory structure looks like so:

The "bundled-local-projects/" subdirectory appears only if the include-local-projects argument to bundle-systems is true

Loading a bundle

In the following descriptions, loading a bundle refers to loading the bundle.lisp file created for a bundle.

When a bundle is loaded, the systems in the bundle are visible to ASDF and can be found via (asdf:find-system system-name) and loaded with (asdf:load-system system-name). Loading a bundle does not load all the bundle's systems — it just makes them available for later loading via ASDF.

By default, loading bundle.lisp will make the bundle's systems load before any other systems known to ASDF. This is done via ASDF's asdf:*system-definition-search-functions* mechanism, which controls how ASDF finds systems.

Neither Quicklisp nor ASDF are required to load a bundle. ASDF will be loaded on demand via CL:REQUIRE, and Quicklisp is not needed for loading the bundled system.

Available systems

A newly-created bundle provides the systems indexed by its system-index.txt file.

It also includes a directory, local-projects, into which files and directories may be placed. Any file in the local-projects directory tree matching "*.asd" is added as a loadable system. These systems take precedence over systems in the system index file; if a system named "foo" is available both in the index and the local-projects directory, the local-projects version is loaded.

If two system files in the local-projects directory tree have the same system name, the one with the shortest pathname namestring takes precedence. When the two pathnames have the same length, the one with the lesser (as determined by CL:STRING<) pathname takes precedence.

Loading more than one bundle

When more than one bundle is loaded, the systems of the most recently loaded bundle take precedence.

Reloading a bundle

A bundle can be loaded any number of times; each time it is loaded, its systems move to the front of the precedence list.

Overwriting a bundle

When given a to argument that points to an existing bundle, BUNDLE-SYSTEMS will delete and recreate the software directory, and overwrite bundle.lisp and system-index.txt. The local-projects directory and any other files in to are left unchanged.

When passed :overwrite nil, BUNDLE-SYSTEMS will signal a continuable error of type QL-BUNDLE:BUNDLE-DIRECTORY-EXISTS.

Detecting updates

When looking up a system, the bundle system search code checks the timestamp of system-index.txt file and the local-projects directory. If either has an updated filesystem timestamp, the index is reloaded or the directory is re-scanned before continuing with the system lookup.

This allows the automatic detection of simple changes to a bundle's contents. If this automatic detection fails to pick up an expected change, the bundle itself can simply be reloaded to force an update.

Disabling bundles

Bundles are implemented by adding a symbol named "QL-BUNDLE-SEARCHER" to ASDF's ASDF:*SYSTEM-DEFINITION-SEARCH-FUNCTIONS* list. To disable all bundles, that symbol can be removed from the list. Loading another bundle will re-initialize the bundle system from scratch.

Interface

[Function]
ql:bundle-systems system-names &key to overwrite include-local-projects => bundle-loader-pathname, bundle-object

Creates a system bundle for system-names in the directory to.

For each system named by system-names, and each system required by those systems, recursively, unpacks the release archive in the software/ subdirectory of to.

If any system is not available via Quicklisp, signals an error of type QL-BUNDLE:SYSTEM-NOT-FOUND

If overwrite is true (the default) and to already exists, deletes its software/ subdirectory before unpacking.

If overwrite is false and to already exists, signals a continuable error of type QL-BUNDLE:BUNDLE-ALREADY-EXISTS.

If include-local-projects is true, each directory in ql:*local-project-directories is copied into "bundled-local-projects/" subdirectory of the bundle. Each local project directory is assigned a number in sequence from 0, and copied into a subdirectory named after that number. The number is used to avoid clashing references to local projects, which may have arbitrary names on the filesystem.

Returns the pathname to the bundle loader file as its primary value, and the bundle object itself as the secondary value.

The bundle object can be used with QL-DIST:PROVIDED-SYSTEMS and QL-DIST:PROVIDED-RELEASES to get an exact list of its contents.

[Condition]
ql-bundle:bundle-directory-exists

This condition is signaled when QL:BUNDLE-SYSTEMS is given :overwrite nil and the to argument refers to a directory that already exists.

The directory in question can be accessed with the QL-BUNDLE:BUNDLE-DIRECTORY-EXISTS-DIRECTORY function.

[Condition]
ql-bundle:system-not-found

This condition is signaled when QL:BUNDLE-SYSTEMS is given a system name that is not available via Quicklisp.

The system name in question can be accessed with the QL-BUNDLE:SYSTEM-NOT-FOUND-SYSTEM function.