Faslpath is a build tool for Common Lisp that is intended to be very easy to use, even for beginner lisp programmers. It does not require separate build files. Instead, it relies on naming conventions to statically resolve dependencies between files. An illustrative example is probably the easiest way to explain how it works.
Suppose we have the following directory structure:
and that the files look like this
(defpackage :foo.foo (:use :cl :foo.util :bar.bar) (:export #:FOO)) ...
(defpackage :foo.util (:use :cl) (:export #:FOO-TOOL)) ...bar/bar.lisp:
(defpackage :bar.bar (:use :cl :bar.macros :bar.util) (:export #:BAR #:WITH-BAR)) ...
(defpackage :bar.util (:use :cl :bar.macros) (:export #:BAR-TOOL)) ...
(defpackage :bar.macros (:use :cl) (:export #:WITH-BAR)) ...
Now, given these files, we can call
This will tell the faslpath loader to compile the package :foo.foo. It will automatically figure out the dependencies of each file and compile them in the right order. It does so by examining the defpackage form in each file and assuming that a package named :bar.util will be found in the file bar/util.lisp. The only configuration that's required is to tell the faslpath loader about the "lib" directory by pushing #P/path/to/lib/ onto the faslpath:*faslpath* variable.
Faslpath is available at http://code.google.com/p/faslpath/
(load "/home/john/lisp/faslpath/faslpath.fasl") (use-package :faslpath) (setf *faslpath* '(#P"/home/john/lisp/"))
After these steps, code placed in /home/john/lisp/ and its subdirectories should be visible to the faslpath loader.
To make use of faslpath, simply name your source files according to their location and make sure that the first form in each file is a defpackage form. Dependencies are specified with the "USE" list in the defpackage form.
In the file my-app/main.lisp you need:
(defpackage :my-app.main (:use :cl :my-app.tools :my-app.macros :my-app.asdf-deps :my-external-lib.main) (:export #:RUN-MY-APP #:MAIN)) (in-package :my-app.main) [ ... Code follows ... ]
Now COMPILE-PACKAGE and LOAD-PACKAGE will know what the symbol "MY-APP.MAIN" means, and will be able to find it. One could now, for instance, run the shell command
$ invoke-lisp-package my-app.main
This would load the MY-APP.MAIN package and its dependencies and then call the MAIN function in that package.
Dependencies are extracted directly from the source files by mapping package names to source files. This enables us to "overload" the use list in defpackage to indicate file dependencies in addition to normal package dependencies. Currently, the mapping from packages to source files used is the familiar "dotted" notation, i.e. a package named "FOO.BAR.QWE" represents the relative pathname "foo/bar/qwe.lisp".
The faslpath loader determines the dependencies of a lisp file by probing the file for a defpackage form and resolving the packages in the use list according to the package->file mapping used. The probing is done by READing the first form in the file. As a result of this, the file does not get loaded.
In order for all of this to work we need to adopt a couple of conventions:
The faslpath:*faslpath* variable should contain a list of directory pathnames that should be searched when resolving dependencies. It is probably best to do set this this in the lisp init file, like this, for example:
(load "/path/to/faslpath.fasl") (use-package :faslpath) (setf *faslpath* '(#P/path/to/my/libs/ #P/path/to/some/other/libs/))
If you absolutely do not want to have a defpackage in each file, or if you need finer control over which symbols are imported into in a package, then you can specify dependencies with a quoted list as the first form in a file. This is a somewhat ugly hack, and may change in the future.
Symbols in the quoted list will be interpreted as package dependencies. They will be searched for in faslpath:*faslpath* and loaded if necessary. Note that a package is considered "loaded" if (find-package …) returns non-nil.
Strings in the quoted list will be interpreted as files, relative to the current file, which will need to be loaded (or compiled) before the current file is loaded. The files are assumed to belong to the same "unit" as the current file and will be loaded unconditionally if the current file ever becomes loaded.
'(:my-app.tools :my-app.macros :my-app.asdf-deps :my-external-lib.main "file-1" "file-2" "subdir/file-3" "subdir/file-4") (in-package :my-app.main) [ ... Code follows ... ]
The following symbols are exported by the FASLPATH package:
A list of directory pathnames that indicates where the root package directories are located.
Note: The current working directory is always searched for packages.
COMPILE-PACKAGE PACKAGE-SYMBOL &OPTIONAL TABULA-RASA MAKE-LOADER
Compiles and loads the package PACKAGE-SYMBOL along with its dependencies. If a file doesn't need to be compiled, then it will only be loaded.
If MAKE-LOADER is t then a package loader will be generated. The name of the loader file is of the form "foo-loader.lisp", assuming PACKAGE-SYMBOL ends with "FOO".
If TABULA-RASA is t then any package compiled or loaded will be cleared of all symbols, as if the package had just been created.
This will make faslpath look for the file "my-app/main.lisp" in the directories specified by faslpath:*faslpath* and compile it and its dependencies.
LOAD-PACKAGE PACKAGE-SYMBOL &OPTIONAL TABULA-RASA FORCE
Loads the package PACKAGE-SYMBOL along with its dependencies if it doesn't already exist. If a package loader is found, it will be used to avoid computing dependencies. If TABULA-RASA is t, then the package loader will be ignored and any package loaded will be cleared of all symbols, as if the package had just been created. If FORCE is t, then the package will be loaded even if it already exists. TABULA-RASA = t implies FORCE = t.
This will make faslpath look for the file "my-app/main-loader.fasl" in the directories specified by faslpath:*faslpath* and load it. If the file isn't found, then a dependency tree for the file "my-app/main.lisp" will be constructed and used to load the required fasl files. No files will be recompiled as a result of calling LOAD-PACKAGE.
COMPILE-PACKAGE-FROM-SCRATCH PACKAGE-SYMBOL &OPTIONAL TABULA-RASA
Unconditionally compiles and loads PACKAGE-SYMBOL and all of its dependencies.
A number of shell scripts are provided for convenience
Compiles the package named PACKAGE-NAME, then exits.
$ compile-lisp-package my-app.main
Loads the package named PACKAGE-NAME and saves the running lisp image.
Note: This script requires that faslpath can find its own directory.
Loads the package named PACKAGE-NAME and calls the function in that package specified by the symbol MAIN, then exits.
Starts the core named PACKAGE-NAME and calls the function in that package specified by the symbol MAIN, then exits.
Starts a lisp and loads the package named PACKAGE-NAME.
Starts the core named PACKAGE-NAME.
These scripts will give easy access to the lisp packages assuming faslpath is loaded by the lisp init file (or otherwise present in the running core).
Currently the following implementations have been tested:
Faslpath should be easily portable to other implementations.
In order to port it to another implementation the following is currently needed:
Date: 2009/04/16 01:07:51