diff --git a/.gitignore b/.gitignore index a4a40c84a619cecc0ba09f5692e396efb7478ada..4a7c338733b8606c319aff7a3eea1e2ac2511141 100644 --- a/.gitignore +++ b/.gitignore @@ -4,18 +4,31 @@ common-lisp.net .vcs init-lisp.lisp website/changelog.xml +push +resbcl +reccl -# Test stuff -test/results/ -test/tmp/ -test/conf.d/ -test/dir1/ -test/dir2/ +# Temporary files from documentation build +doc/asdf/ +doc/asdf.aux +doc/asdf.cp +doc/asdf.cps +doc/asdf.fn +doc/asdf.fns +doc/asdf.html +doc/asdf.info +doc/asdf.ky +doc/asdf.log +doc/asdf.pdf +doc/asdf.pg +doc/asdf.toc +doc/asdf.tp +doc/asdf.vr +doc/asdf.vrs # We build these at various stages in the make process LICENSE website/output/ -test-results/ tmp/ lift-local.config *.dribble diff --git a/GNUmakefile b/GNUmakefile index 2ea0973212131d2f61bd56ad0230754981c21641..ad952fd3640fe18a4db9b2cca9cf7287547c48d7 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -21,7 +21,6 @@ bump_revision: FORCE bin/bump-revision-and-tag.sh archive: FORCE - sbcl --userinit /dev/null --sysinit /dev/null --load bin/make-helper.lisp \ --eval "(rewrite-license)" --eval "(quit)" bin/build-tarball.sh @@ -33,12 +32,11 @@ archive-copy: archive git push cl.net git push --tags cl.net -website-copy: FORCE - bin/rsync-cp.sh website/output/ $(webhome_private) - bin/rsync-cp.sh tmp/asdf.lisp $(webhome_private) +website: + make -C doc website clean_dirs = $(sourceDirectory) -clean_extensions = fasl dfsl cfsl fasl fas lib dx32fsl lx64fsl lx32fsl o +clean_extensions = fasl dfsl cfsl fasl fas lib dx32fsl lx64fsl lx32fsl o bak clean: FORCE @for dir in $(clean_dirs); do \ @@ -52,6 +50,8 @@ clean: FORCE done; \ fi; \ done + rm -rf tmp + make -C doc clean test: FORCE @cd test; make clean;./run-tests.sh $(lisp) $(test-regex) diff --git a/README.asdf-output-translations b/README.asdf-output-translations deleted file mode 100644 index 61b702999d48e06b83ee33fa57ee110e9a5fe89d..0000000000000000000000000000000000000000 --- a/README.asdf-output-translations +++ /dev/null @@ -1,288 +0,0 @@ -======================== -ASDF Output Translations -======================== - -This file specifies how ASDF stores "binary" outputs for its operations, -typically Lisp FASL files, but also any other files -that may be generated, e.g. C files and executables from CFFI-GROVEL. - -Configurations -============== - -Configurations specify mappings from source locations to binary locations. - - 1- An application may explicitly initialize the output-translations - configuration using the `Configuration API`_ below, - in which case this takes precedence. - It may itself compute this configuration from the command-line, - from a script, from its own configuration file, etc. - - 2- The source registry will be configured from - the environment variable ``ASDF_OUTPUT_TRANSLATIONS`` if it exists. - - 3- The source registry will be configured from - user configuration file - ``~/.config/common-lisp/asdf-output-translations.conf`` - if it exists. - - 4- The source registry will be configured from - user configuration directory - ``~/.config/common-lisp/asdf-output-translations.conf.d/`` - if it exists. - - 5- The source registry will be configured from - system configuration file - ``/etc/common-lisp/asdf-output-translations.conf`` - if it exists. - - 6- The source registry will be configured from - system configuration directory - ``/etc/common-lisp/asdf-output-translations.conf.d/`` - if it exists. - -Each of these configurations is specified as a SEXP -in a trival domain-specific language (defined below). -Additionally, a more shell-friendly syntax is available -for the environment variable (defined yet below). - -Each of these configurations is only used if the previous -configuration explicitly or implicitly specifies that it -includes its inherited configuration. - -Additionally, some implementation-specific directories -may be automatically added to whatever mappings are specified -in configuration files, no matter if the last one inherits or not. - - -Backward Compatibility -====================== - -We purposefully do NOT provide backward compatibility with earlier versions of -asdf-binary-locations (8 Sept 2009), -common-lisp-controller (7.00) or -cl-launch (2.35), -each of which had similar general capabilities. -Future versions of same packages (if any) -will hopefully use the new ASDF API as defined below. - -These previous programs' API was not designed -for easy configuration by the end-user -in an easy way with configuration files, -and their previous API didn't fit the new paradigm. - -But this incompatibility won't inconvenience many people. -Indeed, few people use and customize these packages; -these people are experts who can trivially adapt to the new configuration. -Most people are not experts, could not properly configure these features -(except inasmuch as the default configuration of -common-lisp-controller and/or cl-launch -might have been doing the right thing for some users), -and yet will experience software that "just works", -as configured by the system distributor, or by default. - -Nevertheless, if you want to use the old ASDF-Binary-Locations -(the one available as an extension to load of top of ASDF, -not the one built into a few old versions of ASDF), -it may still work if you just make sure you do not configure the new -builtin ASDF-Output-Translations. -But if you configure both ASDF's new builtin and ASDF-Binary-Locations -(or an old common-lisp-controller or cl-launch), -you may experience "interesting" issues, so don't do it. - - -Configuration DSL -================= - -Here is the grammar of the SEXP DSL for asdf-output-translations configuration: - -;; A configuration is single SEXP starting with keyword :source-registry -;; followed by a list of directives. -CONFIGURATION := (:output-translations DIRECTIVE ...) - -;; A directive is one of the following: -DIRECTIVE := - ;; include a configuration file or directory - (:include PATHNAME-DESIGNATOR) | - - ;; Your configuration expression MUST contain - ;; exactly one of either of these: - :inherit-configuration | ; splices contents of inherited configuration - :ignore-inherited-configuration | ; drop contents of inherited configuration - - ;; enable global cache in ~/.common-lisp/cache/sbcl-1.0.35-x86-64/ or something. - :enable-user-cache | - ;; Disable global cache. Map / to / - :disable-cache | - - ;; add a single directory to be scanned (no recursion) - (DIRECTORY-DESIGNATOR DIRECTORY-DESIGNATOR) - - -DIRECTORY-DESIGNATOR := - ABSOLUTE-COMPONENT-DESIGNATOR | - (ABSOLUTE-COMPONENT-DESIGNATOR RELATIVE-COMPONENT-DESIGNATOR ...) - -ABSOLUTE-COMPONENT-DESIGNATOR := - STRING | ;; namestring (directory is assumed, better be absolute or bust, ``/**/*.*`` added) - PATHNAME | ;; pathname (better be an absolute directory or bust) - :HOME | ;; designates the user-homedir-pathname ~/ - :USER-CACHE | ;; designates the default location for the user cache - :SYSTEM-CACHE | ;; designates the default location for the system cache - :ROOT | ;; the root of the filesystem hierarchy - :CURRENT-DIRECTORY ;; the current directory - -RELATIVE-COMPONENT-DESIGNATOR := - STRING | ;; namestring, directory is assumed. If the last component, ``/**/*.*`` is added - PATHNAME | ;; pathname unless last component, directory is assumed. - :IMPLEMENTATION | ;; a directory based on implementation, e.g. sbcl-1.0.32.30-linux-x86-64 - :IMPLEMENTATION-TYPE | ;; a directory based on lisp-implementation-type only, e.g. sbcl - :CURRENT-DIRECTORY | ;; all components of the current directory, without the :absolute - :UID | ;; current UID -- not available on Windows - :USER ;; current USER name -- NOT IMPLEMENTED(!) - -Relative components better be either relative -or subdirectories of the path before them, or bust. - -The last component, if not a pathname, is notionally completed by ``/**/*.*``. -You can specify more fine-grained patterns by using a pathname object, -e.g. ``#p"some/path/**/foo*/bar-*.fasl"`` - -You may use ``#+features`` to customize the configuration file. - -The second designator of a mapping may be ``NIL``, indicating that files are not mapped -to anything but themselves (same as if the second designator was the same as the first). - -``:include`` statements cause the search to recurse with the path specifications -from the file specified. - -An ``:inherit-configuration`` statement cause the search to recurse with the path -specifications from the next configuration -(see section Configurations_ above). - -:enable-user-cache is the same as ``(:root :user-cache)``. -:disable-cache is the same as ``(:root :root)``. -:user-cache uses the contents of variable ``asdf::*user-cache*`` -which by default is the same as using -``(:home ".cache" "common-lisp" :implementation)``. -:system-cache uses the contents of variable ``asdf::*system-cache*`` -which by default is the same as using -``(:root "var" "cache" "common-lisp" :uid :implementation-type)`` - - -Configuration Directories -========================= - -Configuration directories consist in files each contains -a list of directives without any enclosing ``(:asdf-output-translations ...)`` form. -The files will be sorted by namestring as if by ``#'string<`` and -the lists of directives of these files with be concatenated in order. -An implicit ``:inherit-configuration`` will be included -at the end of the list. - -This allows for packaging software that has file granularity -(e.g. Debian's ``dpkg`` or some future version of ``clbuild``) -to easily include configuration information about distributed software. - -Directories may be included by specifying a directory pathname -or namestring in an ``:include`` directive, e.g.:: - (:include "/foo/bar/") - - -Shell-friendly syntax for configuration -======================================= - -When considering environment variable ``ASDF_OUTPUT_TRANSLATIONS`` -ASDF will skip to next configuration if it's an empty string. -It will ``READ`` the string as a SEXP in the DSL -if it begins with a paren ``(`` -and it will be interpreted as a colon-separated list of directories. -Directories should come in pairs, each pair indicating a :map directive. - -The magic empty entry indicates the splicing of inherited configuration -rather than one of entry in a mapping pair. - - -Semantics of Output Translations -================================ - -From the specified configuration, a list of mappings is extracted -in a straightforward way: -mappings are collected in order, recursing through -included or inherited configuration as specified. -To this list is prepended some implementation-specific mappings, -and is appended a global default. - -The list is then compiled to a mapping table as follows: -for each entry, in order, resolve the first designated directory -into an actual directory pathname for source locations. -If no mapping was specified yet for that location, -resolve the second designated directory to an output location directory -add a mapping to the table mapping the source location to the output location, -and add another mapping from the output location to itself -(unless a mapping already exists for the output location). - -Based on the table, a mapping function is defined, -mapping source pathnames to output pathnames: -given a source pathname, locate the longest matching prefix -in the source column of the mapping table. -Replace that prefix by the corresponding output column -in the same row of the table, and return the result. -If no match is found, return the source pathname. -(A global default mapping the filesystem root to itself -may ensure that there will always be a match, -with same fall-through semantics). - -Caching Results -=============== - -The implementation is allowed to either eagerly compute the information -from the configurations and file system, or to lazily re-compute it -every time, or to cache any part of it as it goes. -To explicitly flush any information cached by the system, use the API below. - - -Output location API -=================== - -The specified functions are exported from package ASDF. - -(initialize-output-translations) - will read the configuration and initialize all internal variables. - -(clear-output-translations) - undoes any output location configuration - and clears any cache for the mapping algorithm. - You might want to call that before you - dump an image that would be resumed with a different configuration, - and return an empty configuration. - Note that this does not include clearing information about - systems defined in the current image, only about - where to look for systems not yet defined. - -(ensure-output-translations) - checks whether output translations have been initialized. - If not, initialize them. - This function will be called before any attempt to operate on a system. - If your application wants to override the provided defaults, - it will have to use the below function process-output-translations. - -(apply-output-translations PATHNAME) - Applies the configured output location translations to PATHNAME - (calls ensure-output-translations for the translations). - - -Credits -======= - -Thanks a lot to Bjorn Lindberg and Gary King for ASDF-Binary-Locations, -and to Peter van Eynde for Common Lisp Controller. - -All bad design ideas and implementation bugs are to mine, not theirs. -But so are good design ideas and elegant implementation tricks. - - -- Francois-Rene Rideau <fare@tunes.org> - -.. - ;;; Local Variables: - ;;; mode: rst - ;;; End: diff --git a/README.source-registry b/README.source-registry deleted file mode 100644 index c17028b0b64b22511494a8acd9ac462eaa98c370..0000000000000000000000000000000000000000 --- a/README.source-registry +++ /dev/null @@ -1,315 +0,0 @@ -=========================== -Common Lisp Source Registry -=========================== - -This file specifies how build systems such as ASDF and XCVB -may be configured with respect to filesystem paths -where to search for Common Lisp source code. - -Configurations -============== - -Configurations specify paths where to find system files. - - 1- An application may explicitly initialize the source-registry - configuration using the `Configuration API`_ below, - in which case this takes precedence. - It may itself compute this configuration from the command-line, - from a script, from its own configuration file, etc. - - 2- The source registry will be configured from - the environment variable ``CL_SOURCE_REGISTRY`` if it exists. - - 3- The source registry will be configured from - user configuration file - ``~/.config/common-lisp/source-registry.conf`` - if it exists. - - 4- The source registry will be configured from - user configuration directory - ``~/.config/common-lisp/source-registry.conf.d/`` - if it exists. - - 5- The source registry will be configured from - system configuration file - ``/etc/common-lisp/source-registry.conf`` - if it exists. - - 6- The source registry will be configured from - system configuration directory - ``/etc/common-lisp/source-registry.conf.d/`` - if it exists. - - 7- The source registry will be configured from a default configuration, - which allows for implementation-specific software to be searched. - (See below `Backward Compatibility`_). - -Each of these configuration is specified as a SEXP -in a trival domain-specific language (defined below). -Additionally, a more shell-friendly syntax is available -for the environment variable (defined yet below). - -Each of these configurations is only used if the previous -configuration explicitly or implicitly specifies that it -includes its inherited configuration. - -Additionally, some implementation-specific directories -may be automatically prepended to whatever directories are specified -in configuration files, no matter if the last one inherits or not. - - - -Backward Compatibility -====================== - -For backward compatibility, ASDF will fall back to its old ways -of searching for ``.asd`` files in the directories specified in -``asdf:*central-registry*`` -if it fails to find a configuration for the source registry, or -if it fails to find a requested system in the configured source registry. -This new mechanism will therefore not affect you if you don't use it, -but will take precedence over the old mechanism if you do use it. - -Moreover, when using SBCL, now as before, ASDF will first look -for a matching system in the implementation-specific ``contrib`` directory. -This allows for some magic implementation-provided systems -to be loaded specially in a version that matches your implementation. - - -Configuration DSL -================= - -Here is the grammar of the SEXP DSL for source-registry configuration: - -;; A configuration is single SEXP starting with keyword :source-registry -;; followed by a list of directives. -CONFIGURATION := (:source-registry DIRECTIVE ...) - -;; A directive is one of the following: -DIRECTIVE := - ;; add a single directory to be scanned (no recursion) - (:directory DIRECTORY-PATHNAME-DESIGNATOR) | - - ;; add a directory hierarchy, recursing but excluding specified patterns - (:tree DIRECTORY-PATHNAME-DESIGNATOR) | - - ;; override the default defaults for exclusion patterns - (:exclude PATTERN ...) | - - ;; splice the parsed contents of another config file - (:include REGULAR-FILE-PATHNAME-DESIGNATOR) | - - ;; Your configuration expression MUST contain - ;; exactly one of either of these: - :inherit-configuration | ; splices contents of inherited configuration - :ignore-inherited-configuration | ; drop contents of inherited configuration - - ;; This directive specifies that some default must be spliced. - :default-registry - -PATTERN := a string without wildcards, that will be matched exactly - against the name of a any subdirectory in the directory component - of a path. e.g. "_darcs" will match #p"/foo/bar/_darcs/src/bar.asd" - - -Configuration Directories -========================= - -Configuration directories consist in files each contains -a list of directives without any enclosing ``(:source-registry ...)`` form. -The files will be sorted by namestring as if by #'string< and -the lists of directives of these files with be concatenated in order. -An implicit ``:inherit-configuration`` will be included -at the end of the list. - -This allows for packaging software that has file granularity -(e.g. Debian's ``dpkg`` or some future version of ``clbuild``) -to easily include configuration information about distributed software. - -Directories may be included by specifying a directory pathname -or namestring in an ``:include`` directive, e.g.:: - (:include "/foo/bar/") - - -Shell-friendly syntax for configuration -======================================= - -When considering environment variable ``CL_SOURCE_REGISTRY`` -ASDF will skip to next configuration if it's an empty string. -It will ``READ`` the string as a SEXP in the DSL -if it begins with a paren ``(`` -and it will be interpreted much like ``TEXINPUTS`` -as ``:`` (colon) separated list of paths, where - - * each entry is a directory to add to the search path. - - * if the entry ends with a double slash ``//`` - then it instead indicates a tree in the subdirectories - of which to recurse. - - * if the entry is the empty string (which may only appear once), - then it indicates that the inherited configuration should be - spliced there. - - -Search Algorithm -================ - -In case that isn't clear, the semantics of the configuration is that -when searching for a system of a given name, -directives are processed in order. - -When looking in a directory, if the system is found, the search succeeds, -otherwise it continues. - -When looking in a tree, if one system is found, the search succeeds. -If multiple systems are found, the consequences are unspecified: -the search may succeed with any of the found systems, -or an error may be raised. -ASDF currently returns the first system found, -XCVB currently raised an error. -If none is found, the search continues. - -Exclude statements specify patterns of subdirectories the systems of which -to ignore. Typically you don't want to use copies of files kept by such -version control systems as Darcs. - -Include statements cause the search to recurse with the path specifications -from the file specified. - -An inherit-configuration statement cause the search to recurse with the path -specifications from the next configuration -(see section Configurations_ above). - - -Caching Results -=============== - -The implementation is allowed to either eagerly compute the information -from the configurations and file system, or to lazily re-compute it -every time, or to cache any part of it as it goes. -To explicitly flush any information cached by the system, use the API below. - - -Configuration API -================= - -The specified functions are exported from your build system's package. -Thus for ASDF the corresponding functions are in package ASDF, -and for XCVB the corresponding functions are in package XCVB. - -(initialize-source-registry) - will read the configuration and initialize all internal variables. - -(clear-source-registry) - undoes any source registry configuration - and clears any cache for the search algorithm. - You might want to call that before you - dump an image that would be resumed with a different configuration, - and return an empty configuration. - Note that this does not include clearing information about - systems defined in the current image, only about - where to look for systems not yet defined. - -(ensure-source-registry) - checks whether a source registry has been initialized. - If not, initialize it. - This function will be called before any attempt to find a system - in the source registry. - If your application wants to override the provided defaults, - it will have to use the below function process-source-registry. - -(process-source-registry X &key inherit collect) - If X is a CONS, parse it as a SEXP in the configuration DSL, - and extend or override inheritted configuration. - If X is a STRING, first parse it into a SEXP - as for the CL_SOURCE_REGISTRY - environment variable (see above) then process it. - If X is a PATHNAME, read the file as a single SEXP and process it. - The inheritted configuration is provided in keyword argument inherit, - itself a list of functions that take inherit - and collect keyword arguments - and defaulting to a list of functions - that implements the default behavior. - - -Future -====== - -If this mechanism is successful, in the future, we may declare -``asdf:*central-registry*`` obsolete and eventually remove it. -Any hook into implementation-specific search mechanisms will by then -have been integrated in the ``:default-configuration`` which everyone -should either explicitly use or implicit inherit. Some shell syntax -for it should probably be added somehow. - -But we're not there yet. For now, let's see how practical this new -source-registry is. - - -Rejected ideas -============== - -Alternatives I considered and rejected included: - -1- Keep asdf:*central-registry* as the master with its current semantics, - and somehow the configuration parser expands the new configuration - language into a expanded series of directories of subdirectories to - lookup, pre-recursing through specified hierarchies. This is kludgy, - and leaves little space of future cleanups and extensions. - -2- Keep asdf:*central-registry* remains the master but extend its semantics - in completely new ways, so that new kinds of entries may be implemented - as a recursive search, etc. This seems somewhat backwards. - -3- Completely remove asdf:*central-registry* - and break backwards compatibility. - Hopefully this will happen in a few years after everyone migrate to - a better ASDF and/or to XCVB, but it would be very bad to do it now. - -4- Replace asdf:*central-registry* by a symbol-macro with appropriate magic - when you dereference it or setf it. Only the new variable with new - semantics is handled by the new search procedure. - - -I've been suggested the below features, but have rejected them, -for the sake of keeping ASDF no more complex than strictly necessary. - -* More syntactic sugar: synonyms for the configuration directives, such as - (:add-directory X) for (:directory X), or (:add-directory-hierarchy X) - or (:add-directory X :recurse t) for (:tree X). - -* The possibility to register individual files instead of directories. - -* Integrate Xach Beane's tilde expander into the parser, - or something similar that is shell-friendly or shell-compatible. - I'd rather keep ASDF minimal. But maybe this precisely keeps it - minimal by removing the need for evaluated entries that ASDF has? - i.e. uses of USER-HOMEDIR-PATHNAME and $SBCL_HOME - Hopefully, these are already superseded by the :default-registry - -* Using the shell-unfriendly syntax /** instead of // to specify recursion - down a filesystem tree in the environment variable. - It isn't that Lisp friendly either. - -TODO -==== - -* Integrate into the rest of the documentation. -* Add examples -* Add proper defaults. -* Get inspired by the XDG base directory specification? - http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - - -Credits -======= - -Thanks a lot to Stelian Ionescu for the initial idea. - -Thanks to Rommel Martinez for the initial implementation attempt. - -All bad design ideas and implementation bugs are to mine, not theirs. -But so are good design ideas and elegant implementation tricks. - - -- Francois-Rene Rideau <fare@tunes.org>, Mon, 22 Feb 2010 00:07:33 -0500 diff --git a/asdf-ecl.lisp b/asdf-ecl.lisp index ae8459f13af5eff99bfc57a5c89b48e5ad1888f0..acec80f7c51aae828945193b6c88776d16896aae 100644 --- a/asdf-ecl.lisp +++ b/asdf-ecl.lisp @@ -9,6 +9,7 @@ ;;; ;;; ECL SPECIFIC OPERATIONS FOR ASDF ;;; +#+xcvb (module (:depends-on ("asdf"))) (in-package :asdf) (require 'cmp) @@ -168,8 +169,8 @@ (defmethod output-files ((o bundle-op) (c system)) (let ((name (concatenate 'base-string (component-name c) (slot-value o 'name-suffix)))) - (list (merge-pathnames (compile-file-pathname name :type (bundle-op-type o)) - (component-relative-pathname c))))) + (list (merge-pathnames* (compile-file-pathname name :type (bundle-op-type o)) + (component-relative-pathname c))))) (defmethod output-files ((o fasl-op) (c system)) (loop for file in (call-next-method) @@ -207,10 +208,36 @@ (defun make-build (system &rest args &key (monolithic nil) (type :fasl) + (move-here nil move-here-p) &allow-other-keys) - (apply #'operate (select-operation monolithic type) - system - (remove-keys '(monolithic type) args))) + (let* ((operation-name (select-operation monolithic type)) + (operation (apply #'operate operation-name + system + (remove-keys '(monolithic type move-here) args))) + (system (find-system system)) + (files (and system (output-files operation system)))) + (print files) + (print move-here) + (if (or move-here + (and (null move-here-p) + (member operation-name '(:program)))) + (loop for path in files + for filename = (namestring (truename path)) + for new-path = (make-pathname :name (pathname-name path) + :type (pathname-type path) + :defaults *default-pathname-defaults*) + for new-filename = (namestring new-path) + for command = + #+windows + (format nil "move ~S ~S" filename new-filename) + #-windows + (format nil "mv ~S ~S" filename new-filename) + do (unless (equalp new-filename filename) + (when (plusp (si::system (print command))) + (error "Unable to move file~& ~S~&to new location~& ~S" + path new-path))) + collect new-path) + files))) ;;; ;;; LOAD-FASL-OP @@ -254,10 +281,10 @@ (defclass compiled-file (component) ()) (defmethod component-relative-pathname ((component compiled-file)) (compile-file-pathname - (merge-component-relative-pathname - (slot-value component 'relative-pathname) - (component-name component) - "fas"))) + (merge-component-name-type + (or (slot-value component 'relative-pathname) + (component-name component)) + :type "fas"))) (defmethod output-files (o (c compiled-file)) nil) @@ -270,6 +297,33 @@ (defmethod perform (o (c compiled-file)) nil) +;;; +;;; Pre-built systems +;;; +(defclass prebuilt-system (system) + ((static-library :accessor prebuilt-system-static-library :initarg :lib))) + +(defmethod output-files ((o lib-op) (c prebuilt-system)) + (values (list (compile-file-pathname (prebuilt-system-static-library c) + :type :lib)) + t ; Advertise that we do not want this path renamed + )) + +(defmethod perform ((o lib-op) (c prebuilt-system)) + (car (output-files o c))) + +(defmethod component-depends-on ((o lib-op) (c prebuilt-system)) + nil) + +(defmethod bundle-sub-operations ((o lib-op) (c prebuilt-system)) + nil) + +(defmethod bundle-sub-operations ((o monolithic-lib-op) (c prebuilt-system)) + (error "Prebuilt system ~S shipped with ECL can not be used in a monolithic library operation." c)) + +(defmethod bundle-sub-operations ((o monolithic-bundle-op) (c prebuilt-system)) + nil) + ;;; ;;; Final integration steps ;;; @@ -289,7 +343,8 @@ t)))) (defun register-pre-built-system (name) - (register-system name (make-instance 'system :name name))) + (register-system name (make-instance 'system :name name + :source-file nil))) (setf si::*module-provider-functions* (loop for f in si::*module-provider-functions* diff --git a/asdf.lisp b/asdf.lisp index bd493a17b28917898ee3f25c899bbe31aaa11ee0..16a55dbd2a24b557c19be69e70d84bb0f8809356 100644 --- a/asdf.lisp +++ b/asdf.lisp @@ -1,9 +1,9 @@ ;;; -*- mode: common-lisp; package: asdf; -*- -;;; This is asdf: Another System Definition Facility. +;;; This is ASDF: Another System Definition Facility. ;;; ;;; Feedback, bug reports, and patches are all welcome: ;;; please mail to <asdf-devel@common-lisp.net>. -;;; Note first that the canonical source for asdf is presently +;;; Note first that the canonical source for ASDF is presently ;;; <URL:http://common-lisp.net/project/asdf/>. ;;; ;;; If you obtained this copy from anywhere else, and you experience @@ -49,208 +49,244 @@ (cl:in-package :cl-user) -(declaim (optimize (speed 3) (debug 2) (safety 2))) +(declaim (optimize (speed 2) (debug 2) (safety 3))) -#+ecl (require 'cmp) +#+ecl (require :cmp) ;;;; Create packages in a way that is compatible with hot-upgrade. ;;;; See https://bugs.launchpad.net/asdf/+bug/485687 ;;;; See more at the end of the file. (eval-when (:load-toplevel :compile-toplevel :execute) - (labels ((rename-away (package) - (loop :with name = (package-name package) - :for i :from 1 :for n = (format nil "~A.~D" name i) - :unless (find-package n) :do (rename-package package n))) - (ensure-exists (name nicknames use) - (let* ((previous - (remove-duplicates - (remove-if - #'null - (mapcar #'find-package (cons name nicknames))) - :from-end t))) - (cond - (previous - (map () #'rename-away (cdr previous)) - (let ((p (car previous))) - (rename-package p name nicknames) - (ensure-use p use) - p)) - (t - (make-package name :nicknames nicknames :use use))))) - (remove-symbol (symbol package) - (let ((sym (find-symbol (string symbol) package))) - (when sym - (unexport sym package) - (unintern sym package)))) - (ensure-unintern (package symbols) - (dolist (sym symbols) (remove-symbol sym package))) - (ensure-shadow (package symbols) - (shadow symbols package)) - (ensure-use (package use) - (dolist (used (reverse use)) - (do-external-symbols (sym used) - (unless (eq sym (find-symbol (string sym) package)) - (remove-symbol sym package))) - (use-package used package))) - (ensure-export (package export) - (let ((syms (loop :for x :in export :collect - (intern (string x) package)))) - (do-external-symbols (sym package) - (unless (member sym syms) - (remove-symbol sym package))) - (dolist (sym syms) - (export sym package)))) - (ensure-package (name &key nicknames use unintern shadow export) - (let ((p (ensure-exists name nicknames use))) - (ensure-unintern p unintern) - (ensure-shadow p shadow) - (ensure-export p export) - p))) - (ensure-package - ':asdf-utilities - :nicknames '(#:asdf-extensions) - :use '(#:common-lisp) - :export - '(#:absolute-pathname-p - #:aif - #:appendf - #:asdf-message - #:coerce-name - #:directory-pathname-p - #:ends-with - #:ensure-directory-pathname - #:getenv - #:get-uid - #:length=n-p - #:make-collector - #:pathname-directory-pathname - #:pathname-sans-name+type ;; deprecated. Use pathname-directory-pathname - #:read-file-forms - #:remove-keys - #:remove-keyword - #:resolve-symlinks - #:split - #:component-name-to-pathname-components - #:system-registered-p - #:truenamize)) - (ensure-package - ':asdf - :use '(:common-lisp :asdf-utilities) - :unintern '(#:*asdf-revision*) - :export - '(#:defsystem #:oos #:operate #:find-system #:run-shell-command - #:system-definition-pathname #:find-component ; miscellaneous - #:compile-system #:load-system #:test-system - #:compile-op #:load-op #:load-source-op - #:test-op - #:operation ; operations - #:feature ; sort-of operation - #:version ; metaphorically sort-of an operation - - #:input-files #:output-files #:perform ; operation methods - #:operation-done-p #:explain - - #:component #:source-file - #:c-source-file #:cl-source-file #:java-source-file - #:static-file - #:doc-file - #:html-file - #:text-file - #:source-file-type - #:module ; components - #:system - #:unix-dso - - #:module-components ; component accessors - #:component-pathname - #:component-relative-pathname - #:component-name - #:component-version - #:component-parent - #:component-property - #:component-system - - #:component-depends-on - - #:system-description - #:system-long-description - #:system-author - #:system-maintainer - #:system-license - #:system-licence - #:system-source-file - #:system-relative-pathname - #:map-systems - - #:operation-on-warnings - #:operation-on-failure - + #+allegro + (setf excl::*autoload-package-name-alist* + (remove "asdf" excl::*autoload-package-name-alist* :test 'equalp :key 'car)) + (let* ((asdf-version + ;; the 1+ hair is to ensure that we don't do an inadvertent find and replace + (subseq "VERSION:1.704" (1+ (length "VERSION")))) + (existing-asdf (find-package :asdf)) + (versym '#:*asdf-version*) + (existing-version (and existing-asdf + (symbol-value + (find-symbol (string versym) existing-asdf)))) + (redefined-functions + '(#:perform #:explain #:output-files #:operation-done-p + #:perform-with-restarts #:component-relative-pathname + #:system-source-file))) + (unless (equal asdf-version existing-version) + (when existing-asdf + (format *error-output* + "~&Upgrading ASDF package ~@[from version ~A ~]to version ~A~%" + existing-version asdf-version)) + (labels ((rename-away (package) + (loop :with name = (package-name package) + :for i :from 1 :for new = (format nil "~A.~D" name i) + :unless (find-package new) :do + (rename-package-name package name new))) + (rename-package-name (package old new) + (let* ((old-names (cons (package-name package) (package-nicknames package))) + (new-names (subst new old old-names :test 'equal)) + (new-name (car new-names)) + (new-nicknames (cdr new-names))) + (rename-package package new-name new-nicknames))) + (ensure-exists (name nicknames use) + (let* ((previous + (remove-duplicates + (remove-if + #'null + (mapcar #'find-package (cons name nicknames))) + :from-end t))) + (cond + (previous + (map () #'rename-away (cdr previous)) ;; packages with conflicting (nick)names + (let ((p (car previous))) ;; previous package with same name + (rename-package p name nicknames) + (ensure-use p use) + p)) + (t + (make-package name :nicknames nicknames :use use))))) + (find-sym (symbol package) + (find-symbol (string symbol) package)) + (remove-symbol (symbol package) + (let ((sym (find-sym symbol package))) + (when sym + (unexport sym package) + (unintern sym package)))) + (ensure-unintern (package symbols) + (dolist (sym symbols) (remove-symbol sym package))) + (ensure-shadow (package symbols) + (shadow symbols package)) + (ensure-use (package use) + (dolist (used (reverse use)) + (do-external-symbols (sym used) + (unless (eq sym (find-sym sym package)) + (remove-symbol sym package))) + (use-package used package))) + (ensure-fmakunbound (package symbols) + (loop :for name :in symbols + :for sym = (find-sym name package) + :when sym :do (fmakunbound sym))) + (ensure-export (package export) + (let ((syms (loop :for x :in export :collect + (intern (string x) package)))) + (do-external-symbols (sym package) + (unless (member sym syms) + (remove-symbol sym package))) + (dolist (sym syms) + (export sym package)))) + (ensure-package (name &key nicknames use unintern fmakunbound shadow export) + (let ((p (ensure-exists name nicknames use))) + (ensure-unintern p unintern) + (ensure-shadow p shadow) + (ensure-export p export) + (ensure-fmakunbound p fmakunbound) + p))) + (ensure-package + ':asdf-utilities + :nicknames '(#:asdf-extensions) + :use '(#:common-lisp) + :unintern '(#:split #:make-collector) + :export + '(#:absolute-pathname-p + #:aif + #:appendf + #:asdf-message + #:coerce-name + #:directory-pathname-p + #:ends-with + #:ensure-directory-pathname + #:getenv + #:get-uid + #:length=n-p + #:merge-pathnames* + #:pathname-directory-pathname + #:pathname-sans-name+type ;; deprecated. Use pathname-directory-pathname + #:read-file-forms + #:remove-keys + #:remove-keyword + #:resolve-symlinks + #:split-string + #:component-name-to-pathname-components + #:split-name-type + #:system-registered-p + #:truenamize + #:while-collecting)) + (ensure-package + ':asdf + :use '(:common-lisp :asdf-utilities) + :unintern `(#-ecl ,@redefined-functions + #:*asdf-revision* #:around #:asdf-method-combination + #:split #:make-collector) + :fmakunbound `(#+ecl ,@redefined-functions + #:system-source-file + #:component-relative-pathname #:system-relative-pathname + #:process-source-registry + #:inherit-source-registry #:process-source-registry-directive) + :export + '(#:defsystem #:oos #:operate #:find-system #:run-shell-command + #:system-definition-pathname #:find-component ; miscellaneous + #:compile-system #:load-system #:test-system + #:compile-op #:load-op #:load-source-op + #:test-op + #:operation ; operations + #:feature ; sort-of operation + #:version ; metaphorically sort-of an operation + #:version-satisfies + + #:input-files #:output-files #:perform ; operation methods + #:operation-done-p #:explain + + #:component #:source-file + #:c-source-file #:cl-source-file #:java-source-file + #:static-file + #:doc-file + #:html-file + #:text-file + #:source-file-type + #:module ; components + #:system + #:unix-dso + + #:module-components ; component accessors + #:module-components-by-name ; component accessors + #:component-pathname + #:component-relative-pathname + #:component-name + #:component-version + #:component-parent + #:component-property + #:component-system + + #:component-depends-on + + #:system-description + #:system-long-description + #:system-author + #:system-maintainer + #:system-license + #:system-licence + #:system-source-file + #:system-source-directory + #:system-relative-pathname + #:map-systems + + #:operation-on-warnings + #:operation-on-failure ;#:*component-parent-pathname* - #:*system-definition-search-functions* - #:*central-registry* ; variables - #:*compile-file-warnings-behaviour* - #:*compile-file-failure-behaviour* - #:*resolve-symlinks* - - #:asdf-version - - #:operation-error #:compile-failed #:compile-warned #:compile-error - #:error-name - #:error-pathname - #:missing-definition - #:error-component #:error-operation - #:system-definition-error - #:missing-component - #:missing-component-of-version - #:missing-dependency - #:missing-dependency-of-version - #:circular-dependency ; errors - #:duplicate-names - - #:try-recompiling - #:retry - #:accept ; restarts - #:coerce-entry-to-directory - #:remove-entry-from-registry - - #:standard-asdf-method-combination - #:around ; protocol assistants - - #:initialize-output-translations - #:clear-output-translations - #:ensure-output-translations - #:apply-output-translations - #:compile-file-pathname* - - #:*default-source-registries* - #:initialize-source-registry - #:compute-source-registry - #:clear-source-registry - #:ensure-source-registry - #:process-source-registry)))) - -#+nil -(error "The author of this file habitually uses #+nil to comment out ~ - forms. But don't worry, it was unlikely to work in the New ~ - Implementation of Lisp anyway") + #:*system-definition-search-functions* + #:*central-registry* ; variables + #:*compile-file-warnings-behaviour* + #:*compile-file-failure-behaviour* + #:*resolve-symlinks* + #:*asdf-verbose* + + #:asdf-version + + #:operation-error #:compile-failed #:compile-warned #:compile-error + #:error-name + #:error-pathname + #:load-system-definition-error + #:error-component #:error-operation + #:system-definition-error + #:missing-component + #:missing-component-of-version + #:missing-dependency + #:missing-dependency-of-version + #:circular-dependency ; errors + #:duplicate-names + + #:try-recompiling + #:retry + #:accept ; restarts + #:coerce-entry-to-directory + #:remove-entry-from-registry + + #:initialize-output-translations + #:disable-output-translations + #:clear-output-translations + #:ensure-output-translations + #:apply-output-translations + #:compile-file-pathname* + #:enable-asdf-binary-locations-compatibility + + #:*default-source-registries* + #:initialize-source-registry + #:compute-source-registry + #:clear-source-registry + #:ensure-source-registry + #:process-source-registry)) + (eval `(defparameter ,(intern (string versym) (find-package :asdf)) + ,asdf-version)))))) (in-package #:asdf) ;;;; ------------------------------------------------------------------------- ;;;; User-visible parameters ;;;; -(defparameter *asdf-version* - ;; This parameter isn't actually user-visible - ;; -- please use the exported function ASDF:ASDF-VERSION below. - ;; the 1+ hair is to ensure that we don't do an inadvertent find and replace - (subseq "VERSION:1.627" (1+ (length "VERSION")))) - (defun asdf-version () "Exported interface to the version of ASDF currently installed. A string. - -Not officially supported: -you can compare this string with ASDF::VERSION-SATISFIES." +You can compare this string with e.g.: +(ASDF:VERSION-SATISFIES (ASDF:ASDF-VERSION) \"1.704\")." *asdf-version*) (defvar *resolve-symlinks* t @@ -264,8 +300,10 @@ Defaults to `t`.") (defvar *verbose-out* nil) +(defvar *asdf-verbose* t) + (defparameter +asdf-methods+ - '(perform explain output-files operation-done-p)) + '(perform-with-restarts perform explain output-files operation-done-p)) #+allegro (eval-when (:compile-toplevel :execute) @@ -279,16 +317,15 @@ Defaults to `t`.") ;;;; Cleanups before hot-upgrade. ;;;; Things to do in case we're upgrading from a previous version of ASDF. ;;;; See https://bugs.launchpad.net/asdf/+bug/485687 -;;;; * fmakunbound functions that once (in previous version of ASDF) -;;;; were simple DEFUNs but now are generic functions. ;;;; * define methods on UPDATE-INSTANCE-FOR-REDEFINED-CLASS ;;;; for each of the classes we define that has changed incompatibly. (eval-when (:compile-toplevel :load-toplevel :execute) - (when (and (fboundp 'system-source-file) - (not (typep (fdefinition 'system-source-file) 'generic-function))) - (fmakunbound 'system-source-file)) - (map () 'fmakunbound '(process-source-registry inherit-source-registry - process-source-registry-directive)) + (when (find-class 'module nil) + (eval ;; without this protection, Lispworks issues a warning. Sigh. + '(defmethod update-instance-for-redefined-class :after + ((m module) added deleted plist &key) + (when (member 'components-by-name added) + (compute-module-components-by-name m))))) #+ecl (when (find-class 'compile-op nil) (defmethod update-instance-for-redefined-class :after @@ -297,57 +334,15 @@ Defaults to `t`.") (let ((system-p (getf plist 'system-p))) (when system-p (setf (getf (slot-value c 'flags) :system-p) system-p)))))) -;;;; ------------------------------------------------------------------------- -;;;; CLOS magic for asdf:around methods - -(define-method-combination standard-asdf-method-combination () - ((around-asdf (around)) - (around (:around)) - (before (:before)) - (primary () :required t) - (after (:after))) - (flet ((call-methods (methods) - (mapcar #'(lambda (method) - `(call-method ,method)) - methods))) - (let* ((form (if (or before after (rest primary)) - `(multiple-value-prog1 - (progn ,@(call-methods before) - (call-method ,(first primary) - ,(rest primary))) - ,@(call-methods (reverse after))) - `(call-method ,(first primary)))) - (standard-form (if around - `(call-method ,(first around) - (,@(rest around) - (make-method ,form))) - form))) - (if around-asdf - `(call-method ,(first around-asdf) - (,@(rest around-asdf) (make-method ,standard-form))) - standard-form)))) - -(setf (documentation 'standard-asdf-method-combination - 'method-combination) - "This method combination is based on the standard method combination, -but defines a new method-qualifier, `asdf:around`. `asdf:around` -methods will be run *around* any `:around` methods, so that the core -protocol may employ around methods and those around methods will not -be overridden by around methods added by a system developer.") - ;;;; ------------------------------------------------------------------------- ;;;; ASDF Interface, in terms of generic functions. -(defgeneric perform (operation component) - (:method-combination standard-asdf-method-combination)) -(defgeneric operation-done-p (operation component) - (:method-combination standard-asdf-method-combination)) -(defgeneric explain (operation component) - (:method-combination standard-asdf-method-combination)) -(defgeneric output-files (operation component) - (:method-combination standard-asdf-method-combination)) -(defgeneric input-files (operation component) - (:method-combination standard-asdf-method-combination)) +(defgeneric perform-with-restarts (operation component)) +(defgeneric perform (operation component)) +(defgeneric operation-done-p (operation component)) +(defgeneric explain (operation component)) +(defgeneric output-files (operation component)) +(defgeneric input-files (operation component)) (defgeneric system-source-file (system) (:documentation "Return the source file in which system is defined.")) @@ -389,7 +384,7 @@ VISIT-COMPONENT, if that has been called, otherwise NIL. This value stored will be a cons cell, the first element of which is a computed key, so not interesting. The CDR wil be the DATA value stored by VISIT-COMPONENT; recover -it as \(cdr \(component-visited-p op c\)\). +it as (cdr (component-visited-p op c)). In the current form of ASDF, the DATA value retrieved is effectively a boolean, indicating whether some operations are to be performed in order to do OPERATION X COMPONENT. If the @@ -437,31 +432,18 @@ The plan returned is a list of dotted-pairs. Each pair is the `cons` of ASDF operation object and a `component` object. The pairs will be processed in order by `operate`.")) -(defgeneric output-files-using-mappings (source possible-paths path-mappings) - (:documentation -"Use the variable \\*source-to-target-mappings\\* to find -an output path for the source. The algorithm transforms each -entry in possible-paths as follows: If there is a mapping -whose source starts with the path of possible-path, then -replace possible-path with a pathname that starts with the -target of the mapping and continues with the rest of -possible-path. If no such mapping is found, then use the -default mapping. - -If \\*centralize-lisp-binaries\\* is false, then the default -mapping is to place the output in a subdirectory of the -source. The subdirectory is named using the Lisp -implementation \(see -implementation-specific-directory-name\). If -\\*centralize-lisp-binaries\\* is true, then the default -mapping is to place the output in subdirectories of -\\*default-toplevel-directory\\* where the subdirectory -structure will mirror that of the source.")) - ;;;; ------------------------------------------------------------------------- ;;;; General Purpose Utilities +(defmacro while-collecting ((&rest collectors) &body body) + (let ((vars (mapcar #'(lambda (x) (gensym (symbol-name x))) collectors)) + (initial-values (mapcar (constantly nil) collectors))) + `(let ,(mapcar #'list vars initial-values) + (flet ,(mapcar #'(lambda (c v) `(,c (x) (push x ,v) (values))) collectors vars) + ,@body + (values ,@(mapcar #'(lambda (v) `(reverse ,v)) vars)))))) + (defmacro aif (test then &optional else) `(let ((it ,test)) (if it ,then ,else))) @@ -478,26 +460,89 @@ Deprecated. Use PATHNAME-DIRECTORY-PATHNAME instead." and NIL NAME, TYPE and VERSION components" (make-pathname :name nil :type nil :version nil :defaults pathname)) +(defun current-directory () + (truenamize (pathname-directory-pathname *default-pathname-defaults*))) + +(defun merge-pathnames* (specified &optional (defaults *default-pathname-defaults*)) + "MERGE-PATHNAMES* is like MERGE-PATHNAMES except that if the SPECIFIED pathname +does not have an absolute directory, then the HOST and DEVICE come from the DEFAULTS. +Also, if either argument is NIL, then the other argument is returned unmodified." + (when (null specified) (return-from merge-pathnames* defaults)) + (when (null defaults) (return-from merge-pathnames* specified)) + (let* ((specified (pathname specified)) + (defaults (pathname defaults)) + (directory (pathname-directory specified)) + (directory (if (stringp directory) `(:absolute ,directory) directory)) + (name (or (pathname-name specified) (pathname-name defaults))) + (type (or (pathname-type specified) (pathname-type defaults))) + (version (or (pathname-version specified) (pathname-version defaults)))) + (labels ((ununspecific (x) + (if (eq x :unspecific) nil x)) + (unspecific-handler (p) + (if (typep p 'logical-pathname) #'ununspecific #'identity))) + (multiple-value-bind (host device directory unspecific-handler) + (ecase (first directory) + ((nil) + (values (pathname-host defaults) + (pathname-device defaults) + (pathname-directory defaults) + (unspecific-handler defaults))) + ((:absolute) + (values (pathname-host specified) + (pathname-device specified) + directory + (unspecific-handler specified))) + ((:relative) + (values (pathname-host defaults) + (pathname-device defaults) + (append (pathname-directory defaults) (cdr directory)) + (unspecific-handler defaults)))) + (make-pathname :host host :device device :directory directory + :name (funcall unspecific-handler name) + :type (funcall unspecific-handler type) + :version (funcall unspecific-handler version)))))) + (define-modify-macro appendf (&rest args) - append "Append onto list") + append "Append onto list") ;; only to be used on short lists. + +(define-modify-macro orf (&rest args) + or "or a flag") (defun asdf-message (format-string &rest format-args) (declare (dynamic-extent format-args)) (apply #'format *verbose-out* format-string format-args)) -;;; with apologies to Christophe Rhodes ... -(defun split (string &optional max (ws '(#\Space #\Tab))) - (flet ((is-ws (char) (find char ws))) - (nreverse - (let ((list nil) (start 0) (words 0) end) - (loop - (when (and max (>= words (1- max))) - (return (cons (subseq string start) list))) - (setf end (position-if #'is-ws string :start start)) - (push (subseq string start end) list) - (incf words) - (unless end (return list)) - (setf start (1+ end))))))) +(defun split-string (string &key max (separator '(#\Space #\Tab))) + "Split STRING in components separater by any of the characters in the sequence SEPARATOR, +return a list. +If MAX is specified, then no more than max(1,MAX) components will be returned, +starting the separation from the end, e.g. when called with arguments + \"a.b.c.d.e\" :max 3 :separator \".\" it will return (\"a.b.c\" \"d\" \"e\")." + (block nil + (let ((list nil) (words 0) (end (length string))) + (flet ((separatorp (char) (find char separator)) + (done () (return (cons (subseq string 0 end) list)))) + (loop + :for start = (if (and max (>= words (1- max))) + (done) + (position-if #'separatorp string :end end :from-end t)) :do + (when (null start) + (done)) + (push (subseq string (1+ start) end) list) + (incf words) + (setf end start)))))) + +(defun split-name-type (filename) + (let ((unspecific + ;; Giving :unspecific as argument to make-pathname is not portable. + ;; See CLHS make-pathname and 19.2.2.2.3. + ;; We only use it on implementations that support it. + (or #+(or sbcl ccl ecl lispworks) :unspecific))) + (destructuring-bind (name &optional (type unspecific)) + (split-string filename :max 2 :separator ".") + (if (equal name "") + (values filename unspecific) + (values name type))))) (defun component-name-to-pathname-components (s &optional force-directory) "Splits the path string S, returning three values: @@ -516,7 +561,7 @@ The intention of this function is to support structured component names, e.g., \(:file \"foo/bar\"\), which will be unpacked to relative pathnames." (check-type s string) - (let* ((components (split s nil "/")) + (let* ((components (split-string s :separator "/")) (last-comp (car (last components)))) (multiple-value-bind (relative components) (if (equal (first components) "") @@ -544,10 +589,12 @@ pathnames." :append (list k v))) (defun resolve-symlinks (path) - #-allegro (truename path) + #-allegro (truenamize path) #+allegro (excl:pathname-resolve-symbolic-links path)) (defun getenv (x) + #+abcl + (ext:getenv x) #+sbcl (sb-ext:posix-getenv x) #+clozure @@ -575,24 +622,16 @@ ways that the filename components can be missing are for it to be `nil`, Note that this does _not_ check to see that `pathname` points to an actually-existing directory." (flet ((check-one (x) - (not (null (member x '(nil :unspecific "") - :test 'equal))))) + (member x '(nil :unspecific "") :test 'equal))) (and (check-one (pathname-name pathname)) - (check-one (pathname-type pathname))))) + (check-one (pathname-type pathname)) + t))) (defun ensure-directory-pathname (pathspec) "Converts the non-wild pathname designator PATHSPEC to directory form." (cond ((stringp pathspec) - (pathname - (let ((lastchar (aref pathspec (1- (length pathspec))))) - (cond ((or (eql lastchar #\;) (eql lastchar #\/)) pathspec) - ((find #\; pathspec) - (concatenate 'string pathspec ";")) - (t - ;; guess it's a string that's not a logical - ;; pathname string - (concatenate 'string pathspec "/")))))) + (ensure-directory-pathname (pathname pathspec))) ((not (pathnamep pathspec)) (error "Invalid pathname designator ~S" pathspec)) ((wild-pathname-p pathspec) @@ -625,11 +664,6 @@ actually-existing directory." (and (<= 0 start) (string-equal s suffix :start1 start)))) -(defun make-collector () - (let ((acc ())) - (values (lambda (x) (push x acc)) - (lambda () (reverse acc))))) - (defun read-file-forms (file) (with-open-file (in file) (loop :with eof = (list nil) @@ -637,60 +671,70 @@ actually-existing directory." :until (eq form eof) :collect form))) -#-windows +#-(and (or win32 windows mswindows mingw32) (not cygwin)) (progn #+clisp (defun get-uid () (posix:uid)) #+sbcl (defun get-uid () (sb-unix:unix-getuid)) #+cmu (defun get-uid () (unix:unix-getuid)) +#+ecl (ffi:clines "#include <sys/types.h>" "#include <unistd.h>") #+ecl (defun get-uid () (ffi:c-inline () () :int "getuid()" :one-liner t)) #+allegro (defun get-uid () (excl.osi:getuid)) #-(or cmu sbcl clisp allegro ecl) (defun get-uid () (let ((uid-string - (with-output-to-string (asdf::*VERBOSE-OUT*) + (with-output-to-string (*verbose-out*) (asdf:run-shell-command "id -ur")))) (with-input-from-string (stream uid-string) (read-line stream) (handler-case (parse-integer (read-line stream)) (error () (error "Unable to find out user ID"))))))) +(defun pathname-root (pathname) + (make-pathname :host (pathname-host pathname) + :device (pathname-device pathname) + :directory '(:absolute) + :name nil :type nil :version nil)) + (defun truenamize (p) "Resolve as much of a pathname as possible" - (block :t - (setf p (translate-logical-pathname (merge-pathnames p))) - (ignore-errors (return-from :t (truename p))) - (let ((host (pathname-host p)) - (device (pathname-device p)) - (directory (pathname-directory p))) - (when (or (atom directory) (not (eq :absolute (car directory)))) - (return-from :t p)) - (let ((sofar (ignore-errors - (truename (make-pathname :host host :device device - :directory '(:absolute)))))) - (unless sofar (return-from :t p)) + (block nil + (when (typep p 'logical-pathname) (return p)) + (let* ((p (merge-pathnames* p)) + (directory (pathname-directory p))) + (when (typep p 'logical-pathname) (return p)) + (ignore-errors (return (truename p))) + (when (stringp directory) + (return p)) + (when (not (eq :absolute (car directory))) + (return p)) + (let ((sofar (ignore-errors (truename (pathname-root p))))) + (unless sofar (return p)) (loop :for component :in (cdr directory) :for rest :on (cdr directory) :for more = (ignore-errors (truename - (merge-pathnames + (merge-pathnames* (make-pathname :directory `(:relative ,component)) sofar))) :do (if more (setf sofar more) - (return-from :t - (merge-pathnames + (return + (merge-pathnames* (make-pathname :host nil :device nil :directory `(:relative ,@rest) :defaults p) sofar))) :finally - (return-from :t - (merge-pathnames + (return + (merge-pathnames* (make-pathname :host nil :device nil :directory nil :defaults p) sofar))))))) +(defun lispize-pathname (input-file) + (make-pathname :type "lisp" :defaults input-file)) + ;;;; ------------------------------------------------------------------------- ;;;; Classes, Conditions @@ -709,19 +753,22 @@ actually-existing directory." (:report (lambda (c s) (apply #'format s (format-control c) (format-arguments c))))) -(define-condition missing-definition (system-definition-error) +(define-condition load-system-definition-error (system-definition-error) ((name :initarg :name :reader error-name) - (pathname :initarg :pathname :reader error-pathname)) + (pathname :initarg :pathname :reader error-pathname) + (condition :initarg :condition :reader error-condition)) (:report (lambda (c s) - (format s "~@<Definition search function returned a wrong pathname ~A ~ - in search of a definition for system ~A.~@:>" - (error-pathname c) (error-name c))))) + (format s "~@<Error while trying to load definition for system ~A from pathname ~A: ~A~@:>" + (error-name c) (error-pathname c) (error-condition c))))) (define-condition circular-dependency (system-definition-error) ((components :initarg :components :reader circular-dependency-components))) (define-condition duplicate-names (system-definition-error) - ((name :initarg :name :reader duplicate-names-name))) + ((name :initarg :name :reader duplicate-names-name)) + (:report (lambda (c s) + (format s "~@<Error while defining system: multiple components are given same name ~A~@:>" + (duplicate-names-name c))))) (define-condition missing-component (system-definition-error) ((requires :initform "(unnamed)" :reader missing-requires :initarg :requires) @@ -753,7 +800,9 @@ actually-existing directory." (version :accessor component-version :initarg :version) (in-order-to :initform nil :initarg :in-order-to :accessor component-in-order-to) - ;; XXX crap name + ;; This one is used by POIU. Maybe in the future by ASDF instead of in-order-to? + (load-dependencies :accessor component-load-dependencies :initform nil) + ;; XXX crap name, but it's an official API name! (do-first :initform nil :initarg :do-first :accessor component-do-first) ;; methods defined using the "inline" style inside a defsystem form: @@ -764,6 +813,7 @@ actually-existing directory." ;; no direct accessor for pathname, we do this as a method to allow ;; it to default in funky ways if not supplied (relative-pathname :initarg :pathname) + (absolute-pathname) (operation-times :initform (make-hash-table) :accessor component-operation-times) ;; XXX we should provide some atomic interface for updating the @@ -808,35 +858,59 @@ actually-existing directory." (ignore-errors (prin1 (component-name c) stream)))) +(defvar *default-component-class* 'cl-source-file) + +(defun compute-module-components-by-name (module) + (let ((hash (module-components-by-name module))) + (clrhash hash) + (loop :for c :in (module-components module) + :for name = (component-name c) + :for previous = (gethash name (module-components-by-name module)) + :do + (when previous + (error 'duplicate-names :name name)) + :do (setf (gethash name (module-components-by-name module)) c)) + hash)) + (defclass module (component) - ((components :initform nil :accessor module-components :initarg :components) - ;; what to do if we can't satisfy a dependency of one of this module's - ;; components. This allows a limited form of conditional processing - (if-component-dep-fails :initform :fail - :accessor module-if-component-dep-fails - :initarg :if-component-dep-fails) - (default-component-class :accessor module-default-component-class - :initform 'cl-source-file :initarg :default-component-class))) + ((components + :initform nil + :initarg :components + :accessor module-components) + (components-by-name + :initform (make-hash-table :test 'equal) + :accessor module-components-by-name) + ;; What to do if we can't satisfy a dependency of one of this module's + ;; components. This allows a limited form of conditional processing. + (if-component-dep-fails + :initform :fail + :initarg :if-component-dep-fails + :accessor module-if-component-dep-fails) + (default-component-class + :initform *default-component-class* + :initarg :default-component-class + :accessor module-default-component-class))) (defun component-parent-pathname (component) - (aif (component-parent component) - (component-pathname it) - (truename *default-pathname-defaults*))) - -(defmethod component-relative-pathname ((component module)) - (let ((specified-pathname (or (slot-value component 'relative-pathname) - (component-name component)))) - (if (pathnamep specified-pathname) - specified-pathname - (multiple-value-bind (relative path) - (component-name-to-pathname-components specified-pathname t) - (make-pathname - :directory `(,relative ,@path) - :host (pathname-host (component-parent-pathname component))))))) + ;; No default anymore (in particular, no *default-pathname-defaults*). + ;; If you force component to have a NULL pathname, you better arrange + ;; for any of its children to explicitly provide a proper absolute pathname + ;; wherever a pathname is actually wanted. + (let ((parent (component-parent component))) + (when parent + (component-pathname parent)))) (defmethod component-pathname ((component component)) - (merge-pathnames (component-relative-pathname component) - (component-parent-pathname component))) + (if (slot-boundp component 'absolute-pathname) + (slot-value component 'absolute-pathname) + (let ((pathname + (merge-pathnames* + (component-relative-pathname component) + (component-parent-pathname component)))) + (unless (or (null pathname) (absolute-pathname-p pathname)) + (error "Invalid relative pathname ~S for component ~S" pathname component)) + (setf (slot-value component 'absolute-pathname) pathname) + pathname))) (defmethod component-property ((c component) property) (cdr (assoc property (slot-value c 'properties) :test #'equal))) @@ -846,7 +920,8 @@ actually-existing directory." (if a (setf (cdr a) new-value) (setf (slot-value c 'properties) - (acons property new-value (slot-value c 'properties)))))) + (acons property new-value (slot-value c 'properties))))) + new-value) (defclass system (module) ((description :accessor system-description :initarg :description) @@ -869,9 +944,9 @@ actually-existing directory." (defmethod version-satisfies ((cver string) version) (let ((x (mapcar #'parse-integer - (split cver nil '(#\.)))) + (split-string cver :separator "."))) (y (mapcar #'parse-integer - (split version nil '(#\.))))) + (split-string version :separator ".")))) (labels ((bigger (x y) (cond ((not y) t) ((not x) nil) @@ -920,7 +995,7 @@ called with an object of type asdf:system." ;;; convention that functions in this list are prefixed SYSDEF- (defparameter *system-definition-search-functions* - '(sysdef-central-registry-search)) + '(sysdef-central-registry-search sysdef-source-registry-search)) (defun system-definition-pathname (system) (let ((system-name (coerce-name system))) @@ -956,21 +1031,9 @@ Going forward, we recommend new users should be using the source-registry. (let ((defaults (eval dir))) (when defaults (cond ((directory-pathname-p defaults) - (let ((file (and defaults - (make-pathname - :defaults defaults :version :newest - :name name :type "asd" :case :local))) - #+(and (or win32 windows) (not :clisp)) - (shortcut (make-pathname - :defaults defaults :version :newest - :name name :type "asd.lnk" :case :local))) - (if (and file (probe-file file)) - (return file)) - #+(and (or win32 windows) (not :clisp)) - (when (probe-file shortcut) - (let ((target (parse-windows-shortcut shortcut))) - (when target - (return (pathname target))))))) + (let ((file (probe-asd name defaults))) + (when file + (return file)))) (t (restart-case (let* ((*print-circle* nil) @@ -1015,10 +1078,11 @@ to `~a` which is not a directory.~@:>" ;; that's the case, well, that's not good, but as long as ;; the operation is otherwise considered to be done we ;; could continue and survive. - (or (file-write-date pathname) + (or (and pathname (probe-file pathname) (file-write-date pathname)) (progn - (warn "Missing FILE-WRITE-DATE for ~S: treating it as zero." - pathname) + (when pathname + (warn "Missing FILE-WRITE-DATE for ~S: treating it as zero." + pathname)) 0))) (defun find-system (name &optional (error-p t)) @@ -1030,24 +1094,26 @@ to `~a` which is not a directory.~@:>" (< (car in-memory) (safe-file-write-date on-disk)))) (let ((package (make-temporary-package))) (unwind-protect - (with-open-file (asd on-disk :if-does-not-exist nil) - (if asd - (let ((*package* package)) - (asdf-message - "~&~@<; ~@;loading system definition from ~A into ~A~@:>~%" - ;; FIXME: This wants to be (ENOUGH-NAMESTRING - ;; ON-DISK), but CMUCL barfs on that. - on-disk - *package*) - (load on-disk)) - (error 'missing-definition :name name :pathname on-disk))) + (handler-bind + ((error (lambda (condition) + (error 'load-system-definition-error + :name name :pathname on-disk + :condition condition)))) + (let ((*package* package)) + (asdf-message + "~&~@<; ~@;loading system definition from ~A into ~A~@:>~%" + ;; FIXME: This wants to be (ENOUGH-NAMESTRING + ;; ON-DISK), but CMUCL barfs on that. + on-disk + *package*) + (load on-disk))) (delete-package package)))) (let ((in-memory (system-registered-p name))) (if in-memory - (progn (if on-disk (setf (car in-memory) - (safe-file-write-date on-disk))) + (progn (when on-disk (setf (car in-memory) + (safe-file-write-date on-disk))) (cdr in-memory)) - (if error-p (error 'missing-component :requires name)))))) + (when error-p (error 'missing-component :requires name)))))) (defun register-system (name system) (asdf-message "~&~@<; ~@;registering ~A as ~A~@:>~%" system name) @@ -1060,8 +1126,7 @@ to `~a` which is not a directory.~@:>" (defmethod find-component ((module module) name &optional version) (if (slot-boundp module 'components) - (let ((m (find name (module-components module) - :test #'equal :key #'component-name))) + (let ((m (gethash name (module-components-by-name module)))) (if (and m (version-satisfies m version)) m)))) @@ -1073,40 +1138,61 @@ to `~a` which is not a directory.~@:>" ;;; component subclasses -(defclass source-file (component) ()) +(defclass source-file (component) + ((type :accessor source-file-explicit-type :initarg :type :initform nil))) -(defclass cl-source-file (source-file) ()) -(defclass c-source-file (source-file) ()) -(defclass java-source-file (source-file) ()) +(defclass cl-source-file (source-file) + ((type :initform "lisp"))) +(defclass c-source-file (source-file) + ((type :initform "c"))) +(defclass java-source-file (source-file) + ((type :initform "java"))) (defclass static-file (source-file) ()) (defclass doc-file (static-file) ()) -(defclass html-file (doc-file) ()) - -(defmethod source-file-type ((c cl-source-file) (s module)) "lisp") -(defmethod source-file-type ((c c-source-file) (s module)) "c") -(defmethod source-file-type ((c java-source-file) (s module)) "java") -(defmethod source-file-type ((c html-file) (s module)) "html") -(defmethod source-file-type ((c static-file) (s module)) nil) - -(defun merge-component-relative-pathname (pathname name type) - (multiple-value-bind (relative path filename) - (component-name-to-pathname-components name) - (merge-pathnames - (or pathname (make-pathname :directory `(,relative ,@path))) - (if type - (make-pathname :name filename :type type) - filename)))) - -(defmethod component-relative-pathname ((component source-file)) - ;; This binding of *default-pathname-defaults* is required notably because - ;; it will provide the default host to the above make-pathname, which may - ;; crucially matter to e.g. people somehow using logical-pathnames. - (let ((*default-pathname-defaults* - (component-pathname (component-parent component)))) - (merge-component-relative-pathname - (slot-value component 'relative-pathname) - (component-name component) - (source-file-type component (component-system component))))) +(defclass html-file (doc-file) + ((type :initform "html"))) + +(defmethod source-file-type ((component module) (s module)) :directory) +(defmethod source-file-type ((component source-file) (s module)) + (source-file-explicit-type component)) + +(defun merge-component-name-type (name &key type defaults) + ;; The defaults are required notably because they provide the default host + ;; to the below make-pathname, which may crucially matter to people using + ;; merge-pathnames with non-default hosts, e.g. for logical-pathnames. + ;; NOTE that the host and device slots will be taken from the defaults, + ;; but that should only matter if you either (a) use absolute pathnames, or + ;; (b) later merge relative pathnames with CL:MERGE-PATHNAMES instead of + ;; ASDF-UTILITIES:MERGE-PATHNAMES* + (etypecase name + (pathname + name) + (symbol + (merge-component-name-type (string-downcase name) :type type :defaults defaults)) + (string + (multiple-value-bind (relative path filename) + (component-name-to-pathname-components name (eq type :directory)) + (multiple-value-bind (name type) + (cond + ((or (eq type :directory) (null filename)) + (values nil nil)) + (type + (values filename type)) + (t + (split-name-type filename))) + (let* ((defaults (pathname (or defaults *default-pathname-defaults*))) + (host (pathname-host defaults)) + (device (pathname-device defaults))) + (make-pathname :directory `(,relative ,@path) + :name name :type type + :host host :device device))))))) + +(defmethod component-relative-pathname ((component component)) + (merge-component-name-type + (or (slot-value component 'relative-pathname) + (component-name component)) + :type (source-file-type component (component-system component)) + :defaults (component-parent-pathname component))) ;;;; ------------------------------------------------------------------------- ;;;; Operations @@ -1115,14 +1201,19 @@ to `~a` which is not a directory.~@:>" (defclass operation () ( - ;; what is the TYPE of this slot? seems like it should be boolean, - ;; but TRAVERSE checks to see if it's a list of component names... - ;; [2010/02/07:rpg] + ;; as of danb's 2003-03-16 commit e0d02781, :force can be: + ;; T to force the inside of existing system, + ;; but not recurse to other systems we depend on. + ;; :ALL (or any other atom) to force all systems + ;; including other systems we depend on. + ;; (SYSTEM1 SYSTEM2 ... SYSTEMN) + ;; to force systems named in a given list + ;; (but this feature never worked before ASDF 1.700 and is cerror'ed out.) (forced :initform nil :initarg :force :accessor operation-forced) (original-initargs :initform nil :initarg :original-initargs :accessor operation-original-initargs) - (visited-nodes :initform nil :accessor operation-visited-nodes) - (visiting-nodes :initform nil :accessor operation-visiting-nodes) + (visited-nodes :initform (make-hash-table :test 'equal) :accessor operation-visited-nodes) + (visiting-nodes :initform (make-hash-table :test 'equal) :accessor operation-visiting-nodes) (parent :initform nil :initarg :parent :accessor operation-parent))) (defmethod print-object ((o operation) stream) @@ -1135,7 +1226,7 @@ to `~a` which is not a directory.~@:>" &allow-other-keys) (declare (ignorable operation slot-names force)) ;; empty method to disable initarg validity checking - ) + (values)) (defun node-for (o c) (cons (class-name (class-of o)) c)) @@ -1171,30 +1262,30 @@ class specifier, not an operation." (defmethod visit-component ((o operation) (c component) data) (unless (component-visited-p o c) - (push (cons (node-for o c) data) - (operation-visited-nodes (operation-ancestor o))))) + (setf (gethash (node-for o c) + (operation-visited-nodes (operation-ancestor o))) + (cons t data)))) (defmethod component-visited-p ((o operation) (c component)) - (assoc (node-for o c) - (operation-visited-nodes (operation-ancestor o)) - :test 'equal)) + (gethash (node-for o c) + (operation-visited-nodes (operation-ancestor o)))) (defmethod (setf visiting-component) (new-value operation component) ;; MCL complains about unused lexical variables - (declare (ignorable new-value operation component))) + (declare (ignorable operation component)) + new-value) (defmethod (setf visiting-component) (new-value (o operation) (c component)) (let ((node (node-for o c)) (a (operation-ancestor o))) (if new-value - (pushnew node (operation-visiting-nodes a) :test 'equal) - (setf (operation-visiting-nodes a) - (remove node (operation-visiting-nodes a) :test 'equal))))) + (setf (gethash node (operation-visiting-nodes a)) t) + (remhash node (operation-visiting-nodes a))) + new-value)) (defmethod component-visiting-p ((o operation) (c component)) (let ((node (node-for o c))) - (member node (operation-visiting-nodes (operation-ancestor o)) - :test 'equal))) + (gethash node (operation-visiting-nodes (operation-ancestor o))))) (defmethod component-depends-on ((op-spec symbol) (c component)) (component-depends-on (make-instance op-spec) c)) @@ -1244,8 +1335,7 @@ class specifier, not an operation." ;; for its side-effects in the current image, ;; assumed to be idem-potent, ;; e.g. LOAD-OP or LOAD-SOURCE-OP of some CL-SOURCE-FILE. - (and op-time - (>= op-time (latest-in)))) + (and op-time (>= op-time (latest-in)))) ((not in-files) ;; an operation without output-files and no input-files ;; is probably meant for its side-effects on the file-system, @@ -1258,187 +1348,230 @@ class specifier, not an operation." ;; assumed to have been done if the latter are all older ;; than the former. ;; e.g. COMPILE-OP of some CL-SOURCE-FILE. + ;; We use >= instead of > to play nice with generated files. + ;; This opens a race condition if an input file is changed + ;; after the output is created but within the same second + ;; of filesystem time; but the same race condition exists + ;; whenever the computation from input to output takes more + ;; than one second of filesystem time (or just crosses the + ;; second). So that's cool. (and (every #'probe-file in-files) (every #'probe-file out-files) - (and (> (earliest-out) (latest-in))))))))) + (>= (earliest-out) (latest-in)))))))) -;;; So you look at this code and think "why isn't it a bunch of -;;; methods". And the answer is, because standard method combination -;;; runs :before methods most->least-specific, which is back to front -;;; for our purposes. + +;;; For 1.700 I've done my best to refactor TRAVERSE +;;; by splitting it up in a bunch of functions, +;;; so as to improve the collection and use-detection algorithm. --fare +;;; The protocol is as follows: we pass around operation, dependency, +;;; bunch of other stuff, and a force argument. Return a force flag. +;;; The returned flag is T if anything has changed that requires a rebuild. +;;; The force argument is a list of components that will require a rebuild +;;; if the flag is T, at which point whoever returns the flag has to +;;; mark them all as forced, and whoever recurses again can use a NIL list +;;; as a further argument. (defvar *forcing* nil "This dynamically-bound variable is used to force operations in recursive calls to traverse.") -(defmethod traverse ((operation operation) (c component)) - (let ((forced nil)) ;return value -- everyone side-effects onto this - (labels ((%do-one-dep (required-op required-c required-v) - ;; returns a partial plan that results from performing required-op - ;; on required-c, possibly with a required-vERSION - (let* ((dep-c (or (find-component - (component-parent c) - ;; XXX tacky. really we should build the - ;; in-order-to slot with canonicalized - ;; names instead of coercing this late - (coerce-name required-c) required-v) - (if required-v - (error 'missing-dependency-of-version - :required-by c - :version required-v - :requires required-c) - (error 'missing-dependency - :required-by c - :requires required-c)))) - (op (make-sub-operation c operation dep-c required-op))) - (traverse op dep-c))) - (do-one-dep (required-op required-c required-v) - ;; this function is a thin, error-handling wrapper around - ;; %do-one-dep. Returns a partial plan per that function. - (loop - (restart-case - (return (%do-one-dep required-op required-c required-v)) - (retry () - :report (lambda (s) - (format s "~@<Retry loading component ~S.~@:>" - required-c)) - :test - (lambda (c) -#| - (print (list :c1 c (typep c 'missing-dependency))) - (when (typep c 'missing-dependency) - (print (list :c2 (missing-requires c) required-c - (equalp (missing-requires c) - required-c)))) -|# - (or (null c) - (and (typep c 'missing-dependency) - (equalp (missing-requires c) - required-c)))))))) - (do-dep (op dep) - ;; type of arguments uncertain: op seems to at least potentially be a - ;; symbol, rather than an operation - ;; dep is either a list of component names (?) or (we hope) a single - ;; component name. - ;; handle a single dependency, returns nothing of interest --- side- - ;; effects onto the FORCED variable, which is scoped over TRAVERSE - (cond ((eq op 'feature) - (or (member (car dep) *features*) - (error 'missing-dependency - :required-by c - :requires (car dep)))) - (t - (dolist (d dep) - ;; structured dependencies --- this parses keywords - ;; the keywords could be broken out and cleanly (extensibly) - ;; processed by EQL methods, but for the pervasive side-effecting - ;; onto FORCED - (cond ((consp d) - (cond ((string-equal - (symbol-name (first d)) - "VERSION") - (appendf - forced - (do-one-dep op (second d) (third d)))) - ;; this particular subform is not documented, indeed - ;; clashes with the documentation, since it assumes a - ;; third component - ((and (string-equal - (symbol-name (first d)) - "FEATURE") - (find (second d) *features* - :test 'string-equal)) - (appendf - forced - (do-one-dep op (second d) (third d)))) - (t - (error "Bad dependency ~a. Dependencies must be (:version <version>), (:feature <feature> [version]), or a name" d)))) - (t - (appendf forced (do-one-dep op d nil))))))))) +(defgeneric do-traverse (operation component collect)) + +(defun %do-one-dep (operation c collect required-op required-c required-v) + ;; collects a partial plan that results from performing required-op + ;; on required-c, possibly with a required-vERSION + (let* ((dep-c (or (find-component + (component-parent c) + ;; XXX tacky. really we should build the + ;; in-order-to slot with canonicalized + ;; names instead of coercing this late + (coerce-name required-c) required-v) + (if required-v + (error 'missing-dependency-of-version + :required-by c + :version required-v + :requires required-c) + (error 'missing-dependency + :required-by c + :requires required-c)))) + (op (make-sub-operation c operation dep-c required-op))) + (do-traverse op dep-c collect))) + +(defun do-one-dep (operation c collect required-op required-c required-v) + ;; this function is a thin, error-handling wrapper around + ;; %do-one-dep. Returns a partial plan per that function. + (loop + (restart-case + (return (%do-one-dep operation c collect + required-op required-c required-v)) + (retry () + :report (lambda (s) + (format s "~@<Retry loading component ~S.~@:>" + required-c)) + :test + (lambda (c) + #| + (print (list :c1 c (typep c 'missing-dependency))) + (when (typep c 'missing-dependency) + (print (list :c2 (missing-requires c) required-c + (equalp (missing-requires c) + required-c)))) + |# + (or (null c) + (and (typep c 'missing-dependency) + (equalp (missing-requires c) + required-c)))))))) + +(defun do-dep (operation c collect op dep) + ;; type of arguments uncertain: + ;; op seems to at least potentially be a symbol, rather than an operation + ;; dep is a list of component names + (cond ((eq op 'feature) + (if (member (car dep) *features*) + nil + (error 'missing-dependency + :required-by c + :requires (car dep)))) + (t + (let ((flag nil)) + (flet ((dep (op comp ver) + (when (do-one-dep operation c collect + op comp ver) + (setf flag t)))) + (dolist (d dep) + (if (atom d) + (dep op d nil) + ;; structured dependencies --- this parses keywords + ;; the keywords could be broken out and cleanly (extensibly) + ;; processed by EQL methods + (cond ((eq :version (first d)) + ;; https://bugs.launchpad.net/asdf/+bug/527788 + (dep op (second d) (third d))) + ;; This particular subform is not documented and + ;; has always been broken in the past. + ;; Therefore no one uses it, and I'm cerroring it out, + ;; after fixing it + ;; See https://bugs.launchpad.net/asdf/+bug/518467 + ((eq :feature (first d)) + (cerror "Continue nonetheless." + "Congratulations, you're the first ever user of FEATURE dependencies! Please contact the asdf-devel mailing-list.") + (when (find (second d) *features* :test 'string-equal) + (dep op (third d) nil))) + (t + (error "Bad dependency ~a. Dependencies must be (:version <version>), (:feature <feature> [version]), or a name" d)))))) + flag)))) + +(defun do-collect (collect x) + (funcall collect x)) + +(defmethod do-traverse ((operation operation) (c component) collect) + (let ((flag nil)) ;; return value: must we rebuild this and its dependencies? + (labels + ((update-flag (x) + (when x + (setf flag t))) + (dep (op comp) + (update-flag (do-dep operation c collect op comp)))) + ;; Have we been visited yet? If so, just process the result. (aif (component-visited-p operation c) - (return-from traverse - (if (cdr it) (list (cons 'pruned-op c)) nil))) + (progn + (update-flag (cdr it)) + (return-from do-traverse flag))) ;; dependencies - (if (component-visiting-p operation c) - (error 'circular-dependency :components (list c))) + (when (component-visiting-p operation c) + (error 'circular-dependency :components (list c))) (setf (visiting-component operation c) t) (unwind-protect - (progn - ;; first we check and do all the dependencies for the - ;; module. Operations planned in this loop will show up - ;; in the contents of the FORCED variable, and are consumed - ;; downstream (watch out for the shadowing FORCED variable - ;; around the DOLIST below!) - (let ((*forcing* nil)) - ;; upstream dependencies are never forced to happen just because - ;; the things that depend on them are.... - (loop :for (required-op . deps) :in - (component-depends-on operation c) - :do (do-dep required-op deps))) - ;; constituent bits - (let ((module-ops - (when (typep c 'module) - (let ((at-least-one nil) - (forced nil) - ;; this is set based on the results of the - ;; dependencies and whether we are in the - ;; context of a *forcing* call... - (must-operate (or *forcing* - ;; inter-system dependencies do NOT trigger - ;; building components - (and - (not (typep c 'system)) - forced))) - (error nil)) - (dolist (kid (module-components c)) - (handler-case - (let ((*forcing* must-operate)) - (appendf forced (traverse operation kid))) - (missing-dependency (condition) - (when (eq (module-if-component-dep-fails c) - :fail) - (error condition)) - (setf error condition)) - (:no-error (c) - (declare (ignore c)) - (setf at-least-one t)))) - (when (and (eq (module-if-component-dep-fails c) - :try-next) - (not at-least-one)) - (error error)) - forced)))) - ;; now the thing itself - ;; the test here is a bit oddly written. FORCED here doesn't - ;; mean that this operation is forced on this component, but that - ;; something upstream of this component has been forced. - (when (or forced module-ops - *forcing* - (not (operation-done-p operation c)) - (let ((f (operation-forced - (operation-ancestor operation)))) - ;; does anyone fully understand the following condition? - ;; if so, please add a comment to explain it... - (and f (or (not (consp f)) - (member (component-name - (operation-ancestor operation)) - (mapcar #'coerce-name f) - ;; this was string=, but for the benefit - ;; of mlisp, we use string-equal for this - ;; purpose. - :test #'string-equal))))) - (let ((do-first (cdr (assoc (class-name (class-of operation)) - (component-do-first c))))) - (loop :for (required-op . deps) :in do-first - :do (do-dep required-op deps))) - (setf forced (append (delete 'pruned-op forced :key #'car) - (delete 'pruned-op module-ops :key #'car) - (list (cons operation c))))))) - (setf (visiting-component operation c) nil)) - (visit-component operation c (and forced t)) - forced))) + (progn + ;; first we check and do all the dependencies for the module. + ;; Operations planned in this loop will show up + ;; in the results, and are consumed below. + (let ((*forcing* nil)) + ;; upstream dependencies are never forced to happen just because + ;; the things that depend on them are.... + (loop + :for (required-op . deps) :in (component-depends-on operation c) + :do (dep required-op deps))) + ;; constituent bits + (let ((module-ops + (when (typep c 'module) + (let ((at-least-one nil) + ;; This is set based on the results of the + ;; dependencies and whether we are in the + ;; context of a *forcing* call... + ;; inter-system dependencies do NOT trigger + ;; building components + (*forcing* + (or *forcing* + (and flag (not (typep c 'system))))) + (error nil)) + (while-collecting (internal-collect) + (dolist (kid (module-components c)) + (handler-case + (update-flag + (do-traverse operation kid #'internal-collect)) + (missing-dependency (condition) + (when (eq (module-if-component-dep-fails c) + :fail) + (error condition)) + (setf error condition)) + (:no-error (c) + (declare (ignore c)) + (setf at-least-one t)))) + (when (and (eq (module-if-component-dep-fails c) + :try-next) + (not at-least-one)) + (error error))))))) + (update-flag + (or + *forcing* + (not (operation-done-p operation c)) + ;; For sub-operations, check whether + ;; the original ancestor operation was forced, + ;; or names us amongst an explicit list of things to force... + ;; except that this check doesn't distinguish + ;; between all the things with a given name. Sigh. + ;; BROKEN! + (let ((f (operation-forced + (operation-ancestor operation)))) + (and f (or (not (consp f)) ;; T or :ALL + (and (typep c 'system) ;; list of names of systems to force + (member (component-name c) f + :test #'string=))))))) + (when flag + (let ((do-first (cdr (assoc (class-name (class-of operation)) + (component-do-first c))))) + (loop :for (required-op . deps) :in do-first + :do (do-dep operation c collect required-op deps))) + (do-collect collect (vector module-ops)) + (do-collect collect (cons operation c))))) + (setf (visiting-component operation c) nil))) + (visit-component operation c flag) + flag)) +(defmethod traverse ((operation operation) (c component)) + ;; cerror'ing a feature that seems to have NEVER EVER worked, + ;; ever since danb created it in his 2003-03-16 commit e0d02781 + (when (consp (operation-forced operation)) + (cerror "Continue nonetheless." + "Congratulations, you're the first ever user of the :force (list of system names) feature! Please contact the asdf-devel mailing-list to collect a cookie.") + (setf (operation-forced operation) + (mapcar #'coerce-name (operation-forced operation)))) + (flatten-tree + (while-collecting (collect) + (do-traverse operation c #'collect)))) + +(defun flatten-tree (l) + (while-collecting (c) + (labels ((r (x) + (if (vectorp x) + (r* (svref x 0)) + (c x))) + (r* (l) + (dolist (x l) (r x)))) + (r* l)))) (defmethod perform ((operation operation) (c source-file)) (sysdef-error @@ -1472,7 +1605,7 @@ recursive calls to traverse.") ;; Note how we use OUTPUT-FILES to find the binary locations ;; This allows the user to override the names. (let* ((input (output-files o c)) - (output (compile-file-pathname (first input) :type :fasl))) + (output (compile-file-pathname (lispize-pathname (first input)) :type :fasl))) (c:build-fasl output :lisp-files (remove "fas" input :key #'pathname-type :test #'string=)))) (defmethod perform :after ((operation operation) (c component)) @@ -1506,11 +1639,12 @@ recursive calls to traverse.") (error 'compile-error :component c :operation operation))))) (defmethod output-files ((operation compile-op) (c cl-source-file)) - #-:broken-fasl-loader - (list #-ecl (compile-file-pathname (component-pathname c)) - #+ecl (compile-file-pathname (component-pathname c) :type :object) - #+ecl (compile-file-pathname (component-pathname c) :type :fasl)) - #+:broken-fasl-loader (list (component-pathname c))) + (let ((p (lispize-pathname (component-pathname c)))) + #-:broken-fasl-loader + (list #-ecl (compile-file-pathname p) + #+ecl (compile-file-pathname p :type :object) + #+ecl (compile-file-pathname p :type :fasl)) + #+:broken-fasl-loader (list p))) (defmethod perform ((operation compile-op) (c static-file)) nil) @@ -1533,10 +1667,13 @@ recursive calls to traverse.") #-ecl (mapcar #'load (input-files o c)) #+ecl (loop :for i :in (input-files o c) :unless (string= (pathname-type i) "fas") - :collect (let ((output (compile-file-pathname i))) + :collect (let ((output (compile-file-pathname (lispize-pathname i)))) (load output)))) -(defmethod perform around ((o load-op) (c cl-source-file)) +(defmethod perform-with-restarts (operation component) + (perform operation component)) + +(defmethod perform-with-restarts ((o load-op) (c cl-source-file)) (let ((state :initial)) (loop :until (or (eq state :success) (eq state :failure)) :do @@ -1547,7 +1684,7 @@ recursive calls to traverse.") (setf state :success)) (:failed-load (setf state :recompiled) - (perform (make-instance 'asdf:compile-op) c)) + (perform (make-instance 'compile-op) c)) (t (with-simple-restart (try-recompiling "Recompile ~a and try loading it again" @@ -1556,26 +1693,6 @@ recursive calls to traverse.") (call-next-method) (setf state :success))))))) -(defmethod perform around ((o compile-op) (c cl-source-file)) - (let ((state :initial)) - (loop :until (or (eq state :success) - (eq state :failure)) :do - (case state - (:recompiled - (setf state :failure) - (call-next-method) - (setf state :success)) - (:failed-compile - (setf state :recompiled) - (perform (make-instance 'asdf:compile-op) c)) - (t - (with-simple-restart - (try-recompiling "Try recompiling ~a" - (component-name c)) - (setf state :failed-compile) - (call-next-method) - (setf state :success))))))) - (defmethod perform ((operation load-op) (c static-file)) nil) @@ -1642,7 +1759,8 @@ recursive calls to traverse.") ;;;; ------------------------------------------------------------------------- ;;;; Invoking Operations -(defun operate (operation-class system &rest args &key (verbose t) version force +(defun operate (operation-class system &rest args + &key ((:verbose *asdf-verbose*) *asdf-verbose*) version force &allow-other-keys) (declare (ignore force)) (let* ((*package* *package*) @@ -1650,7 +1768,7 @@ recursive calls to traverse.") (op (apply #'make-instance operation-class :original-initargs args args)) - (*verbose-out* (if verbose *standard-output* (make-broadcast-stream))) + (*verbose-out* (if *asdf-verbose* *standard-output* (make-broadcast-stream))) (system (if (typep system 'component) system (find-system system)))) (unless (version-satisfies system version) (error 'missing-component-of-version :requires system :version version)) @@ -1659,7 +1777,7 @@ recursive calls to traverse.") (loop :for (op . component) :in steps :do (loop (restart-case - (progn (perform op component) + (progn (perform-with-restarts op component) (return)) (retry () :report @@ -1678,7 +1796,7 @@ recursive calls to traverse.") (return))))))) op)) -(defun oos (operation-class system &rest args &key force (verbose t) version +(defun oos (operation-class system &rest args &key force verbose version &allow-other-keys) (declare (ignore force verbose version)) (apply #'operate operation-class system args)) @@ -1708,21 +1826,21 @@ created with the same initargs as the original one. (setf (documentation 'operate 'function) operate-docstring)) -(defun load-system (system &rest args &key force (verbose t) version +(defun load-system (system &rest args &key force verbose version &allow-other-keys) "Shorthand for `(operate 'asdf:load-op system)`. See OPERATE for details." (declare (ignore force verbose version)) (apply #'operate 'load-op system args)) -(defun compile-system (system &rest args &key force (verbose t) version +(defun compile-system (system &rest args &key force verbose version &allow-other-keys) "Shorthand for `(operate 'asdf:compile-op system)`. See OPERATE for details." (declare (ignore force verbose version)) (apply #'operate 'compile-op system args)) -(defun test-system (system &rest args &key force (verbose t) version +(defun test-system (system &rest args &key force verbose version &allow-other-keys) "Shorthand for `(operate 'asdf:test-op system)`. See OPERATE for details." @@ -1743,23 +1861,27 @@ details." ;; to resolve symbolic links. If not resolving symlinks, then we use ;; *load-pathname* instead of *load-truename* since in some ;; implementations, the latter has *already resolved it. - (or (and pathname-supplied-p pathname) - (when *load-pathname* - (pathname-directory-pathname - (if *resolve-symlinks* - (resolve-symlinks *load-truename*) - *load-pathname*))) - *default-pathname-defaults*)) + (let ((file-pathname + (when (or *load-pathname* *compile-file-pathname*) + (pathname-directory-pathname + (if *resolve-symlinks* + (resolve-symlinks (or *load-truename* *compile-file-truename*)) + *load-pathname*))))) + (or (and pathname-supplied-p (merge-pathnames* pathname file-pathname)) + file-pathname + (current-directory)))) (defmacro defsystem (name &body options) (destructuring-bind (&key (pathname nil pathname-arg-p) (class 'system) - &allow-other-keys) + system-dependencies &allow-other-keys) options - (let ((component-options (remove-keyword :class options))) + (let ((component-options (remove-keys '(:system-dependencies :class) options))) `(progn ;; system must be registered before we parse the body, otherwise ;; we recur when trying to find an existing system of the same name ;; to reuse options (e.g. pathname) from + ,@(loop for system in system-dependencies + collect `(asdf:load-system ,system)) (let ((s (system-registered-p ',name))) (cond ((and s (eq (type-of (cdr s)) ',class)) (setf (car s) (get-universal-time))) @@ -1823,7 +1945,7 @@ Returns the new tree (which probably shares structure with the old one)" new-tree)) -(defvar *serial-depends-on*) +(defvar *serial-depends-on* nil) (defun sysdef-error-component (msg type name value) (sysdef-error (concatenate 'string msg @@ -1850,7 +1972,7 @@ Returns the new tree (which probably shares structure with the old one)" (dolist (name +asdf-methods+) (map () ;; this is inefficient as most of the stored - ;; methods will not be for this particular gf n + ;; methods will not be for this particular gf ;; But this is hardly performance-critical (lambda (m) (remove-method (symbol-function name) m)) @@ -1877,7 +1999,6 @@ Returns the new tree (which probably shares structure with the old one)" (%define-component-inline-methods component rest)) (defun parse-component-form (parent options) - (destructuring-bind (type name &rest rest &key ;; the following list of keywords is reproduced below in the @@ -1909,15 +2030,15 @@ Returns the new tree (which probably shares structure with the old one)" (or (find-component parent name) (make-instance (class-for-type parent type))))) (when weakly-depends-on - (setf depends-on (append depends-on (remove-if (complement #'find-system) weakly-depends-on)))) - (when (boundp '*serial-depends-on*) - (setf depends-on - (concatenate 'list *serial-depends-on* depends-on))) + (appendf depends-on (remove-if (complement #'find-system) weakly-depends-on))) + (when *serial-depends-on* + (push *serial-depends-on* depends-on)) (apply #'reinitialize-instance ret :name (coerce-name name) :pathname pathname :parent parent other-args) + (component-pathname ret) ; eagerly compute the absolute pathname (when (typep ret 'module) (setf (module-default-component-class ret) (or default-component-class @@ -1925,32 +2046,24 @@ Returns the new tree (which probably shares structure with the old one)" (module-default-component-class parent)))) (let ((*serial-depends-on* nil)) (setf (module-components ret) - (loop :for c-form :in components + (loop + :for c-form :in components :for c = (parse-component-form ret c-form) + :for name = (component-name c) :collect c - :if serial - :do (push (component-name c) *serial-depends-on*)))) - - ;; check for duplicate names - (let ((name-hash (make-hash-table :test #'equal))) - (loop :for c in (module-components ret) :do - (if (gethash (component-name c) - name-hash) - (error 'duplicate-names - :name (component-name c)) - (setf (gethash (component-name c) - name-hash) - t))))) + :when serial :do (setf *serial-depends-on* name)))) + (compute-module-components-by-name ret)) + + (setf (component-load-dependencies ret) depends-on) ;; Used by POIU (setf (component-in-order-to ret) (union-of-dependencies in-order-to `((compile-op (compile-op ,@depends-on)) - (load-op (load-op ,@depends-on)))) - (component-do-first ret) `((compile-op (load-op ,@depends-on)))) + (load-op (load-op ,@depends-on))))) + (setf (component-do-first ret) `((compile-op (load-op ,@depends-on)))) (%refresh-component-inline-methods ret rest) - ret))) ;;;; --------------------------------------------------------------------------- @@ -2003,8 +2116,8 @@ output to `*verbose-out*`. Returns the shell's exit code." (system:call-system-showing-output command :shell-type "/bin/sh" - :prefix "" :show-cmd nil + :prefix "" :output-stream *verbose-out*) #+clisp ;XXX not exactly *verbose-out*, I know @@ -2020,20 +2133,28 @@ output to `*verbose-out*`. Returns the shell's exit code." #+ecl ;; courtesy of Juan Jose Garcia Ripoll (si:system command) - #-(or openmcl clisp lispworks allegro scl cmu sbcl ecl) + #+abcl + (ext:run-shell-command command :output *verbose-out*) + + #-(or openmcl clisp lispworks allegro scl cmu sbcl ecl abcl) (error "RUN-SHELL-COMMAND not implemented for this Lisp") )) ;;;; --------------------------------------------------------------------------- ;;;; system-relative-pathname -(defmethod system-source-file ((system-name t)) +(defmethod system-source-file ((system-name string)) + (system-source-file (find-system system-name))) +(defmethod system-source-file ((system-name symbol)) (system-source-file (find-system system-name))) -(defun system-source-directory (system-name) - (make-pathname :name nil +(defun system-source-directory (system-designator) + "Return a pathname object corresponding to the +directory in which the system specification (.asd file) is +located." + (make-pathname :name nil :type nil - :defaults (system-source-file system-name))) + :defaults (system-source-file system-designator))) (defun relativize-directory (directory) (if (eq (car directory) :absolute) @@ -2046,13 +2167,10 @@ output to `*verbose-out*`. Returns the shell's exit code." :directory (relativize-directory (pathname-directory p)) :defaults p))) -(defun system-relative-pathname (system pathname &key name type) - (let ((directory (pathname-directory pathname))) - (merge-pathnames - (make-pathname :name (or name (pathname-name pathname)) - :type (or type (pathname-type pathname)) - :directory (relativize-directory directory)) - (system-source-directory system)))) +(defun system-relative-pathname (system name &key type) + (merge-pathnames* + (merge-component-name-type name :type type) + (system-source-directory system))) ;;; --------------------------------------------------------------------------- @@ -2148,10 +2266,52 @@ output to `*verbose-out*`. Returns the shell's exit code." ;;; --------------------------------------------------------------------------- ;;; Generic support for configuration files -(defun user-configuration-directory () - (merge-pathnames #p".config/" (user-homedir-pathname))) -(defun system-configuration-directory () - #p"/etc/") + +(defparameter *inter-directory-separator* + #+(or unix cygwin) #\: + #-(or unix cygwin) #\;) + +(defun user-homedir () + (truename (user-homedir-pathname))) + +(defun try-directory-subpath (x sub &key type) + (let* ((p (and x (ensure-directory-pathname x))) + (tp (and p (ignore-errors (truename p)))) + (sp (and tp (merge-pathnames* (merge-component-name-type sub :type type) p))) + (ts (and sp (ignore-errors (truename sp))))) + (and ts (values sp ts)))) +(defun user-configuration-directories () + (remove-if + #'null + (flet ((try (x sub) (try-directory-subpath x sub :type :directory))) + `(,(try (getenv "XDG_CONFIG_HOME") "common-lisp/") + ,@(loop :with dirs = (getenv "XDG_CONFIG_DIRS") + :for dir :in (split-string dirs :separator ":") + :collect (try dir "common-lisp/")) + #+(and (or win32 windows mswindows mingw32) (not cygwin)) + ,@`(#+lispworks ,(try (sys:get-folder-path :common-appdata) "common-lisp/config/") + ;;; read-windows-registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\AppData + #+(not cygwin) + ,(try (or (getenv "USERPROFILE") (user-homedir)) + "Application Data/common-lisp/config/")) + ,(try (user-homedir) ".config/common-lisp/"))))) +(defun system-configuration-directories () + (remove-if + #'null + (append + #+(and (or win32 windows mswindows mingw32) (not cygwin)) + (flet ((try (x sub) (try-directory-subpath x sub :type :directory))) + `(,@`(#+lispworks ,(try (sys:get-folder-path :local-appdata) "common-lisp/config/") + ;;; read-windows-registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Common AppData + ,(try (getenv "ALLUSERSPROFILE") "Application Data/common-lisp/config/")))) + (list #p"/etc/")))) +(defun in-first-directory (dirs x) + (loop :for dir :in dirs + :thereis (and dir (ignore-errors (truename (merge-pathnames* x (ensure-directory-pathname dir))))))) +(defun in-user-configuration-directory (x) + (in-first-directory (user-configuration-directories) x)) +(defun in-system-configuration-directory (x) + (in-first-directory (system-configuration-directories) x)) (defun configuration-inheritance-directive-p (x) (let ((kw '(:inherit-configuration :ignore-inherited-configuration))) @@ -2181,9 +2341,7 @@ output to `*verbose-out*`. Returns the shell's exit code." (defun validate-configuration-directory (directory tag validator) (let ((files (sort (ignore-errors - (directory (merge-pathnames - (make-pathname :name :wild :type :wild) - directory) + (directory (make-pathname :name :wild :type :wild :defaults directory) #+sbcl :resolve-symlinks #+sbcl nil)) #'string< :key #'namestring))) `(,tag @@ -2205,17 +2363,42 @@ said element itself being a sorted list of mappings. Each mapping is a pair of a source pathname and destination pathname, and the order is by decreasing length of namestring of the source pathname.") -(defvar *user-cache* '(:home ".cache" "common-lisp" :implementation)) -(defvar *system-cache* '(:root "var" "cache" "common-lisp" :uid :implementation)) +(defvar *user-cache* + (or + (let ((h (getenv "XDG_CACHE_HOME"))) + (and h `(,h "common-lisp" :implementation))) + #+(and windows lispworks) + (let ((h (sys:get-folder-path :common-appdata))) ;; no :common-caches in Windows??? + (and h `(,h "common-lisp" "cache"))) + #+(and (or win32 windows mswindows mingw32) (not cygwin)) + ;;; read-windows-registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Cache + (let ((h (or (getenv "USERPROFILE") (user-homedir)))) + (and h `(,h "Local Settings" "Temporary Internet Files" "common-lisp"))) + '(:home ".cache" "common-lisp" :implementation))) +(defvar *system-cache* + (or + #+(and windows lispworks) + (let ((h (sys:get-folder-path :common-appdata))) ;; no :common-caches in Windows??? + (and h `(,h "common-lisp" "cache"))) + #+(and (or win32 windows mswindows mingw32) (not cygwin)) + (let ((h (or (getenv "USERPROFILE") (user-homedir)))) + (and h `(,h "Local Settings" "Temporary Internet Files" "common-lisp"))) + #+(or unix cygwin) + '("/var/cache/common-lisp" :uid :implementation))) (defun output-translations () (car *output-translations*)) -(defun (setf output-translations) (x) +(defun (setf output-translations) (new-value) (setf *output-translations* (list - (stable-sort (copy-list x) #'> - :key (lambda (x) (length (pathname-directory (car x)))))))) + (stable-sort (copy-list new-value) #'> + :key (lambda (x) + (etypecase (car x) + ((eql t) -1) + (pathname + (length (pathname-directory (car x))))))))) + new-value) (defun output-translations-initialized-p () (and *output-translations* t)) @@ -2229,21 +2412,36 @@ with a different configuration, so the configuration would be re-read then." (defparameter *wild-path* (make-pathname :directory '(:relative :wild-inferiors) - :name :wild :type :wild :version nil)) + :name :wild :type :wild :version :wild)) + +(defparameter *wild-asd* + (make-pathname :directory '(:relative :wild-inferiors) + :name :wild :type "asd" :version :newest)) (defun wilden (path) - (merge-pathnames *wild-path* path)) + (merge-pathnames* *wild-path* path)) (defun resolve-absolute-location-component (x wildenp) (let* ((r (etypecase x (pathname x) (string (ensure-directory-pathname x)) - ((eql :home) (user-homedir-pathname)) + (cons + (let ((car (resolve-absolute-location-component (car x) nil))) + (if (null (cdr x)) + car + (let ((cdr (resolve-relative-location-component + car (cdr x) wildenp))) + (merge-pathnames* cdr car))))) + ((eql :root) + ;; special magic! we encode such paths as relative pathnames, + ;; but it means "relative to the root of the source pathname's host and device". + (return-from resolve-absolute-location-component + (make-pathname :directory '(:relative)))) + ((eql :home) (user-homedir)) ((eql :user-cache) (resolve-location *user-cache* nil)) ((eql :system-cache) (resolve-location *system-cache* nil)) - ((eql :current-directory) (truenamize *default-pathname-defaults*)) - ((eql :root) (make-pathname :directory '(:absolute))))) + ((eql :current-directory) (current-directory)))) (s (if (and wildenp (not (pathnamep x))) (wilden r) r))) @@ -2255,11 +2453,17 @@ with a different configuration, so the configuration would be re-read then." (let* ((r (etypecase x (pathname x) (string x) + (cons + (let ((car (resolve-relative-location-component super (car x) nil))) + (if (null (cdr x)) + car + (let ((cdr (resolve-relative-location-component + (merge-pathnames* car super) (cdr x) wildenp))) + (merge-pathnames* cdr car))))) ((eql :current-directory) - (relativize-pathname-directory - (truenamize *default-pathname-defaults*))) + (relativize-pathname-directory (current-directory))) ((eql :implementation) (implementation-identifier)) - ((eql :implementation-type) (princ-to-string (implementation-type))) + ((eql :implementation-type) (string-downcase (implementation-type))) ((eql :uid) (princ-to-string (get-uid))))) (d (if (pathnamep x) r (ensure-directory-pathname r))) (s (if (and wildenp (not (pathnamep x))) @@ -2267,7 +2471,7 @@ with a different configuration, so the configuration would be re-read then." d))) (when (and (absolute-pathname-p s) (not (pathname-match-p s (wilden super)))) (error "pathname ~S is not relative to ~S" s super)) - (merge-pathnames s super))) + (merge-pathnames* s super))) (defun resolve-location (x &optional wildenp) (if (atom x) @@ -2280,7 +2484,17 @@ with a different configuration, so the configuration would be re-read then." (defun location-designator-p (x) (flet ((componentp (c) (typep c '(or string pathname keyword)))) - (or (componentp x) (and (consp x) (every #'componentp x))))) + (or (typep x 'boolean) (componentp x) (and (consp x) (every #'componentp x))))) + +(defun location-function-p (x) + (and + (consp x) + (length=n-p x 2) + (or (and (equal (first x) :function) + (typep (second x) 'symbol)) + (and (equal (first x) 'lambda) + (cddr x) + (length=n-p (second x) 2))))) (defun validate-output-translations-directive (directive) (unless @@ -2290,10 +2504,10 @@ with a different configuration, so the configuration would be re-read then." (and (consp directive) (or (and (length=n-p directive 2) (or (and (eq (first directive) :include) - (typep (second directive) '(or string pathname))) + (typep (second directive) '(or string pathname null))) (and (location-designator-p (first directive)) (or (location-designator-p (second directive)) - (null (second directive)))))) + (location-function-p (second directive)))))) (and (length=n-p directive 1) (location-designator-p (first directive)))))) (error "Invalid directive ~S~%" directive)) @@ -2320,7 +2534,9 @@ with a different configuration, so the configuration would be re-read then." '(:output-translations :inherit-configuration)) ((not (stringp string)) (error "environment string isn't: ~S" string)) - ((find (char string 0) "\"(") + ((eql (char string 0) #\") + (parse-output-translations-string (read-from-string string))) + ((eql (char string 0) #\() (validate-output-translations-form (read-from-string string))) (t (loop @@ -2329,7 +2545,7 @@ with a different configuration, so the configuration would be re-read then." :with start = 0 :with end = (length string) :with source = nil - :for i = (or (position #\: string :start start) end) :do + :for i = (or (position *inter-directory-separator* string :start start) end) :do (let ((s (subseq string start i))) (cond (source @@ -2343,7 +2559,7 @@ with a different configuration, so the configuration would be re-read then." (t (setf source s))) (setf start (1+ i)) - (when (>= start end) + (when (> start end) (when source (error "Uneven number of components in source to destination mapping ~S" string)) (unless inherit @@ -2351,38 +2567,37 @@ with a different configuration, so the configuration would be re-read then." (return `(:output-translations ,@(nreverse directives))))))))) (defparameter *default-output-translations* - '(implementation-output-translations + '(environment-output-translations user-output-translations-pathname user-output-translations-directory-pathname system-output-translations-pathname system-output-translations-directory-pathname)) -(defparameter *implementation-output-translations* +(defun wrapping-output-translations () `(:output-translations - ;; If clozure had any precompiled ASDF system, we'd use that: - ; #+clozure (,(ccl::ccl-directory) ()) - ;; SBCL *does* have precompiled ASDF system, so we use this: - #+sbcl (,(getenv "SBCL_HOME") ()) - ;; All-import, here is where we want user stuff to be: - :inherit-configuration - ;; If we want to enable the user cache by default, here would be the place: - :enable-user-cache - )) - -(defun implementation-output-translations () - *implementation-output-translations*) - -(defparameter *output-translations-file* #p"common-lisp/asdf-output-translations.conf") -(defparameter *output-translations-directory* #p"common-lisp/asdf-output-translations.conf.d/") + ;; Some implementations have precompiled ASDF systems, + ;; so we must disable translations for implementation paths. + #+sbcl (,(getenv "SBCL_HOME") ()) + #+ecl (,(translate-logical-pathname "SYS:**;*.*") ()) ; only needed if LPNs are resolved manually. + #+clozure (,(wilden (ccl::ccl-directory)) ()) ; not needed: no precompiled ASDF system + #+abcl (#p"jar:file:/**/*.jar!/**/*.*" (:function translate-jar-pathname)) + #+abcl (#p"/:jar:file/**/*.*" (:user-cache #p"**/*.*")) + ;; All-import, here is where we want user stuff to be: + :inherit-configuration + ;; If we want to enable the user cache by default, here would be the place: + :enable-user-cache)) + +(defparameter *output-translations-file* #p"asdf-output-translations.conf") +(defparameter *output-translations-directory* #p"asdf-output-translations.conf.d/") (defun user-output-translations-pathname () - (merge-pathnames *output-translations-file* (user-configuration-directory))) + (in-user-configuration-directory *output-translations-file* )) (defun system-output-translations-pathname () - (merge-pathnames *output-translations-file* (system-configuration-directory))) + (in-system-configuration-directory *output-translations-file*)) (defun user-output-translations-directory-pathname () - (merge-pathnames *output-translations-directory* (user-configuration-directory))) + (in-user-configuration-directory *output-translations-directory*)) (defun system-output-translations-directory-pathname () - (merge-pathnames *output-translations-directory* (system-configuration-directory))) + (in-system-configuration-directory *output-translations-directory*)) (defun environment-output-translations () (getenv "ASDF_OUTPUT_TRANSLATIONS")) @@ -2391,9 +2606,7 @@ with a different configuration, so the configuration would be re-read then." (inherit *default-output-translations*) collect) (process-output-translations (funcall x) :inherit inherit :collect collect)) -(defmethod process-output-translations ((pathname pathname) &key - (inherit *default-output-translations*) - collect) +(defmethod process-output-translations ((pathname pathname) &key inherit collect) (cond ((directory-pathname-p pathname) (process-output-translations (validate-output-translations-directory pathname) @@ -2403,26 +2616,15 @@ with a different configuration, so the configuration would be re-read then." :inherit inherit :collect collect)) (t (inherit-output-translations inherit :collect collect)))) -(defmethod process-output-translations ((string string) &key - (inherit *default-output-translations*) - collect) +(defmethod process-output-translations ((string string) &key inherit collect) (process-output-translations (parse-output-translations-string string) :inherit inherit :collect collect)) -(defmethod process-output-translations ((x null) &key - (inherit *default-output-translations*) - collect) +(defmethod process-output-translations ((x null) &key inherit collect) (declare (ignorable x)) (inherit-output-translations inherit :collect collect)) -(defmethod process-output-translations ((form cons) &key - (inherit *default-output-translations*) - collect) - (multiple-value-bind (collect result) - (if collect - (values collect (constantly nil)) - (make-collector)) - (dolist (directive (cdr (validate-output-translations-form form))) - (process-output-translations-directive directive :inherit inherit :collect collect)) - (funcall result))) +(defmethod process-output-translations ((form cons) &key inherit collect) + (dolist (directive (cdr (validate-output-translations-form form))) + (process-output-translations-directive directive :inherit inherit :collect collect))) (defun inherit-output-translations (inherit &key collect) (when inherit @@ -2432,9 +2634,9 @@ with a different configuration, so the configuration would be re-read then." (if (atom directive) (ecase directive ((:enable-user-cache) - (process-output-translations-directive '(:root :user-cache) :collect collect)) + (process-output-translations-directive '(t :user-cache) :collect collect)) ((:disable-cache) - (process-output-translations-directive '(:root :root) :collect collect)) + (process-output-translations-directive '(t t) :collect collect)) ((:inherit-configuration) (inherit-output-translations inherit :collect collect)) ((:ignore-inherited-configuration) @@ -2442,17 +2644,48 @@ with a different configuration, so the configuration would be re-read then." (let ((src (first directive)) (dst (second directive))) (if (eq src :include) - (process-output-translations (pathname dst) :inherit nil :collect collect) - (let* ((trusrc (truenamize (resolve-location src t))) - (trudst (if dst (resolve-location dst t) trusrc))) - (funcall collect (list trusrc trudst))))))) - -;; Will read the configuration and initialize all internal variables, -;; and return the new configuration. -(defun initialize-output-translations - (&optional (translations *default-output-translations*)) - (setf (output-translations) - (inherit-output-translations translations))) + (when dst + (process-output-translations (pathname dst) :inherit nil :collect collect)) + (when src + (let ((trusrc (or (eql src t) + (let ((loc (resolve-location src t))) + (if (absolute-pathname-p loc) (truenamize loc) loc))))) + (cond + ((location-function-p dst) + (funcall collect + (list trusrc + (if (symbolp (second dst)) + (fdefinition (second dst)) + (eval (second dst)))))) + ((eq dst t) + (funcall collect (list trusrc t))) + (t + (let* ((trudst (make-pathname + :defaults (if dst (resolve-location dst t) trusrc))) + (wilddst (make-pathname + :name :wild :type :wild :version :wild + :defaults trudst))) + (funcall collect (list wilddst t)) + (funcall collect (list trusrc trudst))))))))))) + +(defun compute-output-translations (&optional parameter) + "read the configuration, return it" + (remove-duplicates + (while-collecting (c) + (inherit-output-translations + `(wrapping-output-translations ,parameter ,@*default-output-translations*) :collect #'c)) + :test 'equal :from-end t)) + +(defun initialize-output-translations (&optional parameter) + "read the configuration, initialize the internal configuration variable, +return the configuration" + (setf (output-translations) (compute-output-translations parameter))) + +(defun disable-output-translations () + "Initialize output translations in a way that maps every file to itself, +effectively disabling the output translation facility." + (initialize-output-translations + '(:output-translations :disable-cache :ignore-inherited-configuration))) ;; checks an initial variable to see whether the state is initialized ;; or cleared. In the former case, return current configuration; in @@ -2464,23 +2697,123 @@ with a different configuration, so the configuration would be re-read then." (initialize-output-translations))) (defun apply-output-translations (path) - (ensure-output-translations) - (setf path (truenamize path)) - (loop :for (source destination) :in (car *output-translations*) - :when (pathname-match-p path source) - :return (translate-pathname path source destination) - :finally (return path))) - -(defmethod output-files :around ((op operation) (c component)) - "Method to rewrite output files to fasl-root" - (mapcar #'apply-output-translations (call-next-method))) + (etypecase path + (logical-pathname + path) + ((or pathname string) + (ensure-output-translations) + (loop :with p = (truenamize path) + :for (source destination) :in (car *output-translations*) + :for root = (when (or (eq source t) + (and (pathnamep source) + (not (absolute-pathname-p source)))) + (pathname-root p)) + :for absolute-source = (cond + ((eq source t) (wilden root)) + (root (merge-pathnames* source root)) + (t source)) + :when (or (eq source t) (pathname-match-p p absolute-source)) + :return + (cond + ((functionp destination) + (funcall destination p absolute-source)) + ((eq destination t) + p) + ((not (pathnamep destination)) + (error "invalid destination")) + ((not (absolute-pathname-p destination)) + (translate-pathname p absolute-source (merge-pathnames* destination root))) + (root + (translate-pathname (directorize-pathname-host-device p) absolute-source destination)) + (t + (translate-pathname p absolute-source destination))) + :finally (return p))))) + +(defun last-char (s) + (and (stringp s) (plusp (length s)) (char s (1- (length s))))) + +(defun directorize-pathname-host-device (pathname) + (let* ((root (pathname-root pathname)) + (wild-root (wilden root)) + (absolute-pathname (merge-pathnames* pathname root)) + (foo (make-pathname :directory '(:absolute "FOO") :defaults root)) + (separator (last-char (namestring foo))) + (root-namestring (namestring root)) + (root-string + (substitute-if #\/ + (lambda (x) (or (eql x #\:) + (eql x separator))) + root-namestring))) + (multiple-value-bind (relative path filename) + (component-name-to-pathname-components root-string t) + (declare (ignore relative filename)) + (let ((new-base + (make-pathname :defaults root + :directory `(:absolute ,@path)))) + (translate-pathname absolute-pathname wild-root (wilden new-base)))))) + +(defmethod output-files :around (operation component) + "Translate output files, unless asked not to" + (declare (ignorable operation component)) + (values + (multiple-value-bind (files fixedp) (call-next-method) + (if fixedp + files + (mapcar #'apply-output-translations files))) + t)) (defun compile-file-pathname* (input-file &rest keys) (apply-output-translations (apply #'compile-file-pathname - (truenamize (merge-pathnames (make-pathname :type "lisp") input-file)) + (truenamize (lispize-pathname input-file)) keys))) +#+abcl +(defun translate-jar-pathname (source wildcard) + (declare (ignore wildcard)) + (let ((root (apply-output-translations + (concatenate 'string + "/:jar:file/" + (namestring (first (pathname-device + source)))))) + (entry (make-pathname :directory (pathname-directory source) + :name (pathname-name source) + :type (pathname-type source)))) + (concatenate 'string (namestring root) (namestring entry)))) + +;;;; ----------------------------------------------------------------- +;;;; Compatibility mode for ASDF-Binary-Locations + +(defun enable-asdf-binary-locations-compatibility + (&key + (centralize-lisp-binaries nil) + (default-toplevel-directory + ;; Use ".cache/common-lisp" instead ??? + (merge-pathnames* (make-pathname :directory '(:relative ".fasls")) + (user-homedir))) + (include-per-user-information nil) + (map-all-source-files nil) + (source-to-target-mappings nil)) + (let* ((fasl-type (pathname-type (compile-file-pathname "foo.lisp"))) + (wild-inferiors (make-pathname :directory '(:relative :wild-inferiors))) + (mapped-files (make-pathname + :name :wild :version :wild + :type (if map-all-source-files :wild fasl-type))) + (destination-directory + (if centralize-lisp-binaries + `(,default-toplevel-directory + ,@(when include-per-user-information + (cdr (pathname-directory (user-homedir)))) + :implementation ,wild-inferiors) + `(:root ,wild-inferiors :implementation)))) + (initialize-output-translations + `(:output-translations + ,@source-to-target-mappings + ((:root ,wild-inferiors ,mapped-files) + (,@destination-directory ,mapped-files)) + (t t) + :ignore-inherited-configuration)))) + ;;;; ----------------------------------------------------------------- ;;;; Windows shortcut support. Based on: ;;;; @@ -2557,9 +2890,7 @@ with a different configuration, so the configuration would be re-read then." ;;;; ----------------------------------------------------------------- ;;;; Source Registry Configuration, by Francois-Rene Rideau -;;;; See README.source-registry and https://bugs.launchpad.net/asdf/+bug/485918 - -(pushnew 'sysdef-source-registry-search *system-definition-search-functions*) +;;;; See the Manual and https://bugs.launchpad.net/asdf/+bug/485918 ;; Using ack 1.2 exclusions (defvar *default-exclusions* @@ -2574,8 +2905,9 @@ said element itself being a list of directory pathnames where to look for .asd f (defun source-registry () (car *source-registry*)) -(defun (setf source-registry) (x) - (setf *source-registry* (list x))) +(defun (setf source-registry) (new-value) + (setf *source-registry* (list new-value)) + new-value) (defun source-registry-initialized-p () (and *source-registry* t)) @@ -2587,29 +2919,33 @@ with a different configuration, so the configuration would be re-read then." (setf *source-registry* '()) (values)) +(defun probe-asd (name defaults) + (block nil + (when (directory-pathname-p defaults) + (let ((file + (make-pathname + :defaults defaults :version :newest :case :local + :name name + :type "asd"))) + (when (probe-file file) + (return file))) + #+(and (or win32 windows mswindows mingw32) (not cygwin) (not clisp)) + (let ((shortcut + (make-pathname + :defaults defaults :version :newest :case :local + :name (concatenate 'string name ".asd") + :type "lnk"))) + (when (probe-file shortcut) + (let ((target (parse-windows-shortcut shortcut))) + (when target + (return (pathname target))))))))) + (defun sysdef-source-registry-search (system) (ensure-source-registry) - (let ((name (coerce-name system))) - (block nil - (dolist (dir (source-registry)) - (let ((defaults (eval dir))) - (when defaults - (cond ((directory-pathname-p defaults) - (let ((file (and defaults - (make-pathname - :defaults defaults :version :newest - :name name :type "asd" :case :local))) - #+(and (or win32 windows) (not :clisp)) - (shortcut (make-pathname - :defaults defaults :version :newest - :name name :type "asd.lnk" :case :local))) - (when (and file (probe-file file)) - (return file)) - #+(and (or win32 windows) (not :clisp)) - (when (probe-file shortcut) - (let ((target (parse-windows-shortcut shortcut))) - (when target - (return (pathname target)))))))))))))) + (loop :with name = (coerce-name system) + :for defaults :in (source-registry) + :for file = (probe-asd name defaults) + :when file :return file)) (defun validate-source-registry-directive (directive) (unless @@ -2618,7 +2954,7 @@ with a different configuration, so the configuration would be re-read then." (case kw ((:include :directory :tree) (and (length=n-p rest 1) - (typep (car rest) '(or pathname string)))) + (typep (car rest) '(or pathname string null)))) ((:exclude) (every #'stringp rest)) (null rest)))) @@ -2643,7 +2979,7 @@ with a different configuration, so the configuration would be re-read then." '(:source-registry :inherit-configuration)) ((not (stringp string)) (error "environment string isn't: ~S" string)) - ((eql (char string 0) "\"(") + ((find (char string 0) "\"(") (validate-source-registry-form (read-from-string string))) (t (loop @@ -2651,8 +2987,8 @@ with a different configuration, so the configuration would be re-read then." :with directives = () :with start = 0 :with end = (length string) - :for i = (or (position #\: string :start start) end) :do - (let ((s (subseq string start i))) + :for pos = (position *inter-directory-separator* string :start start) :do + (let ((s (subseq string start (or pos end)))) (cond ((equal "" s) ; empty element: inherit (when inherit @@ -2663,21 +2999,23 @@ with a different configuration, so the configuration would be re-read then." (push `(:tree ,(subseq s 0 (1- (length s)))) directives)) (t (push `(:directory ,s) directives))) - (setf start (1+ i)) - (when (>= start end) + (cond + (pos + (setf start (1+ pos))) + (t (unless inherit (push '(:ignore-inherited-configuration) directives)) - (return `(:source-registry ,@(nreverse directives))))))))) + (return `(:source-registry ,@(nreverse directives)))))))))) (defun register-asd-directory (directory &key recurse exclude collect) (if (not recurse) - (funcall collect (ensure-directory-pathname directory)) + (funcall collect directory) (let* ((files (ignore-errors - (directory (merge-pathnames #P"**/*.asd" directory) + (directory (merge-pathnames* *wild-asd* directory) #+sbcl #+sbcl :resolve-symlinks nil #+clisp #+clisp :circle t))) (dirs (remove-duplicates (mapcar #'pathname-directory-pathname files) - :test #'equal))) + :test #'equal :from-end t))) (loop :for dir :in dirs :unless (loop :for x :in exclude @@ -2689,36 +3027,53 @@ with a different configuration, so the configuration would be re-read then." user-source-registry user-source-registry-directory system-source-registry - system-source-registry-directory)) + system-source-registry-directory + default-source-registry)) -(defparameter *source-registry-file* #p"common-lisp/source-registry.conf") -(defparameter *source-registry-directory* #p"common-lisp/source-registry.conf.d/") +(defparameter *source-registry-file* #p"source-registry.conf") +(defparameter *source-registry-directory* #p"source-registry.conf.d/") (defun wrapping-source-registry () `(:source-registry #+sbcl (:tree ,(getenv "SBCL_HOME")) :inherit-configuration)) (defun default-source-registry () - `(:source-registry - #+sbcl (:directory ,(merge-pathnames ".sbcl/systems/" (user-homedir-pathname))) - (:directory ,(truenamize (directory-namestring *default-pathname-defaults*))) - (:directory ,(merge-pathnames ".local/share/common-lisp/systems/" (user-homedir-pathname))) - (:tree ,(merge-pathnames ".local/share/common-lisp/source/" (user-homedir-pathname))) - (:directory "/usr/local/share/common-lisp/systems/") - (:tree "/usr/local/share/common-lisp/source/") - (:directory "/usr/local/share/common-lisp/systems/") - (:tree "/usr/local/share/common-lisp/source/") - (:directory "/usr/share/common-lisp/systems/") - (:tree "/usr/share/common-lisp/source/") - :inherit-configuration)) + (flet ((try (x sub) (try-directory-subpath x sub :type :directory))) + `(:source-registry + #+sbcl (:directory ,(merge-pathnames* ".sbcl/systems/" (user-homedir))) + (:directory ,(truenamize (directory-namestring *default-pathname-defaults*))) + ,@(let* + #+(or unix cygwin) + ((datahome + (or (getenv "XDG_DATA_HOME") + (try (user-homedir) ".local/share/"))) + (datadirs + (or (getenv "XDG_DATA_DIRS") "/usr/local/share:/usr/share")) + (dirs (cons datahome (split-string datadirs :separator ":")))) + #+(and (or win32 windows mswindows mingw32) (not cygwin)) + ((datahome + #+lispworks (sys:get-folder-path :common-appdata) + #-lispworks (try (or (getenv "USERPROFILE") (user-homedir)) + "Application Data")) + (datadir + #+lispworks (sys:get-folder-path :local-appdata) + #-lispworks (try (getenv "ALLUSERSPROFILE") + "Application Data")) + (dirs (list datahome datadir))) + #-(or unix win32 windows mswindows mingw32 cygwin) + ((dirs ())) + (loop :for dir :in dirs + :collect `(:directory ,(try dir "common-lisp/systems/")) + :collect `(:tree ,(try dir "common-lisp/source/")))) + :inherit-configuration))) (defun user-source-registry () - (merge-pathnames *source-registry-file* (user-configuration-directory))) + (in-user-configuration-directory *source-registry-file*)) (defun system-source-registry () - (merge-pathnames *source-registry-file* (system-configuration-directory))) + (in-system-configuration-directory *source-registry-file*)) (defun user-source-registry-directory () - (merge-pathnames *source-registry-directory* (user-configuration-directory))) + (in-user-configuration-directory *source-registry-directory*)) (defun system-source-registry-directory () - (merge-pathnames *source-registry-directory* (system-configuration-directory))) + (in-system-configuration-directory *source-registry-directory*)) (defun environment-source-registry () (getenv "CL_SOURCE_REGISTRY")) @@ -2758,10 +3113,12 @@ with a different configuration, so the configuration would be re-read then." (process-source-registry (pathname pathname) :inherit nil :register register))) ((:directory) (destructuring-bind (pathname) rest - (funcall register pathname))) + (when pathname + (funcall register (ensure-directory-pathname pathname))))) ((:tree) (destructuring-bind (pathname) rest - (funcall register pathname :recurse t :exclude *default-exclusions*))) + (when pathname + (funcall register (ensure-directory-pathname pathname) :recurse t :exclude *default-exclusions*)))) ((:exclude) (setf *default-exclusions* rest)) ((:default-registry) @@ -2771,21 +3128,26 @@ with a different configuration, so the configuration would be re-read then." ((:ignore-inherited-configuration) nil)))) +(defun flatten-source-registry (&optional parameter) + (remove-duplicates + (while-collecting (collect) + (inherit-source-registry + `(wrapping-source-registry + ,parameter + ,@*default-source-registries*) + :register (lambda (directory &key recurse exclude) + (collect (list directory :recurse recurse :exclude exclude))))) + :test 'equal :from-end t)) + ;; Will read the configuration and initialize all internal variables, ;; and return the new configuration. (defun compute-source-registry (&optional parameter) - (multiple-value-bind (collect result) (make-collector) - (inherit-source-registry - `(wrapping-source-registry - ,parameter - ,@*default-source-registries* - default-source-registry) - :register - (lambda (directory &key recurse exclude) - (register-asd-directory - directory - :recurse recurse :exclude exclude :collect collect))) - (funcall result))) + (while-collecting (collect) + (dolist (entry (flatten-source-registry parameter)) + (destructuring-bind (directory &key recurse exclude) entry + (register-asd-directory + directory + :recurse recurse :exclude exclude :collect #'collect))))) (defun initialize-source-registry (&optional parameter) (setf (source-registry) (compute-source-registry parameter))) @@ -2800,18 +3162,26 @@ with a different configuration, so the configuration would be re-read then." (initialize-source-registry))) ;;;; ----------------------------------------------------------------- -;;;; SBCL hook into REQUIRE +;;;; SBCL and ClozureCL hook into REQUIRE ;;;; -#+sbcl +#+(or sbcl clozure abcl) (progn (defun module-provide-asdf (name) - (handler-bind ((style-warning #'muffle-warning)) + (handler-bind + ((style-warning #'muffle-warning) + (missing-component (constantly nil)) + (error (lambda (e) + (format *error-output* "ASDF could not load ~A because ~A.~%" + name e)))) (let* ((*verbose-out* (make-broadcast-stream)) (system (asdf:find-system name nil))) (when system (asdf:operate 'asdf:load-op name) t)))) - (pushnew 'module-provide-asdf sb-ext:*module-provider-functions*)) + (pushnew 'module-provide-asdf + #+sbcl sb-ext:*module-provider-functions* + #+clozure ccl::*module-provider-functions* + #+abcl sys::*module-provider-functions*)) ;;;; ------------------------------------------------------------------------- ;;;; Cleanups after hot-upgrade. @@ -2841,7 +3211,8 @@ with a different configuration, so the configuration would be re-read then." (setf excl:*warn-on-nested-reader-conditionals* *acl-warn-save*))) (pushnew :asdf *features*) -;;(pushnew :asdf2 *features*) ;; do that when we reach version 2 +;; this is a release candidate for ASDF 2.0 +(pushnew :asdf2 *features*) (provide :asdf) diff --git a/asdf.texinfo b/asdf.texinfo deleted file mode 100644 index ccee58764bdb44576a84a378e5af33ab67585b75..0000000000000000000000000000000000000000 --- a/asdf.texinfo +++ /dev/null @@ -1,1675 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename asdf.info -@settitle asdf Manual -@c %**end of header - -@c for install-info -@dircategory Software development -@direntry -* asdf: (asdf). another system definition facility -@end direntry - -@copying -This manual describes asdf, a system definition facility for Common -Lisp programs and libraries. - -asdf Copyright @copyright{} 2001-2010 Daniel Barlow and contributors - -This manual Copyright @copyright{} 2001-2007 Daniel Barlow and -contributors -This manual revised @copyright{} 2009 Robert P. Goldman - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -``Software''), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -@end copying - - - -@titlepage -@title asdf: another system definition facility - -@c The following two commands start the copyright page. -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@c Output the table of contents at the beginning. -@contents - -@c ------------------- - -@ifnottex - -@node Top, Introduction, (dir), (dir) -@top asdf: another system definition facility - -@insertcopying - -@menu - -* Introduction:: -* Using asdf to load systems:: -* Defining systems with defsystem:: -* The object model of asdf:: -* Controlling where ASDF saves compiled files:: -* Error handling:: -* Compilation error and warning handling:: -* Miscellaneous additional functionality:: -* Getting the latest version:: -* FAQ:: -* TODO list:: -* missing bits in implementation:: -* Inspiration:: -* Concept Index:: -* Function and Class Index:: -* Variable Index:: - -@detailmenu - --- The Detailed Node Listing --- - -Using ASDF - -@c * Installing ASDF:: - -Defining systems with defsystem - -* The defsystem form:: -* A more involved example:: -* The defsystem grammar:: -* Other code in .asd files:: - - -The object model of asdf - -* Operations:: -* Components:: - -Operations - -* Predefined operations of asdf:: -* Creating new operations:: - -Components - -* Common attributes of components:: -* Pre-defined subclasses of component:: -* Creating new component types:: - -properties - -* Pre-defined subclasses of component:: -* Creating new component types:: - -@end detailmenu -@end menu - -@end ifnottex - -@c ------------------- - -@node Introduction, Using asdf to load systems, Top, Top -@comment node-name, next, previous, up -@chapter Introduction - -ASDF is Another System Definition Facility: a tool for describing the -sub-systems and files that comprise a system and for operating on these -components in the right order so that they can be compiled, loaded, -tested, etc. - -ASDF presents two faces: one for system implementors who need to be able -to describe their systems and one for Lisp programmers who want to use -those systems. @xref{Using asdf to load systems,the getting started guide}, to learn how to use ASDF to -load a system. @xref{Defining systems with defsystem}, to learn how to -define a system of your own. Later chapters describe the ASDF internals -and how to extend ASDF. - - -@node Using asdf to load systems, Defining systems with defsystem, Introduction, Top -@comment node-name, next, previous, up -@chapter Using ASDF -@vindex *central-registry* -@cindex link farm -@findex load-system -@findex compile-system -@findex test-system -@cindex system directory designator -@findex operate -@findex oos - -@c @menu -@c * Installing ASDF:: -@c @end menu - -@section Installing ASDF - -Many Lisp implementations include a copy of ASDF. You can -usually load this copy using Common-Lisp's @code{require} -function: - -@kbd{(require 'asdf)} - -Consult your Lisp implementation's documentation for details. If ASDF -doesn't come bundled with your Lisp or if you want to make -sure that you have the most recent version, then you'll want -to download it from the -@url{http://common-lisp.net/project/asdf/,asdf-home} website. - -Note to SBCL users: you are cautioned that installing ASDF yourself may -break some SBCL contribs. We are working to provide instructions for -doing this safely. - -If you want to get the latest and greatest version of ASDF from its VCS -repository, @xref{Getting the latest version}. - -@section Loading ASDF - -If your CL implementation does @emph{not} provide ASDF through the -@code{require} feature, then you need to download ASDF and load it -yourself. - -The single file @file{asdf.lisp} is all you need to use asdf normally. -Once you load it in a running Lisp, you're ready to use asdf. For -maximum convenience you might want to have asdf loaded whenever you -start your Lisp implementation, for example by loading it from the -startup script or dumping a custom core -- check your Lisp -implementation's manual for details. - -@section Setting up a system to be loaded - -To compile and load a system, you need to ensure that its -system definition can be found in one of the directories in -@code{*central-registry*}@footnote{It is possible to customize the -system definition file search. That's considered advanced use, and -covered later: search forward for -@code{*system-definition-search-functions*}. @xref{Defining systems -with defsystem}.}. - -There are two ways that you can make a system definition findable -through @code{*central-registry*}. One is to simply insert into -@code{*central-registry*} an entry that specifies the pathname of for -the directory containing the system definition file (a file whose name -is of the form @code{*.asd}). -For example, if we had a -system @var{foo} that is stored in a directory -@file{/home/me/src/foo/}, containing @file{/home/me/src/foo/}, we could -make the foo system asdf-operable by doing @kbd{(push -"/home/me/src/foo/" asdf:*central-registry*)}. - -ASDF will also properly handle the case where the -@code{*central-registry*} points to a directory containing a -@emph{symbolic link} to the system definition file. - -For example, if @code{#p"/home/me/cl/systems/"} (note the trailing -slash) is a member of @code{*central-registry*}, you could set up the -system @var{foo} for loading with asdf with the following -commands at the shell: - -@example -$ cd /home/me/cl/systems/ -$ ln -s ~/src/foo/foo.asd . -@end example - -Previous versions of the ASDF manual suggested that users create a -directory in their @code{*central-registry*} that would act as a ``link -farm.'' However, this method may cause problems if you work on multiple -lisp projects that contain different mixes of ASDF libraries. - -The variable @code{asdf:*central-registry*} is a list of ``system -directory designators''@footnote{When we say ``directory'' here, we -mean ``designator for a pathname with a supplied DIRECTORY -component''.}. A @dfn{system directory designator} is a form which -will be evaluated whenever a system is to be found, and must evaluate -to a directory to look in. You might want to set or augment -@code{*central-registry*} in your Lisp init file, for example: - -@lisp -(setf asdf:*central-registry* - (list* '*default-pathname-defaults* - #p"/home/me/cl/systems/" - #p"/usr/share/common-lisp/systems/" - asdf:*central-registry*)) -@end lisp - -@section Loading a system - -@c{FIXME: Add Gary's material about loading system definitions by hand.} -@c{FIXME: Introduce ASDF-binary-locations.} - -The system @var{foo} is loaded (and compiled, if necessary) by -evaluating the following form in your Lisp implementation: - -@example -(asdf:load-system '@var{foo}) -@end example - -(In older versions of ASDF, you may need to use @code{(asdf:oos -'asdf:load-op 'foo)}, instead.) - -Output from asdf and asdf extensions are supposed to be sent to the CL -stream @code{*standard-output*}, and so rebinding that stream around -calls to @code{asdf:operate} should redirect all output from asdf -operations. - -ASDF provides three commands for the most common system -operations: @code{load-system}, @code{compile-system} or @code{test-system}. - -Because ASDF is an extensible system for defining -@emph{operations} on @emph{components} also provides a generic -function: @code{operate} (which is usually abbreviated by -@code{oos}). You'll use @code{oos} whenever you want to do something -beyond compiling, loading and testing. - -Reminder: before ASDF can operate on a system, however, it must be able -to find and load that system's definition. - -@section Placement of compiled files - -By default, ASDF will place the compiled files it creates in the same -directory as the source files. This works for most simple use cases. -However, if you use multiple Common Lisp implementations, or for some -other reason need to place your compiled files elsewhere, you will want -to enable ASDF-BINARY-LOCATIONS. @xref{Controlling where ASDF saves -compiled files}. - -@section Summary - -To use ASDF: - -@itemize -@item -load @file{asdf.lisp} into your Lisp image, either through -@code{require} or @code{load}. - -@item -make sure ASDF can find system definitions by loading them -yourself or setting up @code{*central-registry*}. - -@item -use @code{operate} (or shorthand @code{oos}) -to tell ASDF what you'd like to do to what systems. -For simple operations, you can use @code{load-system}, @code{compile-system} or @code{test-system} -instead. - -@end itemize - -@section Moving on - -That's all you need to know to use asdf to load systems written by -others. The rest of this manual deals with writing system -definitions for Lisp software you write yourself, including how to -extend ASDF to define new operation and component types. - -@node Defining systems with defsystem, The object model of asdf, Using asdf to load systems, Top -@comment node-name, next, previous, up -@chapter Defining systems with defsystem - -This chapter describes how to use asdf to define systems and develop -software. - - -@menu -* The defsystem form:: -* A more involved example:: -* The defsystem grammar:: -* Other code in .asd files:: -@end menu - -@node The defsystem form, A more involved example, Defining systems with defsystem, Defining systems with defsystem -@comment node-name, next, previous, up -@section The defsystem form - -Systems can be constructed programmatically by instantiating -components using make-instance. Most of the time, however, it is much -more practical to use a static @code{defsystem} form. This section -begins with an example of a system definition, then gives the full -grammar of @code{defsystem}. - -Let's look at a simple system. This is a complete file that would -usually be saved as @file{hello-lisp.asd}: - -@lisp -(defpackage hello-lisp-system - (:use :common-lisp :asdf)) - -(in-package :hello-lisp-system) - -(defsystem "hello-lisp" - :description "hello-lisp: a sample Lisp system." - :version "0.2" - :author "Joe User <joe@@example.com>" - :licence "Public Domain" - :components ((:file "packages") - (:file "macros" :depends-on ("packages")) - (:file "hello" :depends-on ("macros")))) -@end lisp - -Some notes about this example: - -@itemize - -@item -The file starts with @code{defpackage} and @code{in-package} forms to -make and use a package expressly for defining this system in. This -package is named by taking the system name and suffixing -@code{-system} - note that it is @emph{not} the same package as you -will use for the application code. - -This is not absolutely required by asdf, but helps avoid namespace -pollution and so is considered good form. - -@item -The defsystem form defines a system named "hello-lisp" that contains -three source files: @file{packages}, @file{macros} and @file{hello}. - -@item -The file @file{macros} depends on @file{packages} (presumably because -the package it's in is defined in @file{packages}), and the file -@file{hello} depends on @file{macros} (and hence, transitively on -@file{packages}). This means that asdf will compile and load -@file{packages} and @file{macros} before starting the compilation of -file @file{hello}. - - -@item -The files are located in the same directory as the file with the -system definition. asdf resolves symbolic links before loading the system -definition file and stores its location in the resulting -system@footnote{It is possible, though almost never necessary, to -override this behaviour.}. This is a good thing because the user can -move the system sources without having to edit the system definition. - -@end itemize - -@node A more involved example, The defsystem grammar, The defsystem form, Defining systems with defsystem -@comment node-name, next, previous, up -@section A more involved example - -Let's illustrate some more involved uses of @code{defsystem} via a -slightly convoluted example: - -@lisp -(defsystem "foo" - :version "1.0" - :components ((:module "foo" :components ((:file "bar") (:file"baz") - (:file "quux")) - :perform (compile-op :after (op c) - (do-something c)) - :explain (compile-op :after (op c) - (explain-something c))) - (:file "blah"))) -@end lisp - -The method-form tokens need explaining: essentially, this part: - -@lisp - :perform (compile-op :after (op c) - (do-something c)) - :explain (compile-op :after (op c) - (explain-something c)) -@end lisp - -has the effect of - -@lisp -(defmethod perform :after ((op compile-op) (c (eql ...))) - (do-something c)) -(defmethod explain :after ((op compile-op) (c (eql ...))) - (explain-something c)) -@end lisp - -where @code{...} is the component in question; note that although this -also supports @code{:before} methods, they may not do what you want -them to -- a @code{:before} method on perform @code{((op compile-op) (c -(eql ...)))} will run after all the dependencies and sub-components -have been processed, but before the component in question has been -compiled. - -@node The defsystem grammar, Other code in .asd files, A more involved example, Defining systems with defsystem -@comment node-name, next, previous, up -@section The defsystem grammar - -@verbatim -system-definition := ( defsystem system-designator {option}* ) - -option := :components component-list - | :pathname pathname - | :default-component-class - | :perform method-form - | :explain method-form - | :output-files method-form - | :operation-done-p method-form - | :depends-on ( {dependency-def}* ) - | :serial [ t | nil ] - | :in-order-to ( {dependency}+ ) - -component-list := ( {component-def}* ) - -component-def := ( component-type name {option}* ) - -component-type := :module | :file | :system | other-component-type - -dependency-def := simple-component-name - | ( :feature name ) - | ( :version simple-component-name version-specifier) - -dependency := (dependent-op {requirement}+) -requirement := (required-op {required-component}+) - | (feature feature-name) -dependent-op := operation-name -required-op := operation-name | feature - -simple-component-name := string - | symbol - -method-form := (operation-name qual lambda-list &rest body) -qual := method qualifier -@end verbatim - -@subsection Serial dependencies - -If the @code{:serial t} option is specified for a module, asdf will add -dependencies for each each child component, on all the children -textually preceding it. This is done as if by @code{:depends-on}. - -@lisp -:components ((:file "a") (:file "b") (:file "c")) -:serial t -@end lisp - -is equivalent to - -@lisp -:components ((:file "a") - (:file "b" :depends-on ("a")) - (:file "c" :depends-on ("a" "b"))) -@end lisp - - -@subsection Source location - -The @code{:pathname} option is optional in all cases for systems -defined via @code{defsystem}, and in the usual case the user is -recommended not to supply it. - -Instead, asdf follows a hairy set of rules that are designed so that -@enumerate -@item @code{find-system} will load a system from disk and have its pathname -default to the right place -@item this pathname information will not be -overwritten with @code{*default-pathname-defaults*} (which could be -somewhere else altogether) if the user loads up the @file{.asd} file -into his editor and interactively re-evaluates that form. -@end enumerate - -If a system is being loaded for the first time, its top-level pathname -will be set to: - -@itemize -@item The host/device/directory parts of @code{*load-truename*}, if it is bound -@item @code{*default-pathname-defaults*}, otherwise -@end itemize - -If a system is being redefined, the top-level pathname will be - -@itemize -@item -changed, if explicitly supplied or obtained from -@code{*load-truename*} (so that an updated source location is -reflected in the system definition) -@item -changed if it had previously been set from -@code{*default-pathname-defaults*} -@item -left as before, if it had previously been set from -@code{*load-truename*} and @code{*load-truename*} is currently -unbound (so that a developer can evaluate a @code{defsystem} form from -within an editor without clobbering its source location) -@end itemize - -@node Other code in .asd files, , The defsystem grammar, Defining systems with defsystem -@section Other code in .asd files - -Files containing defsystem forms are regular Lisp files that are -executed by @code{load}. Consequently, you can put whatever Lisp code -you like into these files (e.g., code that examines the compile-time -environment and adds appropriate features to @code{*features*}). -However, some conventions should be followed, so that users can -control certain details of execution of the Lisp in .asd files: - -@itemize -@item -Any informative output (other than warnings and errors, which are the -condition system's to dispose of) should be sent to the standard CL -stream @code{*standard-output*}, so that users can easily control the -disposition of output from asdf operations. -@end itemize - - -@node The object model of asdf, Controlling where ASDF saves compiled files, Defining systems with defsystem, Top -@comment node-name, next, previous, up -@chapter The object model of asdf - -asdf is designed in an object-oriented way from the ground up. Both a -system's structure and the operations that can be performed on systems -follow a protocol. asdf is extensible to new operations and to new -component types. This allows the addition of behaviours: for example, -a new component could be added for Java JAR archives, and methods -specialised on @code{compile-op} added for it that would accomplish the -relevant actions. - -This chapter deals with @emph{components}, the building blocks of a -system, and @emph{operations}, the actions that can be performed on a -system. - - - -@menu -* Operations:: -* Components:: -@end menu - -@node Operations, Components, The object model of asdf, The object model of asdf -@comment node-name, next, previous, up -@section Operations -@cindex operation - -An @dfn{operation} object of the appropriate type is instantiated -whenever the user wants to do something with a system like - -@itemize -@item compile all its files -@item load the files into a running lisp environment -@item copy its source files somewhere else -@end itemize - -Operations can be invoked directly, or examined to see what their -effects would be without performing them. @emph{FIXME: document how!} There -are a bunch of methods specialised on operation and component type -that actually do the grunt work. - -The operation object contains whatever state is relevant for this -purpose (perhaps a list of visited nodes, for example) but primarily -is a nice thing to specialise operation methods on and easier than -having them all be EQL methods. - -Operations are invoked on systems via @code{operate}. -@anchor{operate} -@deffn {Generic function} operate operation system &rest initargs -@deffnx {Generic function} oos operation system &rest initargs -@code{operate} invokes @var{operation} on @var{system}. @code{oos} -is a synonym for @code{operate}. - -@var{operation} is a symbol that is passed, along with the supplied -@var{initargs}, to @code{make-instance} to create the operation object. -@var{system} is a system designator. - -The initargs are passed to the @code{make-instance} call when creating -the operation object. Note that dependencies may cause the operation -to invoke other operations on the system or its components: the new -operations will be created with the same initargs as the original one. - -@end deffn - -@menu -* Predefined operations of asdf:: -* Creating new operations:: -@end menu - -@node Predefined operations of asdf, Creating new operations, Operations, Operations -@comment node-name, next, previous, up -@subsection Predefined operations of asdf - -All the operations described in this section are in the @code{asdf} -package. They are invoked via the @code{operate} generic function. - -@lisp -(asdf:operate 'asdf:@var{operation-name} '@var{system-name} @{@var{operation-options ...}@}) -@end lisp - -@deffn Operation compile-op &key proclamations - -This operation compiles the specified component. If proclamations are -supplied, they will be proclaimed. This is a good place to specify -optimization settings. - -When creating a new component type, you should provide methods for -@code{compile-op}. - -When @code{compile-op} is invoked, component dependencies often cause -some parts of the system to be loaded as well as compiled. Invoking -@code{compile-op} does not necessarily load all the parts of the -system, though; use @code{load-op} to load a system. -@end deffn - -@deffn Operation load-op &key proclamations - -This operation loads a system. - -The default methods for @code{load-op} compile files before loading them. -For parity, your own methods on new component types should probably do -so too. -@end deffn - -@deffn Operation load-source-op - -This operation will load the source for the files in a module even if -the source files have been compiled. Systems sometimes have knotty -dependencies which require that sources are loaded before they can be -compiled. This is how you do that. - -If you are creating a component type, you need to implement this -operation - at least, where meaningful. -@end deffn - -@anchor{test-op} -@deffn Operation test-op - -This operation will perform some tests on the module. The default -method will do nothing. The default dependency is to require -@code{load-op} to be performed on the module first. The default -@code{operation-done-p} is that the operation is @emph{never} done --- -we assume that if you invoke the @code{test-op}, you want to test the -system, even if you have already done so. - -The results of this operation are not defined by ASDF. It has proven -difficult to define how the test operation should signal its results to -the user in a way that is compatible with all of the various test -libraries and test techniques in use in the community. -@end deffn - -@c @deffn Operation test-system-version &key minimum - -@c Asks the system whether it satisfies a version requirement. - -@c The default method accepts a string, which is expected to contain of a -@c number of integers separated by #\. characters. The method is not -@c recursive. The component satisfies the version dependency if it has -@c the same major number as required and each of its sub-versions is -@c greater than or equal to the sub-version number required. - -@c @lisp -@c (defun version-satisfies (x y) -@c (labels ((bigger (x y) -@c (cond ((not y) t) -@c ((not x) nil) -@c ((> (car x) (car y)) t) -@c ((= (car x) (car y)) -@c (bigger (cdr x) (cdr y)))))) -@c (and (= (car x) (car y)) -@c (or (not (cdr y)) (bigger (cdr x) (cdr y)))))) -@c @end lisp - -@c If that doesn't work for your system, you can override it. I hope -@c you have as much fun writing the new method as @verb{|#lisp|} did -@c reimplementing this one. -@c @end deffn - -@c @deffn Operation feature-dependent-op - -@c An instance of @code{feature-dependent-op} will ignore any components -@c which have a @code{features} attribute, unless the feature combination -@c it designates is satisfied by @code{*features*}. This operation is -@c not intended to be instantiated directly, but other operations may -@c inherit from it. - -@c @end deffn - -@node Creating new operations, , Predefined operations of asdf, Operations -@comment node-name, next, previous, up -@subsection Creating new operations - -asdf was designed to be extensible in an object-oriented fashion. To -teach asdf new tricks, a programmer can implement the behaviour he -wants by creating a subclass of @code{operation}. - - -asdf's pre-defined operations are in no way ``privileged'', but it is -requested that developers never use the @code{asdf} package for -operations they develop themselves. The rationale for this rule is -that we don't want to establish a ``global asdf operation name -registry'', but also want to avoid name clashes. - -An operation must provide methods for the following generic functions -when invoked with an object of type @code{source-file}: @emph{FIXME describe -this better} - -@itemize - -@item @code{output-files} -@item @code{perform} -The @code{perform} method must call @code{output-files} to find out -where to put its files, because the user is allowed to override -@item @code{output-files} for local policy @code{explain} -@item @code{operation-done-p}, if you don't like the default one - -@end itemize - -Operations that print output should send that output to the standard -CL stream @code{*standard-output*}, as the Lisp compiler and loader do. - -@node Components, , Operations, The object model of asdf -@comment node-name, next, previous, up -@section Components -@cindex component -@cindex system -@cindex system designator -@vindex *system-definition-search-functions* - -A @dfn{component} represents a source file or (recursively) a -collection of components. A @dfn{system} is (roughly speaking) a -top-level component that can be found via @code{find-system}. - -A @dfn{system designator} is a string or symbol and behaves just like -any other component name (including with regard to the case conversion -rules for component names). - - -@defun find-system system-designator &optional (error-p t) - -Given a system designator, @code{find-system} finds and returns a -system. If no system is found, an error of type -@code{missing-component} is thrown, or @code{nil} is returned if -@code{error-p} is false. - -To find and update systems, @code{find-system} funcalls each element -in the @code{*system-definition-search-functions*} list, expecting a -pathname to be returned. The resulting pathname is loaded if either -of the following conditions is true: - -@itemize -@item there is no system of that name in memory -@item the file's last-modified time exceeds the last-modified time of the - system in memory -@end itemize - -When system definitions are loaded from @file{.asd} files, a new -scratch package is created for them to load into, so that different -systems do not overwrite each others operations. The user may also -wish to (and is recommended to) include @code{defpackage} and -@code{in-package} forms in his system definition files, however, so -that they can be loaded manually if need be. - -The default value of @code{*system-definition-search-functions*} is a -function that looks in each of the directories given by evaluating -members of @code{*central-registry*} for a file whose name is the -name of the system and whose type is @file{asd}. The first such file -is returned, whether or not it turns out to actually define the -appropriate system. Hence, it is strongly advised to define a system -@var{foo} in the corresponding file @var{foo.asd}. -@end defun - - -@menu -* Common attributes of components:: -* Pre-defined subclasses of component:: -* Creating new component types:: -@end menu - -@node Common attributes of components, Pre-defined subclasses of component, Components, Components -@comment node-name, next, previous, up -@subsection Common attributes of components - -All components, regardless of type, have the following attributes. -All attributes except @code{name} are optional. - -@subsubsection Name - -A component name is a string or a symbol. If a symbol, its name is -taken and lowercased. The name must be a suitable value for the -@code{:name} initarg to @code{make-pathname} in whatever filesystem -the system is to be found. - -The lower-casing-symbols behaviour is unconventional, but was selected -after some consideration. Observations suggest that the type of -systems we want to 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 @code{:case -:common} as argument to @code{make-pathname}, which is reported not to -work on some implementations. - -@subsubsection Version identifier - -This optional attribute is used by the test-system-version -operation. @xref{Predefined operations of asdf}. For the default method of -test-system-version, the version should be a string of intergers -separated by dots, for example @samp{1.0.11}. - -@subsubsection Required features - -@emph{FIXME: This subsection seems to contradict the defsystem grammar -subsection; which doesn't provide any obvious way to specify required -features. Furthermore, recent discussion on the asdf-devel mailing list -suggests that the specification of required features may be broken.} - -Traditionally defsystem users have used reader conditionals to include -or exclude specific per-implementation files. This means that any -single implementation cannot read the entire system, which becomes a -problem if it doesn't wish to compile it, but instead for example to -create an archive file containing all the sources, as it will omit to -process the system-dependent sources for other systems. - -Each component in an asdf system may therefore specify features using -the same syntax as #+ does, and it will (somehow) be ignored for -certain operations unless the feature conditional is a member of -@code{*features*}. - - -@subsubsection Dependencies - -This attribute specifies dependencies of the component on its -siblings. It is optional but often necessary. - -There is an excitingly complicated relationship between the initarg -and the method that you use to ask about dependencies - -Dependencies are between (operation component) pairs. In your -initargs for the component, you can say - -@lisp -:in-order-to ((compile-op (load-op "a" "b") (compile-op "c")) - (load-op (load-op "foo"))) -@end lisp - -This means the following things: -@itemize -@item -before performing compile-op on this component, we must perform -load-op on @var{a} and @var{b}, and compile-op on @var{c}, -@item -before performing @code{load-op}, we have to load @var{foo} -@end itemize - -The syntax is approximately - -@verbatim -(this-op {(other-op required-components)}+) - -required-components := component-name - | (required-components required-components) - -component-name := string - | (:version string minimum-version-object) -@end verbatim - -Side note: - -This is on a par with what ACL defsystem does. mk-defsystem is less -general: it has an implied dependency - -@verbatim - for all x, (load x) depends on (compile x) -@end verbatim - -and using a @code{:depends-on} argument to say that @var{b} depends on -@var{a} @emph{actually} means that - -@verbatim - (compile b) depends on (load a) -@end verbatim - -This is insufficient for e.g. the McCLIM system, which requires that -all the files are loaded before any of them can be compiled ] - -End side note - -In asdf, the dependency information for a given component and -operation can be queried using @code{(component-depends-on operation -component)}, which returns a list - -@lisp -((load-op "a") (load-op "b") (compile-op "c") ...) -@end lisp - -@code{component-depends-on} can be subclassed for more specific -component/operation types: these need to @code{(call-next-method)} and -append the answer to their dependency, unless they have a good reason -for completely overriding the default dependencies. - -If it weren't for CLISP, we'd be using @code{LIST} method -combination to do this transparently. But, we need to support CLISP. -If you have the time for some CLISP hacking, I'm sure they'd welcome -your fixes. - -@subsubsection pathname - -This attribute is optional and if absent will be inferred from the -component's name, type (the subclass of source-file), and the location -of its parent. - -The rules for this inference are: - -(for source-files) -@itemize -@item the host is taken from the parent -@item pathname type is @code{(source-file-type component system)} -@item the pathname case option is @code{:local} -@item the pathname is merged against the parent -@end itemize - -(for modules) -@itemize -@item the host is taken from the parent -@item the name and type are @code{NIL} -@item the directory is @code{(:relative component-name)} -@item the pathname case option is @code{:local} -@item the pathname is merged against the parent -@end itemize - -Note that the DEFSYSTEM operator (used to create a ``top-level'' -system) does additional processing to set the filesystem location of -the top component in that system. This is detailed -elsewhere, @xref{Defining systems with defsystem}. - -The answer to the frequently asked question "how do I create a system -definition where all the source files have a .cl extension" is thus - -@lisp -(defmethod source-file-type ((c cl-source-file) (s (eql (find-system 'my-sys)))) - "cl") -@end lisp - -@subsubsection properties - -This attribute is optional. - -Packaging systems often require information about files or systems in -addition to that specified by asdf's pre-defined component attributes. -Programs that create vendor packages out of asdf systems therefore -have to create ``placeholder'' information to satisfy these systems. -Sometimes the creator of an asdf system may know the additional -information and wish to provide it directly. - -(component-property component property-name) and associated setf -method will allow the programmatic update of this information. -Property names are compared as if by @code{EQL}, so use symbols or -keywords or something. - -@menu -* Pre-defined subclasses of component:: -* Creating new component types:: -@end menu - -@node Pre-defined subclasses of component, Creating new component types, Common attributes of components, Components -@comment node-name, next, previous, up -@subsection Pre-defined subclasses of component - -@deffn Component source-file - -A source file is any file that the system does not know how to -generate from other components of the system. - -Note that this is not necessarily the same thing as ``a file -containing data that is typically fed to a compiler''. If a file is -generated by some pre-processor stage (e.g. a @file{.h} file from -@file{.h.in} by autoconf) then it is not, by this definition, a source -file. Conversely, we might have a graphic file that cannot be -automatically regenerated, or a proprietary shared library that we -received as a binary: these do count as source files for our purposes. - -Subclasses of source-file exist for various languages. @emph{FIXME: -describe these.} -@end deffn - -@deffn Component module - -A module is a collection of sub-components. - -A module component has the following extra initargs: - -@itemize -@item -@code{:components} the components contained in this module - -@item -@code{:default-component-class} All child components which don't -specify their class explicitly are inferred to be of this type. - -@item -@code{:if-component-dep-fails} This attribute takes one of the values -@code{:fail}, @code{:try-next}, @code{:ignore}, its default value is -@code{:fail}. The other values can be used for implementing -conditional compilation based on implementation @code{*features*}, for -the case where it is not necessary for all files in a module to be -compiled. - -@item -@code{:serial} When this attribute is set, each subcomponent of this -component is assumed to depend on all subcomponents before it in the -list given to @code{:components}, i.e. all of them are loaded before -a compile or load operation is performed on it. - -@end itemize - -The default operation knows how to traverse a module, so most -operations will not need to provide methods specialised on modules. - -@code{module} may be subclassed to represent components such as -foreign-language linked libraries or archive files. -@end deffn - -@deffn Component system - -@code{system} is a subclass of @code{module}. - -A system is a module with a few extra attributes for documentation -purposes; these are given elsewhere. @xref{The defsystem grammar}. - -Users can create new classes for their systems: the default -@code{defsystem} macro takes a @code{:classs} keyword -argument. -@end deffn - -@node Creating new component types, , Pre-defined subclasses of component, Components -@comment node-name, next, previous, up -@subsection Creating new component types - -New component types are defined by subclassing one of the existing -component classes and specializing methods on the new component class. - -@emph{FIXME: this should perhaps be explained more throughly, not only by -example ...} - -As an example, suppose we have some implementation-dependent -functionality that we want to isolate in one subdirectory per Lisp -implementation our system supports. We create a subclass of -@code{cl-source-file}: - -@lisp -(defclass unportable-cl-source-file (cl-source-file) - ()) -@end lisp - -A hypothetical function @code{system-dependent-dirname} gives us the -name of the subdirectory. All that's left is to define how to -calculate the pathname of an @code{unportable-cl-source-file}. - -@lisp -(defmethod component-pathname ((component unportable-cl-source-file)) - (let ((pathname (call-next-method)) - (name (string-downcase (system-dependent-dirname)))) - (merge-pathnames - (make-pathname :directory (list :relative name)) - pathname))) -@end lisp - -The new component type is used in a @code{defsystem} form in this way: - -@lisp -(defsystem :foo - :components - ((:file "packages") - ... - (:unportable-cl-source-file "threads" - :depends-on ("packages" ...)) - ... - ) -@end lisp - -@node Controlling where ASDF saves compiled files, Error handling, The object model of asdf, Top -@comment node-name, next, previous, up -@chapter Controlling where ASDF saves compiled files -@cindex ASDF-binary-locations - -Each Common Lisp implementation has its own format for -compiled files (fasls for short). If you use multiple -implementations (or multiple versions of the same -implementation), you'll soon find your source directories -littered with various `DFSL`s, `FASL`s, `CFSL`s and so on. -Worse yet, some implementations use the same file extension -or change formats from version to version which means that -you'll have to recompile binaries as you switch from one -implementation to the next. - -As of version 1.365, ASDF includes @emph{ASDF-binary-locations} to -mitigate the problem. - -@section Default locations -@findex output-files-for-system-and-operation - -The default binary location for each Lisp implementation is a -subdirectory of each source directory. To account for different Lisps, -Operating Systems, Implementation versions, and so on, ASDF borrows code -from SLIME to create reasonable custom directory names. Here are -some examples: - -@itemize -@item SBCL, version 1.0 on Mac OS X for intel: @code{sbcl-1.0-darwin-x86} - -@item Franz Allegro, version 8.0, ANSI Common Lisp: @code{allegro-8.0a-macosx-x86} - -@item Franz Allegro, version 8.1, Modern (case sensitive) Common Lisp: @code{allegro-8.1m-macosx-x86} -@end itemize - -If you want to keep compiled files out of the source tree entirely, use -@code{*centralize-lisp-binaries*} to put compiled files into -sub-directories of a single central location (see below). - -Here is a summary of the variables that control ASDF's source-to-binary -mappings. All of them are in the ASDF package, so must be set -@emph{after} loading ASDF. - -@defvar *enable-asdf-binary-locations* - If false, then ASDF will place binaries in the same directory as the - source. If true, then ASDF will move the binaries using the rest of - the configuration. Defaults to @code{nil}. -@end defvar - - @defvar *centralize-lisp-binaries* - If true, compiled lisp files - without an explicit mapping (see @code{*source-to-target-mappings*}) - will be placed in subdirectories of - @code{*default-toplevel-directory*}. If false, then compiled lisp - files without an explicit mapping will be placed in subdirectories - of their sources. Defaults to @code{nil}. -@end defvar - - @defvar *default-toplevel-directory* - If - @code{*centralize-lisp-binaries*} is true, then compiled lisp files - without an explicit mapping (see @code{*source-to-target-mappings*}) - will be placed in subdirectories of - @code{*default-toplevel-directory*}. Defaults to a sub-directory - named `.fasls` in the current user's home directory. -@end defvar - - @defvar *include-per-user-information* - specifies whether or not - to include user information in the directory. Only used when - @code{*centralize-lisp-binaries*} is true. Defaults to @code{nil}. -@end defvar - - @defvar *map-all-source-files* - If true, then all source files - will be mapped by ASDF. If @code{nil}, then only Common Lisp Source - Files (i.e., instances of cl-source-file or its subclasses) will - be. Defaults to @code{nil}. -@end defvar - - @defvar *source-to-target-mappings* - This specifies mappings - from source to target. If the target is nil, then it - means to *not* map the source to anything. I.e., to leave - it as is. This has the effect of turning off - ASDF-Binary-Locations for the given source directory. The - default depends on the Lisp implementation. -@end defvar - -These variables are used by -@code{output-files-for-system-and-operation} to determine where to -place a source file's binary. You can further customize asdf-binary-locations -by writing additional methods on the generic function -@code{output-files-for-system-and-operation}. - -@c See the [manual][binary-locations] for more details. - -@subsection Notes and Issues - -SBCL ships with several included ASDF libraries which used to confuse -ABL because it would try to recompile the FASLs and then run into -directory permission problems. ASDF knows about these and uses a mapping -like @file{/usr/local/lib/sbcl} to @code{nil} so that the FASLs are not -relocated. - -@node Error handling, Compilation error and warning handling, Controlling where ASDF saves compiled files, Top -@comment node-name, next, previous, up -@chapter Error handling -@findex SYSTEM-DEFINITION-ERROR -@findex OPERATION-ERROR - -It is an error to define a system incorrectly: an implementation may -detect this and signal a generalised instance of -@code{SYSTEM-DEFINITION-ERROR}. - -Operations may go wrong (for example when source files contain -errors). These are signalled using generalised instances of -@code{OPERATION-ERROR}. - -@node Compilation error and warning handling, Miscellaneous additional functionality, Error handling, Top -@comment node-name, next, previous, up -@chapter Compilation error and warning handling -@vindex *compile-file-warnings-behaviour* -@vindex *compile-file-errors-behavior* - -ASDF checks for warnings and errors when a file is compiled. The -variables @code{*compile-file-warnings-behaviour*} and -@code{*compile-file-errors-behavior*} controls the handling of any -such events. The valid values for these variables are @code{:error}, -@code{:warn}, and @code{:ignore}. - -@node Miscellaneous additional functionality, Getting the latest version, Compilation error and warning handling, Top -@comment node-name, next, previous, up -@chapter Additional Functionality - -@emph{FIXME: Add discussion of @code{run-shell-command}? Others?} - -ASDF includes several additional features that are generally -useful for system definition and development. These include: - -@enumerate -@item -system-relative-pathname - -It's often handy to locate a file relative to some system. The system-relative-pathname function meets this need. It takes two arguments: the name of a system and a relative pathname. It returns a pathname built from the -location of the system's source file and the relative pathname. For example - -@lisp -> (asdf:system-relative-pathname 'cl-ppcre "regex.data") -#P"/repository/other/cl-ppcre/regex.data" -@end lisp - -@end enumerate - - -@node Getting the latest version, FAQ, Miscellaneous additional functionality, Top -@comment node-name, next, previous, up -@chapter Getting the latest version - -Decide which version you want. HEAD is the newest version and -usually OK, whereas RELEASE is for cautious people (e.g. who already -have systems using asdf that they don't want broken), a slightly older -version about which none of the HEAD users have complained. -There is also a STABLE version, which is earlier than release. - -You may get the ASDF source repository using git: -@kbd{git clone http://common-lisp.net/project/asdf/asdf.git} - -You will find the above referenced tags in this repository. - -Discussion of ASDF development is conducted on the mailing list -@kbd{asdf-devel@@common-lisp.net}. - -@node FAQ, TODO list, Getting the latest version, Top -@comment node-name, next, previous, up -@chapter FAQ - - -@itemize -@item ``My Common Lisp implementation comes with an outdated version of ASDF. What to do?'' - -More up-to-date versions of ASDF are distributed with an @file{asdf.asd} -file, and @emph{should} load cleanly on top of older versions. So you -should be able to load this definition file (or add its pathname to your -@code{asdf:*central-registry*} variable), and then do: -@example -(asdf:oos 'asdf:load-op :asdf) -@end example - -If this does not work, it is a bug, and you should report it -(@pxref{FAQ, report-bugs, Where do I report a bug}). - - -@item ``How can I cater for unit-testing in my system?'' - -ASDF provides a predefined test operation, @code{test-op}. -@xref{Predefined operations of asdf, test-op}. -The test operation, however, is largely left to the system definer to specify. -@code{test-op} has been -a topic of considerable discussion on the asdf-devel mailing list, and -on the launchpad bug-tracker. - -Here are some guidelines: - -@itemize -@item -For a given system, @var{foo}, you will want to define a corresponding -test system, such as @var{foo-test}. The reason that you will want this -separate system is that ASDF does not out of the box supply components -that are conditionally loaded. So if you want to have source files -(with the test definitions) that will not be loaded except when testing, -they should be put elsewhere. - -@item -The @var{foo-test} system can be defined in an asd file of its own or -together with @var{foo}. An aesthetic preference against cluttering up -the filesystem with extra asd files should be balanced against the -question of whether one might want to directly load @var{foo-test}. -Typically one would not want to do this except in early stages of -debugging. - -@item -Record that testing is implemented by @var{foo-test}. For example: -@example -(defsystem @var{foo} - :in-order-to ((test-op (test-op @var{foo-test}))) - ....) - -(defsystem @var{foo-test} - :depends-on (@var{foo} @var{my-test-library} ...) - ....) -@end example -@end itemize - -This procedure will allow you to support users who do not wish to -install your test framework. - -One oddity of ASDF is that @code{operate} (@pxref{Operations,operate}) -does not return a value. So in current versions of ASDF there is no -reliable programmatic means of determining whether or not a set of tests -has passed, or which tests have failed. The user must simply read the -console output. This limitation has been the subject of much -discussion. - -item ``How can I cater for documentation generation in my system?'' - -The ASDF developers are currently working to add a @code{doc-op} to the -set of predefined ASDF operations (@pxref{Predefined operations of -asdf}). See also @url{https://bugs.launchpad.net/asdf/+bug/479470}. - -@item ``How can I customize where fasl files are stored?'' - -@xref{Controlling where ASDF saves compiled files}. - -Note that in the past there was an add-on to ASDF called -ASDF-binary-locations, developed by Gary King. That add-on has been -merged into ASDF proper. - -Note that use of asdf-binary-locations can interfere with one aspect of -your systems --- if your system uses @code{*load-truename*} to find -files (e.g., if you have some data files stored with your program), then -the relocation that this ASDF customization performs is likely to -interfere. - -@item ``How can I maintain non-Lisp (e.g. C) source files?'' - -@anchor{report-bugs} -@item Where do I report a bug? - -ASDF bugs are tracked on launchpad: @url{https://launchpad.net/asdf}. - -@item ``I want to put my module's files at the top level. How do I do this?'' - -By default, the files contained in an asdf module go -in a subdirectory with the same name as the module. -However, this can be overridden by adding a @code{:pathname} argument -to the module description. -For example, here is how it is done -in the spatial-trees ASDF system definition: - -@example -(asdf:defsystem :spatial-trees - :components - ((:module base - :pathname "" - :components - ((:file "package") - (:file "basedefs" :depends-on ("package")) - (:file "rectangles" :depends-on ("package")))) - (:module tree-impls - :depends-on (base) - :pathname "" - :components - ((:file "r-trees") - (:file "greene-trees" :depends-on ("r-trees")) - (:file "rstar-trees" :depends-on ("r-trees")) - (:file "rplus-trees" :depends-on ("r-trees")) - (:file "x-trees" :depends-on ("r-trees" "rstar-trees")))) - (:module viz - :depends-on (base) - :pathname "" - :components - ((:static-file "spatial-tree-viz.lisp"))) - (:module tests - :depends-on (base) - :pathname "" - :components - ((:static-file "spatial-tree-test.lisp"))) - (:static-file "LICENCE") - (:static-file "TODO"))) -@end example - -All of the files in the @code{tree-impls} module are at the top level, -instead of in a @code{tree-impls/} subdirectory. - -Note that the argument to @code{:pathname} can be either a pathname object or a string. -A pathname object can be constructed with the @code{#p"foo/bar/"} syntax, -but this is discouraged because the results of parsing a namestring are not portable. -A pathname can only be portably constructed with such syntax as -@code{#.(make-pathname :directory '(:relative "foo" "bar"))}, -and similarly the current directory can only be portably specified as -@code{#.(make-pathname :directory '(:relative))}. -However, as of ASDF 1.624, you can portably use a string to denote a pathname. -The string will be parsed as a @code{/}-separated path from the current directory, -such that the empty string @code{""} denotes the current directory, and -@code{"foo/bar"} (no trailing @code{/} required in the case of modules) -portably denotes the same subdirectory as above. -When files are specified, the last @code{/}-separated component is interpreted -either as the name component of a pathname -(if the component class specifies a pathname type), -or as a name component plus optional dot-separated type component -(if the component class doesn't specifies a pathname type). - -@end itemize - -@node TODO list, missing bits in implementation, FAQ, Top -comment node-name, next, previous, up -@chapter TODO list - -* Outstanding spec questions, things to add - -** packaging systems - -*** manual page component? - -** style guide for .asd files - -You should either use keywords or be careful with the package that you -evaluate defsystem forms in. Otherwise (defsystem partition ...) -being read in the cl-user package will intern a cl-user:partition -symbol, which will then collide with the partition:partition symbol. - -Actually there's a hairier packages problem to think about too. -in-order-to is not a keyword: if you read defsystem forms in a package -that doesn't use ASDF, odd things might happen - -** extending defsystem with new options - -You might not want to write a whole parser, but just to add options to -the existing syntax. Reinstate parse-option or something akin - -** document all the error classes - -** what to do with compile-file failure - -Should check the primary return value from compile-file and see if -that gets us any closer to a sensible error handling strategy - -** foreign files - -lift unix-dso stuff from db-sockets - -** Diagnostics - -A ``dry run'' of an operation can be made with the following form: - -@lisp -(traverse (make-instance '<operation-name>) - (find-system <system-name>) - 'explain) -@end lisp - -This uses unexported symbols. What would be a nice interface for this -functionality? - -@node missing bits in implementation, Inspiration, TODO list, Top -@comment node-name, next, previous, up -@chapter missing bits in implementation - -** all of the above - -** reuse the same scratch package whenever a system is reloaded from disk - -** rules for system pathname defaulting are not yet implemented properly - -** proclamations probably aren't - -** when a system is reloaded with fewer components than it previously - had, odd things happen - -we should do something inventive when processing a defsystem form, -like take the list of kids and setf the slot to nil, then transfer -children from old to new list as they're found - -** traverse may become a normal function - -If you're defining methods on traverse, speak up. - - -** a lot of load-op methods can be rewritten to use input-files - -so should be. - - -** (stuff that might happen later) - -*** david lichteblau's patch for symlink resolution? - -*** Propagation of the :force option. ``I notice that - - (oos 'compile-op :araneida :force t) - -also forces compilation of every other system the :araneida system -depends on. This is rarely useful to me; usually, when I want to force -recompilation of something more than a single source file, I want to -recompile only one system. So it would be more useful to have -make-sub-operation refuse to propagate @code{:force t} to other systems, and -propagate only something like @code{:force :recursively}. - -Ideally what we actually want is some kind of criterion that says to -which systems (and which operations) a @code{:force} switch will -propagate. - -The problem is perhaps that `force' is a pretty meaningless concept. -How obvious is it that @code{load :force t} should force -@emph{compilation}? But we don't really have the right dependency -setup for the user to compile @code{:force t} and expect it to work -(files will not be loaded after compilation, so the compile -environment for subsequent files will be emptier than it needs to be) - -What does the user actually want to do when he forces? Usually, for -me, update for use with a new version of the lisp compiler. Perhaps -for recovery when he suspects that something has gone wrong. Or else -when he's changed compilation options or configuration in some way -that's not reflected in the dependency graph. - -Other possible interface: have a 'revert' function akin to 'make clean' - -@lisp -(asdf:revert 'asdf:compile-op 'araneida) -@end lisp - -would delete any files produced by 'compile-op 'araneida. Of course, it -wouldn't be able to do much about stuff in the image itself. - -How would this work? - -traverse - -There's a difference between a module's dependencies (peers) and its -components (children). Perhaps there's a similar difference in -operations? For example, @code{(load "use") depends-on (load "macros")} is a -peer, whereas @code{(load "use") depends-on (compile "use")} is more of a -`subservient' relationship. - -@node Inspiration, Concept Index, missing bits in implementation, Top -@comment node-name, next, previous, up -@chapter Inspiration - -@section mk-defsystem (defsystem-3.x) - -We aim to solve basically the same problems as mk-defsystem does. -However, our architecture for extensibility better exploits CL -language features (and is documented), and we intend to be portable -rather than just widely-ported. No slight on the mk-defsystem authors -and maintainers is intended here; that implementation has the -unenviable task of supporting pre-ANSI implementations, which is -no longer necessary. - -The surface defsystem syntax of asdf is more-or-less compatible with -mk-defsystem, except that we do not support the @code{source-foo} and -@code{binary-foo} prefixes for separating source and binary files, and -we advise the removal of all options to specify pathnames. - -The mk-defsystem code for topologically sorting a module's dependency -list was very useful. - -@section defsystem-4 proposal - -Marco and Peter's proposal for defsystem 4 served as the driver for -many of the features in here. Notable differences are: - -@itemize -@item -We don't specify output files or output file extensions as part of the -system. - -If you want to find out what files an operation would create, ask the -operation. - -@item -We don't deal with CL packages - -If you want to compile in a particular package, use an in-package form -in that file (ilisp / SLIME will like you more if you do this anyway) - -@item -There is no proposal here that defsystem does version control. - -A system has a given version which can be used to check dependencies, -but that's all. -@end itemize - -The defsystem 4 proposal tends to look more at the external features, -whereas this one centres on a protocol for system introspection. - -@section kmp's ``The Description of Large Systems'', MIT AI Memu 801 - -Available in updated-for-CL form on the web at -@url{http://nhplace.com/kent/Papers/Large-Systems.html} - -In our implementation we borrow kmp's overall PROCESS-OPTIONS and -concept to deal with creating component trees from defsystem surface -syntax. [ this is not true right now, though it used to be and -probably will be again soon ] - - -@c ------------------- - - -@node Concept Index, Function and Class Index, Inspiration, Top -@unnumbered Concept Index - -@printindex cp - -@node Function and Class Index, Variable Index, Concept Index, Top -@unnumbered Function and Class Index - -@printindex fn - -@node Variable Index, , Function and Class Index, Top -@unnumbered Variable Index - -@printindex vr - - - - -@bye diff --git a/debian/changelog b/debian/changelog index 93354f040b9e54b5b71ad94445bfb90b1a8ff541..afccc21e030ea3163e6724dab840218f2cf864aa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ -cl-asdf (2:1.627-2) UNRELEASED; urgency=low +cl-asdf (2:1.704-1) UNRELEASED; urgency=low - * open for changes + * Imported new upstream. -- Peter Van Eynde <pvaneynd@debian.org> Sat, 27 Feb 2010 21:40:19 +0100 diff --git a/doc/.htaccess b/doc/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..b20598c315560bfc0a5f5ab3814572b74dc5a66c --- /dev/null +++ b/doc/.htaccess @@ -0,0 +1 @@ +Redirect /project/asdf/manual.html http://common-lisp.net/project/asdf/asdf.html diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d6d7b1c03c2660e202a5e43e070559593214c1db --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,30 @@ +user=frideau +website:=${user}@common-lisp.net:/project/asdf/public_html/ +output = asdf.html asdf asdf.pdf asdf.info +webfiles = index.html .htaccess style.css cclan.png lisp-logo120x80.png favicon.ico ${output} +intermediate = asdf.cps asdf.log asdf.vr asdf.aux asdf.fn asdf.toc asdf.vrs \ + asdf.cp asdf.fns asdf.ky asdf.pg asdf.tp + +all: asdf.html asdf.info asdf.pdf manual-html + +manual-html: asdf.texinfo + makeinfo --html asdf.texinfo + +asdf.html: asdf.texinfo + makeinfo --html --no-split --no-headers -o asdf.html asdf.texinfo + +asdf.info: asdf.texinfo + makeinfo asdf.texinfo + +asdf.pdf: asdf.texinfo + texi2pdf asdf.texinfo + +website: all + rsync -av ${webfiles} ${website} + +.PHONY: clean veryclean manual-html +clean: + rm -f ${intermediate} +veryclean: clean + rm -f asdf.html asdf.pdf asdf.info + rm -rf asdf/ diff --git a/doc/asdf.texinfo b/doc/asdf.texinfo new file mode 100644 index 0000000000000000000000000000000000000000..7d7689ae5b72651a7cf046dbde274f4002b3282c --- /dev/null +++ b/doc/asdf.texinfo @@ -0,0 +1,3127 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename asdf.info +@settitle ASDF Manual +@c %**end of header + +@c We use @&key, etc to escape & from TeX in lambda lists -- +@c so we need to define them for info as well. +@macro &allow-other-keys +&allow-other-keys +@end macro +@macro &optional +&optional +@end macro +@macro &rest +&rest +@end macro +@macro &key +&key +@end macro +@macro &body +&body +@end macro + +@c for install-info +@dircategory Software development +@direntry +* asdf: (asdf). Another System Definition Facility (for Common Lisp) +@end direntry + +@copying +This manual describes ASDF, a system definition facility +for Common Lisp programs and libraries. + +You can find the latest version of this manual at +@url{http://common-lisp.net/project/asdf/asdf.html}. + +ASDF Copyright @copyright{} 2001-2010 Daniel Barlow and contributors. + +This manual Copyright @copyright{} 2001-2010 Daniel Barlow and contributors. + +This manual revised @copyright{} 2009-2010 Robert P. Goldman and Francois-Rene Rideau. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@end copying + + + +@titlepage +@title asdf: another system definition facility + +@c The following two commands start the copyright page. +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@c Output the table of contents at the beginning. +@contents + +@c ------------------- + +@ifnottex + +@node Top, Introduction, (dir), (dir) +@top asdf: another system definition facility + +@insertcopying + +@menu +* Introduction:: +* Loading ASDF:: +* Configuring ASDF:: +* Using ASDF:: +* Defining systems with defsystem:: +* The object model of ASDF:: +* Controlling where ASDF searches for systems:: +* Controlling where ASDF saves compiled files:: +* Error handling:: +* Miscellaneous additional functionality:: +* Getting the latest version:: +* FAQ:: +* TODO list:: +* Inspiration:: +* Concept Index:: +* Function and Class Index:: +* Variable Index:: + +@c @detailmenu +@c --- The Detailed Node Listing --- + +@c Defining systems with defsystem + +@c * The defsystem form:: +@c * A more involved example:: +@c * The defsystem grammar:: +@c * Other code in .asd files:: + +@c The object model of ASDF + +@c * Operations:: +@c * Components:: + +@c Operations + +@c * Predefined operations of ASDF:: +@c * Creating new operations:: + +@c Components + +@c * Common attributes of components:: +@c * Pre-defined subclasses of component:: +@c * Creating new component types:: + +@c properties + +@c * Pre-defined subclasses of component:: +@c * Creating new component types:: + +@c @end detailmenu +@end menu + +@end ifnottex + +@c ------------------- + +@node Introduction, Loading ASDF, Top, Top +@comment node-name, next, previous, up +@chapter Introduction +@cindex ASDF-related features +@vindex *features* +@cindex Testing for ASDF +@cindex ASDF versions +@cindex :asdf +@cindex :asdf2 + +ASDF is Another System Definition Facility: +a tool for specifying how systems of Common Lisp software +are comprised of components (sub-systems and files), +and how to operate on these components in the right order +so that they can be compiled, loaded, tested, etc. + +ASDF presents three faces: +one for users of Common Lisp software who want to reuse other people's code, +one for writers of Common Lisp software who want to specify how to build their systems, +one for implementers of Common Lisp extensions who want to extend the build system. +@xref{Using ASDF,,Loading a system}, +to learn how to use ASDF to load a system. +@xref{Defining systems with defsystem}, +to learn how to define a system of your own. +@xref{The object model of ASDF}, for a description of +the ASDF internals and how to extend ASDF. + +@emph{Nota Bene}: +We are preparing for a release of ASDF 2, +which will have version 2.000 and later. +Current releases, in the 1.700 series and beyond, +should be considered as release candidates. +We're still working on polishing the code and documentation. +@ref{FAQ,,``What has changed between ASDF 1 and ASDF 2?''}. + + +@node Loading ASDF, Configuring ASDF, Introduction, Top +@comment node-name, next, previous, up +@chapter Loading ASDF +@vindex *central-registry* +@cindex link farm +@findex load-system +@findex compile-system +@findex test-system +@cindex system directory designator +@findex operate +@findex oos + +@c @menu +@c * Installing ASDF:: +@c @end menu + + +@section Loading a pre-installed ASDF + +Many Lisp implementations include a copy of ASDF. +You can usually load this copy using Common Lisp's @code{require} function: + +@lisp +(require :asdf) +@end lisp + +Consult your Lisp implementation's documentation for details. + +Hopefully, ASDF 2 will soon be bundled with every Common Lisp implementation, +and you can load it that way. + + +@section Checking whether ASDF is loaded + +To check whether ASDF is properly loaded in your current Lisp image, +you can run this form: + +@lisp +(asdf:asdf-version) +@end lisp + +If it returns a string, +that is the version of ASDF that is currently installed. + +If it raises an error, +then either ASDF is not loaded, or +you are using an old version of ASDF. + +You can check whether an old version is loaded +by checking if the ASDF package is present. +The form below will allow you to programmatically determine +whether a recent version is loaded, an old version is loaded, +or none at all: + +@lisp +(or #+asdf2 (asdf:asdf-version) #+asdf :old) +@end lisp + +If it returns a version number, that's the version of ASDF installed. +If it returns the keyword @code{:OLD}, +then you're using an old version of ASDF (from before 1.635). +If it returns @code{NIL} then ASDF is not installed. + +If you are running a version older than 1.703, +we recommend that you load a newer ASDF using the method below. + + +@section Upgrading ASDF + +If your implementation does provide ASDF 2 or later, +and you want to upgrade to a more recent version, +just install ASDF like any other package +(see @pxref{Loading ASDF,,Loading an otherwise installed ASDF} below), +configure ASDF as usual (see @pxref{Configuring ASDF} below), +and upgrade with: + +@lisp +(require :asdf) +(asdf:load-system :asdf) +@end lisp + +If on the other hand, your implementation only provides an old ASDF, +you will require a special configuration step and an old-style loading: + +@lisp +(require :asdf) +(push #p"@var{/path/to/new/asdf/}" asdf:*central-registry*) +(asdf:oos 'asdf:load-op :asdf) +@end lisp + +Don't forget the trailing @code{/} at the end of your pathname. + +Also, note that older versions of ASDF won't redirect their output, +or at least won't do it according to your usual ASDF 2 configuration. +You therefore need write access on the directory +where you install the new ASDF, +and make sure you're not using it +for multiple mutually incompatible implementations. +At worst, you may have to have multiple copies of the new ASDF, +e.g. one per implementation installation, to avoid clashes. + +Finally, note that there are some limitations to upgrading ASDF: +@itemize +@item +Any ASDF extension is invalidated, and will need to be reloaded. +@item +It is safer if you upgrade ASDF and its extensions as a special step +at the very beginning of whatever script you are running, +before you start using ASDF to load anything else. +@end itemize + + +@section Loading an otherwise installed ASDF + +If your implementation doesn't include ASDF, +if for some reason the upgrade somehow fails, +does not or cannot apply to your case, +you will have to install the file @file{asdf.lisp} +somewhere and load it with: + +@lisp +(load "/path/to/your/installed/asdf.lisp") +@end lisp + +The single file @file{asdf.lisp} is all you normally need to use ASDF. + +You can extract this file from latest release tarball on the +@url{http://common-lisp.net/project/asdf/,ASDF website}. +If you are daring and willing to report bugs, you can get +the latest and greatest version of ASDF from its git repository. +@xref{Getting the latest version}. + +For maximum convenience you might want to have ASDF loaded +whenever you start your Lisp implementation, +for example by loading it from the startup script or dumping a custom core +--- check your Lisp implementation's manual for details. + + +@node Configuring ASDF, Using ASDF, Loading ASDF, Top +@comment node-name, next, previous, up + +@chapter Configuring ASDF + +@section Configuring ASDF to find your systems + +So it may compile and load your systems, ASDF must be configured to find +the @file{.asd} files that contain system definitions. + +Since ASDF 2, the preferred way to configure where ASDF finds your systems is +the @code{source-registry} facility, +fully described in its own chapter of this manual. +@xref{Controlling where ASDF searches for systems}. + +The default location for a user to install Common Lisp software is under +@file{~/.local/share/common-lisp/source/}. +If you install software there, you don't need further configuration. +If you're installing software yourself at a location that isn't standard, +you have to tell ASDF where you installed it. See below. +If you're using some tool to install software, +the authors of that tool should already have configured ASDF. + +The simplest way to add a path to your search path, +say @file{/foo/bar/baz/quux/} +is to create the directory +@file{~/.config/common-lisp/source-registry.conf.d/} +and there create a file with any name of your choice, +for instance @file{42-bazquux.conf} +containing the line: + +@kbd{(:directory "/foo/bar/baz/quux/")} + +If you want all the subdirectories under @file{/foo/bar/baz/} +to be recursively scanned for @file{.asd} files, instead use: + +@kbd{(:tree "/foo/bar/baz/quux/")} + +Note that your Operating System distribution or your system administrator +may already have configured system-managed libraries for you. + +Also note that when choosing a filename, the convention is to use +the @file{.conf} extension +(and a non-empty extension is required for CLISP compatibility), +and it is customary to start the filename with two digits +that specify the order in which the directories will be scanned. + +ASDF will automatically read your configuration +the first time you try to find a system. +You can reset the source-registry configuration with: + +@lisp +(asdf:clear-source-registry) +@end lisp + +And you probably should do so before you dump your Lisp image, +if the configuration may change +between the machine where you save it at the time you save it +and the machine you resume it at the time you resume it. + + +@section Configuring ASDF to find your systems -- old style + +The old way to configure ASDF to find your systems is by +@code{push}ing directory pathnames onto the variable +@code{asdf:*central-registry*}. + +You must configure this variable between the time you load ASDF +and the time you first try to use it. +Loading and configuring ASDF presumably happen +as part of some initialization script that builds or starts +your Common Lisp software system. +(For instance, some SBCL users used to put it in their @file{~/.sbclrc}.) + +The @code{asdf:*central-registry*} is empty by default in ASDF 2, +but is still supported for compatibility with ASDF 1. +When used, it takes precedence over the above source-registry@footnote{ +It is possible to further customize +the system definition file search. +That's considered advanced use, and covered later: +search forward for +@code{*system-definition-search-functions*}. +@xref{Defining systems with defsystem}.}. + +For instance, if you wanted ASDF to find the @file{.asd} file +@file{/home/me/src/foo/foo.asd} your initialization script +could after it loads ASDF with @code{(require :asdf)} +configure it with: + +@lisp +(push "/home/me/src/foo/" asdf:*central-registry*) +@end lisp + +Note the trailing slash: when searching for a system, +ASDF will evaluate each entry of the central registry +and coerce the result to a pathname@footnote{ +ASDF will indeed call @code{EVAL} on each entry. +It will also skip entries that evaluate to @code{NIL}. + +Strings and pathname objects are self-evaluating, +in which case the @code{EVAL} step does nothing; +but you may push arbitrary SEXP onto the central registry, +that will be evaluated to compute e.g. things that depend +on the value of shell variables or the identity of the user. + +The variable @code{asdf:*central-registry*} is thus a list of +``system directory designators''. +A @dfn{system directory designator} is a form +which will be evaluated whenever a system is to be found, +and must evaluate to a directory to look in. +By ``directory'' here, we mean +``designator for a pathname with a supplied DIRECTORY component''. +} +at which point the presence of the trailing directory name separator +is necessary to tell Lisp that you're discussing a directory +rather than a file. + +Typically, however, there are a lot of @file{.asd} files, and +a common idiom was to have to put +a bunch of @emph{symbolic links} to @file{.asd} files +in a common directory +and push @emph{that} directory (the ``link farm'') +to the +@code{asdf:*central-registry*} +instead of pushing each of the many involved directories +to the @code{asdf:*central-registry*}. +ASDF knows how to follow such @emph{symlinks} +to the actual file location when resolving the paths of system components +(on Windows, you can use Windows shortcuts instead of POSIX symlinks). + +For example, if @code{#p"/home/me/cl/systems/"} (note the trailing slash) +is a member of @code{*central-registry*}, you could set up the +system @var{foo} for loading with asdf with the following +commands at the shell: + +@example +$ cd /home/me/cl/systems/ +$ ln -s ~/src/foo/foo.asd . +@end example + +This old style for configuring ASDF is not recommended for new users, +but it is supported for old users, and for users who want to programmatically +control what directories are added to the ASDF search path. + + +@section Configuring where ASDF stores object files +@findex clear-output-locations + +ASDF lets you configure where object files will be stored. +Sensible defaults are provided and +you shouldn't normally have to worry about it. + +This allows the same source code repository may be shared +between several versions of several Common Lisp implementations, +between several users using different compilation options +and without write privileges on shared source directories, etc. +This also allows to keep source directories uncluttered +by plenty of object files. + +Starting with ASDF 2, the @code{asdf-output-translations} facility +was added to ASDF itself, that controls where object files will be stored. +This facility is fully described in a chapter of this manual, +@ref{Controlling where ASDF saves compiled files}. + +The simplest way to add a translation to your search path, +say from @file{/foo/bar/baz/quux/} +to @file{/where/i/want/my/fasls/} +is to create the directory +@file{~/.config/common-lisp/asdf-output-translations.conf.d/} +and there create a file with any name of your choice, +for instance @file{42-bazquux.conf} +containing the line: + +@kbd{("/foo/bar/baz/quux/" "/where/i/want/my/fasls/")} + +To disable output translations for source under a given directory, +say @file{/toto/tata/} +you can create a file @file{40-disable-toto.conf} +with the line: + +@kbd{("/toto/tata/")} + +To wholly disable output translations for all directories, +you can create a file @file{00-disable.conf} +with the line: + +@kbd{(t t)} + +Note that your Operating System distribution or your system administrator +may already have configured translations for you. +In absence of any configuration, the default is to redirect everything +under an implementation-dependent subdirectory of @file{~/.cache/common-lisp/}. +@xref{Controlling where ASDF searches for systems}, for full details. + + +Also note that when choosing a filename, the convention is to use +the @file{.conf} extension +(and a non-empty extension is required for CLISP compatibility), +and it is customary to start the filename with two digits +that specify the order in which the directories will be scanned. + +ASDF will automatically read your configuration +the first time you try to find a system. +You can reset the source-registry configuration with: + +@lisp +(asdf:clear-output-translations) +@end lisp + +And you probably should do so before you dump your Lisp image, +if the configuration may change +between the machine where you save it at the time you save it +and the machine you resume it at the time you resume it. + +Finally note that before ASDF 2, +other ASDF add-ons offered the same functionality, +each in subtly different and incompatible ways: +ASDF-Binary-Locations, cl-launch, common-lisp-controller. +ASDF-Binary-Locations is now not needed anymore and should not be used. +cl-launch 3.0 and common-lisp-controller 7.1 have been updated +to just delegate this functionality to ASDF. + +@node Using ASDF, Defining systems with defsystem, Configuring ASDF, Top +@comment node-name, next, previous, up + +@chapter Using ASDF + +@section Loading a system + +The system @var{foo} is loaded (and compiled, if necessary) +by evaluating the following Lisp form: + +@example +(asdf:load-system :@var{foo}) +@end example + +On some implementations (namely, SBCL and Clozure CL), +ASDF hooks into the @code{CL:REQUIRE} facility +and you can just use: + +@example +(require :@var{foo}) +@end example + +In older versions of ASDF, you needed to use +@code{(asdf:oos 'asdf:load-op :@var{foo})}. +If your ASDF is too old to provide @code{asdf:load-system} though +we recommend that you upgrade to ASDF 2. +@xref{Loading ASDF,,Loading an otherwise installed ASDF}. + + +@section Other Operations + +ASDF provides three commands for the most common system operations: +@code{load-system}, @code{compile-system} or @code{test-system}. + +Because ASDF is an extensible system +for defining @emph{operations} on @emph{components}, +it also provides a generic function @code{operate} +(which is usually abbreviated by @code{oos}). +You'll use @code{oos} whenever you want to do something beyond +compiling, loading and testing. + +Output from ASDF and ASDF extensions are supposed to be sent +to the CL stream @code{*standard-output*}, +and so rebinding that stream around calls to @code{asdf:operate} +should redirect all output from ASDF operations. + +Reminder: before ASDF can operate on a system, however, +it must be able to find and load that system's definition. +@xref{Configuring ASDF,,Configuring ASDF to find your systems}. + + +@section Summary + +To use ASDF: + +@itemize +@item +Load ASDF itself into your Lisp image, either through +@code{(require :asdf)} or else through +@code{(load "/path/to/asdf.lisp")}. + +@item +Make sure ASDF can find system definitions +thanks to proper source-registry configuration. + +@item +Load a system with @code{(load-system :my-system)} +or use some other operation on some system of your choice. + +@end itemize + +@section Moving on + +That's all you need to know to use ASDF to load systems written by others. +The rest of this manual deals with writing system definitions +for Common Lisp software you write yourself, +including how to extend ASDF to define new operation and component types. + + +@node Defining systems with defsystem, The object model of ASDF, Using ASDF, Top +@comment node-name, next, previous, up +@chapter Defining systems with defsystem + +This chapter describes how to use asdf to define systems and develop +software. + + +@menu +* The defsystem form:: +* A more involved example:: +* The defsystem grammar:: +* Other code in .asd files:: +@end menu + +@node The defsystem form, A more involved example, Defining systems with defsystem, Defining systems with defsystem +@comment node-name, next, previous, up +@section The defsystem form + +Systems can be constructed programmatically +by instantiating components using @code{make-instance}. +Most of the time, however, it is much more practical to use +a static @code{defsystem} form. +This section begins with an example of a system definition, +then gives the full grammar of @code{defsystem}. + +Let's look at a simple system. +This is a complete file that would +usually be saved as @file{hello-lisp.asd}: + +@lisp +(in-package :asdf) + +(defsystem "hello-lisp" + :description "hello-lisp: a sample Lisp system." + :version "0.2" + :author "Joe User <joe@@example.com>" + :licence "Public Domain" + :components ((:file "packages") + (:file "macros" :depends-on ("packages")) + (:file "hello" :depends-on ("macros")))) +@end lisp + +Some notes about this example: + +@itemize + +@item +The file starts with an @code{in-package} form +to use package @code{asdf}. +You could instead start your definition by using +a qualified name @code{asdf:defsystem}. + +@item +If in addition to simply using @code{defsystem}, +you are going to define functions, +create ASDF extension, globally bind symbols, etc., +it is recommended that to avoid namespace pollution between systems, +you should create your own package for that purpose, +for instance replacing the above @code{(in-package :asdf)} with: + +@lisp +(defpackage :foo-system + (:use :cl :asdf)) + +(in-package :foo-system) +@end lisp + +@item +The @code{defsystem} form defines a system named @code{hello-lisp} +that contains three source files: +@file{packages}, @file{macros} and @file{hello}. + +@item +The file @file{macros} depends on @file{packages} +(presumably because the package it's in is defined in @file{packages}), +and the file @file{hello} depends on @file{macros} +(and hence, transitively on @file{packages}). +This means that ASDF will compile and load @file{packages} and @file{macros} +before starting the compilation of file @file{hello}. + +@item +The files are located in the same directory +as the file with the system definition. +ASDF resolves symbolic links (or Windows shortcuts) +before loading the system definition file and +stores its location in the resulting system@footnote{ +It is possible, though almost never necessary, to override this behaviour.}. +This is a good thing because the user can move the system sources +without having to edit the system definition. + +@end itemize + +@node A more involved example, The defsystem grammar, The defsystem form, Defining systems with defsystem +@comment node-name, next, previous, up +@section A more involved example + +Let's illustrate some more involved uses of @code{defsystem} via a +slightly convoluted example: + +@lisp +(defsystem "foo" + :version "1.0" + :components ((:module "foo" :components ((:file "bar") (:file"baz") + (:file "quux")) + :perform (compile-op :after (op c) + (do-something c)) + :explain (compile-op :after (op c) + (explain-something c))) + (:file "blah"))) +@end lisp + +The method-form tokens need explaining: essentially, this part: + +@lisp + :perform (compile-op :after (op c) + (do-something c)) + :explain (compile-op :after (op c) + (explain-something c)) +@end lisp + +has the effect of + +@lisp +(defmethod perform :after ((op compile-op) (c (eql ...))) + (do-something c)) +(defmethod explain :after ((op compile-op) (c (eql ...))) + (explain-something c)) +@end lisp + +where @code{...} is the component in question; +note that although this also supports @code{:before} methods, +they may not do what you want them to --- +a @code{:before} method on perform @code{((op compile-op) (c (eql ...)))} +will run after all the dependencies and sub-components have been processed, +but before the component in question has been compiled. + +@node The defsystem grammar, Other code in .asd files, A more involved example, Defining systems with defsystem +@comment node-name, next, previous, up +@section The defsystem grammar + +@example +system-definition := ( defsystem system-designator @var{option}* ) + +option := :components component-list + | :pathname pathname-specifier + | :default-component-class + | :perform method-form + | :explain method-form + | :output-files method-form + | :operation-done-p method-form + | :depends-on ( @var{dependency-def}* ) + | :serial [ t | nil ] + | :in-order-to ( @var{dependency}+ ) + +component-list := ( @var{component-def}* ) + +component-def := ( component-type simple-component-name @var{option}* ) + +component-type := :system | :module | :file | :static-file | other-component-type + +other-component-type := symbol-by-name (@pxref{The defsystem grammar,,Component types}) + +dependency-def := simple-component-name + | ( :feature name ) + | ( :version simple-component-name version-specifier) + +dependency := (dependent-op @var{requirement}+) +requirement := (required-op @var{required-component}+) + | (feature feature-name) +dependent-op := operation-name +required-op := operation-name | feature + +simple-component-name := string + | symbol + +pathname-specifier := pathname | string | symbol + +method-form := (operation-name qual lambda-list @&rest body) +qual := method qualifier +@end example + +@subsection Component names + +Component names (@code{simple-component-name}) +may be either strings or symbols. + +@subsection 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 @code{my-component-type}, in +the current package @code{my-system-asd} can be specified as +@code{:my-component-type}, or @code{my-component-type}. + +@subsection Pathname specifiers + +A pathname specifier (@code{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 @code{/} characters will be interpreted as directory separators. +Usually, Unix-style relative pathnames are used +(i.e. not starting with @code{/}, as opposed to absolute pathnames); +they are relative to the path of the parent component. +Finally, depending on the @code{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 @code{component-type}, +or else it will be extracted from the string itself (if applicable). + +For instance, the @code{component-type} @code{:module} +wants a directory pathname, and so a string @code{"foo/bar"} +will be interpreted as the pathname @file{#p"foo/bar/"}. +On the other hand, the @code{component-type} @code{:file} +wants a file of type @code{lisp}, and so a string @code{"foo/bar"} +will be interpreted as the pathname @file{#p"foo/bar.lisp"}, +and a string @code{"foo/bar.quux"} +will be interpreted as the pathname @file{#p"foo/bar.quux.lisp"}. +Finally, the @code{component-type} @code{:static-file} +wants a file without specifying a type, and so a string @code{"foo/bar"} +will be interpreted as the pathname @file{#p"foo/bar"}, +and a string @code{"foo/bar.quux"} +will be interpreted as the pathname @file{#p"foo/bar.quux"}. + +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. +Observations suggest that the type of systems we want to 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 @code{:case :common} +as argument to @code{make-pathname}, +which is reported not to work on some implementations. + +Pathnames objects may be given to override the path for a component. +Such objects are typically specified using reader macros such as @code{#p} +or @code{#.(make-pathname ...)}. +Note however, that @code{#p...} is a short for @code{#.(parse-namestring ...)} +and that the behavior @code{parse-namestring} is completely non-portable, +unless you are using Common Lisp @code{logical-pathname}s. +(@xref{The defsystem grammar,,Warning about logical pathnames}, below.) +Pathnames made with @code{#.(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, it is a rare case that pathname objects should be used at all. +Unhappily, ASDF 1 didn't properly support +parsing component names as strings specifying paths with directories, +and the cumbersome @code{#.(make-pathname ...)} syntax had to be used. +Note that when specifying pathname objects, no magic interpretation of the pathname +is made depending on the component type. +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. + + +@subsection Warning about logical pathnames + +To use logical pathnames, +you will have to provide a pathname object as a @code{:pathname} specifier +to components that use it, using such syntax as +@code{#p"LOGICAL-HOST:absolute;path;to;component.lisp"}. + +You only have to specify such logical pathname for your system or +some top-level component, as sub-components using the usual string syntax +for names will be properly merged with the pathname of their parent. +The specification of a logical pathname host however is @emph{not} +otherwise directly supported in the ASDF syntax +for pathname specifiers as strings. + +Logical pathnames are not specifically recommended to newcomers, +but are otherwise supported. +Moreover, the @code{asdf-output-translation} layer will +avoid trying to resolve and translate logical-pathnames, +so you can define yourself what translations you want to use +with the logical pathname facility. + +The user of logical pathnames will have to configure logical pathnames himself, +before they may be used, and ASDF provides no specific support for that. + + +@subsection Serial dependencies + +If the @code{:serial t} option is specified for a module, +ASDF will add dependencies for each each child component, +on all the children textually preceding it. +This is done as if by @code{:depends-on}. + +@lisp +:components ((:file "a") (:file "b") (:file "c")) +:serial t +@end lisp + +is equivalent to + +@lisp +:components ((:file "a") + (:file "b" :depends-on ("a")) + (:file "c" :depends-on ("a" "b"))) +@end lisp + + +@subsection Source location + +The @code{:pathname} option is optional in all cases for systems +defined via @code{defsystem}, +and in the usual case the user is recommended not to supply it. + +Instead, ASDF follows a hairy set of rules that are designed so that +@enumerate +@item +@code{find-system} +will load a system from disk +and have its pathname default to the right place. +@item +This pathname information will not be overwritten with +@code{*default-pathname-defaults*} +(which could be somewhere else altogether) +if the user loads up the @file{.asd} file into his editor +and interactively re-evaluates that form. +@end enumerate + +If a system is being loaded for the first time, +its top-level pathname will be set to: + +@itemize +@item +The host/device/directory parts of @code{*load-truename*}, +if it is bound. +@item +@code{*default-pathname-defaults*}, otherwise. +@end itemize + +If a system is being redefined, the top-level pathname will be + +@itemize +@item +changed, if explicitly supplied or obtained from @code{*load-truename*} +(so that an updated source location is reflected in the system definition) +@item +changed if it had previously been set from @code{*default-pathname-defaults*} +@item +left as before, if it had previously been set from @code{*load-truename*} +and @code{*load-truename*} is currently unbound +(so that a developer can evaluate a @code{defsystem} form +from within an editor without clobbering its source location) +@end itemize + +@node Other code in .asd files, , The defsystem grammar, Defining systems with defsystem +@section Other code in .asd files + +Files containing @code{defsystem} forms +are regular Lisp files that are executed by @code{load}. +Consequently, you can put whatever Lisp code you like into these files +(e.g., code that examines the compile-time environment +and adds appropriate features to @code{*features*}). +However, some conventions should be followed, +so that users can control certain details of execution +of the Lisp in @file{.asd} files: + +@itemize +@item +Any informative output +(other than warnings and errors, +which are the condition system's to dispose of) +should be sent to the standard CL stream @code{*standard-output*}, +so that users can easily control the disposition +of output from ASDF operations. +@end itemize + + +@node The object model of ASDF, Controlling where ASDF searches for systems, Defining systems with defsystem, Top +@comment node-name, next, previous, up +@chapter The object model of ASDF + +ASDF is designed in an object-oriented way from the ground up. +Both a system's structure and the operations that can be performed on systems +follow a protocol. +ASDF is extensible to new operations and to new component types. +This allows the addition of behaviours: +for example, a new component could be added for Java JAR archives, +and methods specialised on @code{compile-op} added for it +that would accomplish the relevant actions. + +This chapter deals with @emph{components}, the building blocks of a system, +and @emph{operations}, the actions that can be performed on a system. + + + +@menu +* Operations:: +* Components:: +@end menu + +@node Operations, Components, The object model of ASDF, The object model of ASDF +@comment node-name, next, previous, up +@section Operations +@cindex operation + +An @dfn{operation} object of the appropriate type is instantiated +whenever the user wants to do something with a system like + +@itemize +@item compile all its files +@item load the files into a running lisp environment +@item copy its source files somewhere else +@end itemize + +Operations can be invoked directly, or examined +to see what their effects would be without performing them. +@emph{FIXME: document how!} +There are a bunch of methods specialised on operation and component type +that actually do the grunt work. + +The operation object contains whatever state is relevant for this purpose +(perhaps a list of visited nodes, for example) +but primarily is a nice thing to specialise operation methods on +and easier than having them all be @code{EQL} methods. + +Operations are invoked on systems via @code{operate}. +@anchor{operate} +@deffn {Generic function} @code{operate} @var{operation} @var{system} @&rest @var{initargs} +@deffnx {Generic function} @code{oos} @var{operation} @var{system} @&rest @var{initargs} +@code{operate} invokes @var{operation} on @var{system}. +@code{oos} is a synonym for @code{operate}. + +@var{operation} is a symbol that is passed, along with the supplied +@var{initargs}, to @code{make-instance} to create the operation object. +@var{system} is a system designator. + +The @var{initargs} are passed to the @code{make-instance} call +when creating the operation object. +Note that dependencies may cause the operation +to invoke other operations on the system or its components: +the new operations will be created +with the same @var{initargs} as the original one. + +@end deffn + +@menu +* Predefined operations of ASDF:: +* Creating new operations:: +@end menu + +@node Predefined operations of ASDF, Creating new operations, Operations, Operations +@comment node-name, next, previous, up +@subsection Predefined operations of ASDF + +All the operations described in this section are in the @code{asdf} package. +They are invoked via the @code{operate} generic function. + +@lisp +(asdf:operate 'asdf:@var{operation-name} :@var{system-name} @{@var{operation-options ...}@}) +@end lisp + +@deffn Operation @code{compile-op} @&key @code{proclamations} + +This operation compiles the specified component. +If proclamations are supplied, they will be proclaimed. +This is a good place to specify optimization settings. + +When creating a new component type, +you should provide methods for @code{compile-op}. + +When @code{compile-op} is invoked, +component dependencies often cause some parts of the system +to be loaded as well as compiled. +Invoking @code{compile-op} +does not necessarily load all the parts of the system, though; +use @code{load-op} to load a system. +@end deffn + +@deffn Operation @code{load-op} @&key @code{proclamations} + +This operation loads a system. + +The default methods for @code{load-op} compile files before loading them. +For parity, your own methods on new component types should probably do so too. +@end deffn + +@deffn Operation @code{load-source-op} + +This operation will load the source for the files in a module +even if the source files have been compiled. +Systems sometimes have knotty dependencies +which require that sources are loaded +before they can be compiled. +This is how you do that. + +If you are creating a component type, +you need to implement this operation --- at least, where meaningful. +@end deffn + +@anchor{test-op} +@deffn Operation @code{test-op} + +This operation will perform some tests on the module. +The default method will do nothing. +The default dependency is to require +@code{load-op} to be performed on the module first. +The default @code{operation-done-p} is that the operation is @emph{never} done +--- +we assume that if you invoke the @code{test-op}, +you want to test the system, even if you have already done so. + +The results of this operation are not defined by ASDF. +It has proven difficult to define how the test operation +should signal its results to the user +in a way that is compatible with all of the various test libraries +and test techniques in use in the community. +@end deffn + +@c @deffn Operation test-system-version @&key minimum + +@c Asks the system whether it satisfies a version requirement. + +@c The default method accepts a string, which is expected to contain of a +@c number of integers separated by #\. characters. The method is not +@c recursive. The component satisfies the version dependency if it has +@c the same major number as required and each of its sub-versions is +@c greater than or equal to the sub-version number required. + +@c @lisp +@c (defun version-satisfies (x y) +@c (labels ((bigger (x y) +@c (cond ((not y) t) +@c ((not x) nil) +@c ((> (car x) (car y)) t) +@c ((= (car x) (car y)) +@c (bigger (cdr x) (cdr y)))))) +@c (and (= (car x) (car y)) +@c (or (not (cdr y)) (bigger (cdr x) (cdr y)))))) +@c @end lisp + +@c If that doesn't work for your system, you can override it. I hope +@c you have as much fun writing the new method as @verb{|#lisp|} did +@c reimplementing this one. +@c @end deffn + +@c @deffn Operation feature-dependent-op + +@c An instance of @code{feature-dependent-op} will ignore any components +@c which have a @code{features} attribute, unless the feature combination +@c it designates is satisfied by @code{*features*}. This operation is +@c not intended to be instantiated directly, but other operations may +@c inherit from it. + +@c @end deffn + +@node Creating new operations, , Predefined operations of ASDF, Operations +@comment node-name, next, previous, up +@subsection Creating new operations + +ASDF was designed to be extensible in an object-oriented fashion. +To teach ASDF new tricks, a programmer can implement the behaviour he wants +by creating a subclass of @code{operation}. + +ASDF's pre-defined operations are in no way ``privileged'', +but it is requested that developers never use the @code{asdf} package +for operations they develop themselves. +The rationale for this rule is that we don't want to establish a +``global asdf operation name registry'', +but also want to avoid name clashes. + +An operation must provide methods for the following generic functions +when invoked with an object of type @code{source-file}: +@emph{FIXME describe this better} + +@itemize + +@item @code{output-files} +The @code{output-files} method determines where the method will put its files. +It returns two values, a list of pathnames, and a boolean. +If the boolean is @code{T} then the pathnames are marked +not be translated by enclosing @code{:around} methods. +If the boolean is @code{NIL} then enclosing @code{:around} methods +may translate these pathnames, e.g. to ensure object files +are somehow stored in some implementation-dependent cache. +@item @code{perform} +The @code{perform} method must call @code{output-files} +to find out where to put its files, +because the user is allowed to override. +@item @code{output-files} +for local policy @code{explain} +@item @code{operation-done-p}, +if you don't like the default one + +@end itemize + +Operations that print output should send that output to the standard +CL stream @code{*standard-output*}, as the Lisp compiler and loader do. + +@node Components, , Operations, The object model of ASDF +@comment node-name, next, previous, up +@section Components +@cindex component +@cindex system +@cindex system designator +@vindex *system-definition-search-functions* + +A @dfn{component} represents a source file or +(recursively) a collection of components. +A @dfn{system} is (roughly speaking) a top-level component +that can be found via @code{find-system}. + +A @dfn{system designator} is a string or symbol +and behaves just like any other component name +(including with regard to the case conversion rules for component names). + + +@defun find-system system-designator &optional (error-p t) + +Given a system designator, @code{find-system} finds and returns a system. +If no system is found, an error of type +@code{missing-component} is thrown, +or @code{nil} is returned if @code{error-p} is false. + +To find and update systems, @code{find-system} funcalls each element +in the @code{*system-definition-search-functions*} list, +expecting a pathname to be returned. +The resulting pathname is loaded if either of the following conditions is true: + +@itemize +@item +there is no system of that name in memory +@item +the file's @code{last-modified} time exceeds the @code{last-modified} time +of the system in memory +@end itemize + +When system definitions are loaded from @file{.asd} files, +a new scratch package is created for them to load into, +so that different systems do not overwrite each others operations. +The user may also wish to (and is recommended to) +include @code{defpackage} and @code{in-package} forms +in his system definition files, however, +so that they can be loaded manually if need be. + +The default value of @code{*system-definition-search-functions*} +is a list of two functions. +The first function looks in each of the directories given +by evaluating members of @code{*central-registry*} +for a file whose name is the name of the system and whose type is @file{asd}. +The first such file is returned, +whether or not it turns out to actually define the appropriate system. +The second function does something similar, +for the directories specified in the @code{source-registry}. +Hence, it is strongly advised to define a system +@var{foo} in the corresponding file @var{foo.asd}. +@end defun + + +@menu +* Common attributes of components:: +* Pre-defined subclasses of component:: +* Creating new component types:: +@end menu + +@node Common attributes of components, Pre-defined subclasses of component, Components, Components +@comment node-name, next, previous, up +@subsection Common attributes of components + +All components, regardless of type, have the following attributes. +All attributes except @code{name} are optional. + +@subsubsection Name + +A component name is a string or a symbol. +If a symbol, its name is taken and lowercased. + +Unless overridden by a @code{:pathname} attribute, +the name will be interpreted as a pathname specifier according +to a Unix-style syntax. +@xref{The defsystem grammar,,Pathname specifiers}. + +@subsubsection Version identifier + +This optional attribute is used by the @code{test-system-version} operation. +@xref{Predefined operations of ASDF}. +For the default method of @code{test-system-version}, +the version should be a string of integers separated by dots, +for example @samp{1.0.11}. + +@emph{Nota Bene}: +This operation, planned for ASDF 1, +is still not implement yet as of ASDF 2. +Don't hold your breath. + + +@subsubsection Required features + +@emph{FIXME: This subsection seems to contradict the +@code{defsystem} grammar subsection, +which doesn't provide any obvious way to specify required features. +Furthermore, in 2009, discussions on the +@uref{http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel,asdf-devel mailing list} +suggested that the specification of required features may be broken, +and that no one may have been using them for a while. +Please contact the +@uref{http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel,asdf-devel mailing list} +if you are interested in getting this features feature fixed.} + +Traditionally defsystem users have used reader conditionals +to include or exclude specific per-implementation files. +This means that any single implementation cannot read the entire system, +which becomes a problem if it doesn't wish to compile it, +but instead for example to create an archive file containing all the sources, +as it will omit to process the system-dependent sources for other systems. + +Each component in an asdf system may therefore specify features using +the same syntax as @code{#+} does, and it will (somehow) be ignored for +certain operations unless the feature conditional is a member of +@code{*features*}. + + +@subsubsection Dependencies + +This attribute specifies dependencies of the component on its siblings. +It is optional but often necessary. + +There is an excitingly complicated relationship between the initarg +and the method that you use to ask about dependencies + +Dependencies are between (operation component) pairs. +In your initargs for the component, you can say + +@lisp +:in-order-to ((compile-op (load-op "a" "b") (compile-op "c")) + (load-op (load-op "foo"))) +@end lisp + +This means the following things: +@itemize +@item +before performing compile-op on this component, we must perform +load-op on @var{a} and @var{b}, and compile-op on @var{c}, +@item +before performing @code{load-op}, we have to load @var{foo} +@end itemize + +The syntax is approximately + +@verbatim +(this-op {(other-op required-components)}+) + +required-components := component-name + | (required-components required-components) + +component-name := string + | (:version string minimum-version-object) +@end verbatim + +Side note: + +This is on a par with what ACL defsystem does. +mk-defsystem is less general: it has an implied dependency + +@verbatim + for all x, (load x) depends on (compile x) +@end verbatim + +and using a @code{:depends-on} argument to say that @var{b} depends on +@var{a} @emph{actually} means that + +@verbatim + (compile b) depends on (load a) +@end verbatim + +This is insufficient for e.g. the McCLIM system, which requires that +all the files are loaded before any of them can be compiled ] + +End side note + +In ASDF, the dependency information for a given component and operation +can be queried using @code{(component-depends-on operation component)}, +which returns a list + +@lisp +((load-op "a") (load-op "b") (compile-op "c") ...) +@end lisp + +@code{component-depends-on} can be subclassed for more specific +component/operation types: these need to @code{(call-next-method)} +and append the answer to their dependency, unless +they have a good reason for completely overriding the default dependencies. + +If it weren't for CLISP, we'd be using @code{LIST} method +combination to do this transparently. +But, we need to support CLISP. +If you have the time for some CLISP hacking, +I'm sure they'd welcome your fixes. +@c Doesn't CLISP now support LIST method combination? + +@subsubsection pathname + +This attribute is optional and if absent (which is the usual case), +the component name will be used. + +@xref{The defsystem grammar,,Pathname specifiers}, +for an explanation of how this attribute is interpreted. + +Note that the @code{defsystem} macro (used to create a ``top-level'' system) +does additional processing to set the filesystem location of +the top component in that system. +This is detailed elsewhere. @xref{Defining systems with defsystem}. + +The answer to the frequently asked question +``how do I create a system definition +where all the source files have a @file{.cl} extension'' +is thus + +@lisp +(defmethod source-file-type ((c cl-source-file) (s (eql (find-system 'my-sys)))) + "cl") +@end lisp + +@subsubsection properties + +This attribute is optional. + +Packaging systems often require information about files or systems +in addition to that specified by ASDF's pre-defined component attributes. +Programs that create vendor packages out of ASDF systems therefore +have to create ``placeholder'' information to satisfy these systems. +Sometimes the creator of an ASDF system may know the additional +information and wish to provide it directly. + +@code{(component-property component property-name)} and +associated @code{setf} method will allow +the programmatic update of this information. +Property names are compared as if by @code{EQL}, +so use symbols or keywords or something. + +@menu +* Pre-defined subclasses of component:: +* Creating new component types:: +@end menu + +@node Pre-defined subclasses of component, Creating new component types, Common attributes of components, Components +@comment node-name, next, previous, up +@subsection Pre-defined subclasses of component + +@deffn Component source-file + +A source file is any file that the system does not know how to +generate from other components of the system. + +Note that this is not necessarily the same thing as +``a file containing data that is typically fed to a compiler''. +If a file is generated by some pre-processor stage +(e.g. a @file{.h} file from @file{.h.in} by autoconf) +then it is not, by this definition, a source file. +Conversely, we might have a graphic file +that cannot be automatically regenerated, +or a proprietary shared library that we received as a binary: +these do count as source files for our purposes. + +Subclasses of source-file exist for various languages. +@emph{FIXME: describe these.} +@end deffn + +@deffn Component module + +A module is a collection of sub-components. + +A module component has the following extra initargs: + +@itemize +@item +@code{:components} the components contained in this module + +@item +@code{:default-component-class} +All children components which don't specify their class explicitly +are inferred to be of this type. + +@item +@code{:if-component-dep-fails} +This attribute takes one of the values +@code{:fail}, @code{:try-next}, @code{:ignore}, +its default value is @code{:fail}. +The other values can be used for implementing conditional compilation +based on implementation @code{*features*}, +for the case where it is not necessary for all files in a module to be +compiled. +@emph{FIXME: such conditional compilation has been reported +to be broken in 2009.} + +@item +@code{:serial} When this attribute is set, +each subcomponent of this component is assumed to depend on all subcomponents +before it in the list given to @code{:components}, i.e. +all of them are loaded before a compile or load operation is performed on it. + +@end itemize + +The default operation knows how to traverse a module, so +most operations will not need to provide methods specialised on modules. + +@code{module} may be subclassed to represent components such as +foreign-language linked libraries or archive files. +@end deffn + +@deffn Component system + +@code{system} is a subclass of @code{module}. + +A system is a module with a few extra attributes for documentation +purposes; these are given elsewhere. +@xref{The defsystem grammar}. + +Users can create new classes for their systems: +the default @code{defsystem} macro takes a @code{:class} keyword argument. +@end deffn + +@node Creating new component types, , Pre-defined subclasses of component, Components +@comment node-name, next, previous, up +@subsection Creating new component types + +New component types are defined by subclassing one of the existing +component classes and specializing methods on the new component class. + +@emph{FIXME: this should perhaps be explained more throughly, +not only by example ...} + +As an example, suppose we have some implementation-dependent +functionality that we want to isolate +in one subdirectory per Lisp implementation our system supports. +We create a subclass of +@code{cl-source-file}: + +@lisp +(defclass unportable-cl-source-file (cl-source-file) + ()) +@end lisp + +A hypothetical function @code{system-dependent-dirname} +gives us the name of the subdirectory. +All that's left is to define how to calculate the pathname +of an @code{unportable-cl-source-file}. + +@lisp +(defmethod component-pathname ((component unportable-cl-source-file)) + (let ((pathname (call-next-method)) + (name (string-downcase (system-dependent-dirname)))) + (merge-pathnames* + (make-pathname :directory (list :relative name)) + pathname))) +@end lisp + +The new component type is used in a @code{defsystem} form in this way: + +@lisp +(defsystem :foo + :components + ((:file "packages") + ... + (:unportable-cl-source-file "threads" + :depends-on ("packages" ...)) + ... + ) +@end lisp + +@node Controlling where ASDF searches for systems, Controlling where ASDF saves compiled files, The object model of ASDF, Top +@comment node-name, next, previous, up +@chapter Controlling where ASDF searches for systems + +@section Configurations + +Configurations specify paths where to find system files. + +@enumerate + +@item +The search registry may use some hardcoded wrapping registry specification. +This allows some implementations (notably SBCL) to specify where to find +some special implementation-provided systems that +need to precisely match the version of the implementation itself. + +@item +An application may explicitly initialize the source-registry configuration +using the configuration API +(@pxref{Controlling where ASDF searches for systems,Configuration API,Configuration API}, below) +in which case this takes precedence. +It may itself compute this configuration from the command-line, +from a script, from its own configuration file, etc. + +@item +The source registry will be configured from +the environment variable @code{CL_SOURCE_REGISTRY} if it exists. + +@item +The source registry will be configured from +user configuration file +@file{$XDG_CONFIG_DIRS/common-lisp/source-registry.conf} +(which defaults to +@file{~/.config/common-lisp/source-registry.conf}) +if it exists. + +@item +The source registry will be configured from +user configuration directory +@file{$XDG_CONFIG_DIRS/common-lisp/source-registry.conf.d/} +(which defaults to +@file{~/.config/common-lisp/source-registry.conf.d/}) +if it exists. + +@item +The source registry will be configured from +system configuration file +@file{/etc/common-lisp/source-registry.conf} +if it exists/ + +@item +The source registry will be configured from +system configuration directory +@file{/etc/common-lisp/source-registry.conf.d/} +if it exists. + +@item +The source registry will be configured from a default configuration. +This configuration may allow for implementation-specific systems +to be found, for systems to be found the current directory +(at the time that the configuration is initialized) as well as +@code{:directory} entries for @file{$XDG_DATA_DIRS/common-lisp/systems/} and +@code{:tree} entries for @file{$XDG_DATA_DIRS/common-lisp/source/}. + +@end enumerate + +Each of these configuration is specified as a SEXP +in a trival domain-specific language (defined below). +Additionally, a more shell-friendly syntax is available +for the environment variable (defined yet below). + +Each of these configurations is only used if the previous +configuration explicitly or implicitly specifies that it +includes its inherited configuration. + +Additionally, some implementation-specific directories +may be automatically prepended to whatever directories are specified +in configuration files, no matter if the last one inherits or not. + +@section XDG base directory + +Note that we purport to respect the XDG base directory specification +as to where configuration files are located, +where data files are located, +where output file caches are located. +Mentions of XDG variables refer to that document. + +@uref{http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html} + +This specification allows the user to specify some environment variables +to customize how applications behave to his preferences. + +On Windows platforms, when not using Cygwin, +instead of the XDG base directory specification, +we try to use folder configuration from the registry regarding +@code{Common AppData} and similar directories. +However, support querying the Windows registry is limited as of ASDF 2, +and on many implementations, we may fall back to always using the defaults +without consulting the registry. +Patches welcome. + +@section Backward Compatibility + +For backward compatibility as well as for a practical backdoor for hackers, +ASDF will first search for @code{.asd} files in the directories specified in +@code{asdf:*central-registry*} +before it searches in the source registry above. + +@xref{Configuring ASDF,,Configuring ASDF to find your systems -- old style}. + +By default, @code{asdf:*central-registry*} will be empty. + +This old mechanism will therefore not affect you if you don't use it, +but will take precedence over the new mechanism if you do use it. + +@section Configuration DSL + +Here is the grammar of the SEXP DSL for source-registry configuration: + +@example +;; A configuration is single SEXP starting with keyword :source-registry +;; followed by a list of directives. +CONFIGURATION := (:source-registry DIRECTIVE ...) + +;; A directive is one of the following: +DIRECTIVE := + ;; add a single directory to be scanned (no recursion) + (:directory DIRECTORY-PATHNAME-DESIGNATOR) | + + ;; add a directory hierarchy, recursing but excluding specified patterns + (:tree DIRECTORY-PATHNAME-DESIGNATOR) | + + ;; override the default defaults for exclusion patterns + (:exclude PATTERN ...) | + + ;; splice the parsed contents of another config file + (:include REGULAR-FILE-PATHNAME-DESIGNATOR) | + + ;; Your configuration expression MUST contain + ;; exactly one of either of these: + :inherit-configuration | ; splices contents of inherited configuration + :ignore-inherited-configuration | ; drop contents of inherited configuration + + ;; This directive specifies that some default must be spliced. + :default-registry + +PATTERN := a string without wildcards, that will be matched exactly + against the name of a any subdirectory in the directory component + of a path. e.g. @code{"_darcs"} will match @file{#p"/foo/bar/_darcs/src/bar.asd"} +@end example + + +@section Configuration Directories + +Configuration directories consist in files each contains +a list of directives without any enclosing @code{(:source-registry ...)} form. +The files will be sorted by namestring as if by @code{string<} and +the lists of directives of these files with be concatenated in order. +An implicit @code{:inherit-configuration} will be included +at the end of the list. + +This allows for packaging software that has file granularity +(e.g. Debian's @code{dpkg} or some future version of @code{clbuild}) +to easily include configuration information about distributed software. + +The convention is that, for sorting purposes, +the names of files in such a directory begin with two digits +that determine the order in which these entries will be read. +Also, the type of these files is conventionally @code{"conf"} +and as a limitation to some implementations (e.g. GNU clisp), +the type cannot be @code{NIL}. + +Directories may be included by specifying a directory pathname +or namestring in an @code{:include} directive, e.g.: + +@example + (:include "/foo/bar/") +@end example + + +@section Shell-friendly syntax for configuration + +When considering environment variable @code{CL_SOURCE_REGISTRY} +ASDF will skip to next configuration if it's an empty string. +It will @code{READ} the string as a SEXP in the DSL +if it begins with a paren @code{(} +and it will be interpreted much like @code{TEXINPUTS} +list of paths, where + + * paths are separated + by a @code{:} (colon) on Unix platforms (including cygwin), + by a @code{;} (semicolon) on other platforms (mainly, Windows). + + * each entry is a directory to add to the search path. + + * if the entry ends with a double slash @code{//} + then it instead indicates a tree in the subdirectories + of which to recurse. + + * if the entry is the empty string (which may only appear once), + then it indicates that the inherited configuration should be + spliced there. + + +@section Search Algorithm + +In case that isn't clear, the semantics of the configuration is that +when searching for a system of a given name, +directives are processed in order. + +When looking in a directory, if the system is found, the search succeeds, +otherwise it continues. + +When looking in a tree, if one system is found, the search succeeds. +If multiple systems are found, the consequences are unspecified: +the search may succeed with any of the found systems, +or an error may be raised. +ASDF currently returns the first system found, +XCVB currently raised an error. +If none is found, the search continues. + +Exclude statements specify patterns of subdirectories the systems of which +to ignore. Typically you don't want to use copies of files kept by such +version control systems as Darcs. + +Include statements cause the search to recurse with the path specifications +from the file specified. + +An inherit-configuration statement cause the search to recurse with the path +specifications from the next configuration +(@pxref{Controlling where ASDF searches for systems,,Configurations} above). + + +@section Caching Results + +The implementation is allowed to either eagerly compute the information +from the configurations and file system, or to lazily re-compute it +every time, or to cache any part of it as it goes. +To explicitly flush any information cached by the system, use the API below. + + +@section Configuration API + +The specified functions are exported from your build system's package. +Thus for ASDF the corresponding functions are in package ASDF, +and for XCVB the corresponding functions are in package XCVB. + +@defun initialize-source-registry @&optional PARAMETER + will read the configuration and initialize all internal variables. + You may extend or override configuration + from the environment and configuration files + with the given @var{PARAMETER}, which can be + @code{NIL} (no configuration override), + or a SEXP (in the SEXP DSL), + a string (as in the string DSL), + a pathname (of a file or directory with configuration), + or a symbol (fbound to function that when called returns one of the above). +@end defun + +@defun clear-source-registry + undoes any source registry configuration + and clears any cache for the search algorithm. + You might want to call that before you + dump an image that would be resumed with a different configuration, + and return an empty configuration. + Note that this does not include clearing information about + systems defined in the current image, only about + where to look for systems not yet defined. +@end defun + +@defun ensure-source-registry @&optional PARAMETER + checks whether a source registry has been initialized. + If not, initialize it with the given @var{PARAMETER}. +@end defun + + +@section Future + +If this mechanism is successful, in the future, we may declare +@code{asdf:*central-registry*} obsolete and eventually remove it. +Any hook into implementation-specific search mechanisms will by then +have been integrated in the @code{:default-configuration} which everyone +should either explicitly use or implicit inherit. Some shell syntax +for it should probably be added somehow. + +But we're not there yet. For now, let's see how practical this new +source-registry is. + + +@section Rejected ideas + +Alternatives I considered and rejected included: + +@enumerate +@item Keep @code{asdf:*central-registry*} as the master with its current semantics, + and somehow the configuration parser expands the new configuration + language into a expanded series of directories of subdirectories to + lookup, pre-recursing through specified hierarchies. This is kludgy, + and leaves little space of future cleanups and extensions. + +@item Keep @code{asdf:*central-registry*} remains the master but extend its semantics + in completely new ways, so that new kinds of entries may be implemented + as a recursive search, etc. This seems somewhat backwards. + +@item Completely remove @code{asdf:*central-registry*} + and break backwards compatibility. + Hopefully this will happen in a few years after everyone migrate to + a better ASDF and/or to XCVB, but it would be very bad to do it now. + +@item Replace @code{asdf:*central-registry*} by a symbol-macro with appropriate magic + when you dereference it or setf it. Only the new variable with new + semantics is handled by the new search procedure. + Complex and still introduces subtle semantic issues. +@end enumerate + + +I've been suggested the below features, but have rejected them, +for the sake of keeping ASDF no more complex than strictly necessary. + +@itemize +@item + More syntactic sugar: synonyms for the configuration directives, such as + @code{(:add-directory X)} for @code{(:directory X)}, or @code{(:add-directory-hierarchy X)} + or @code{(:add-directory X :recurse t)} for @code{(:tree X)}. + +@item + The possibility to register individual files instead of directories. + +@item + Integrate Xach Beane's tilde expander into the parser, + or something similar that is shell-friendly or shell-compatible. + I'd rather keep ASDF minimal. But maybe this precisely keeps it + minimal by removing the need for evaluated entries that ASDF has? + i.e. uses of @code{USER-HOMEDIR-PATHNAME} and @code{$SBCL_HOME} + Hopefully, these are already superseded by the @code{:default-registry} + +@item + Using the shell-unfriendly syntax @code{/**} instead of @code{//} to specify recursion + down a filesystem tree in the environment variable. + It isn't that Lisp friendly either. +@end itemize + +@section TODO + +@itemize +@item Add examples +@end itemize + + +@section Credits for the source-registry + +Thanks a lot to Stelian Ionescu for the initial idea. + +Thanks to Rommel Martinez for the initial implementation attempt. + +All bad design ideas and implementation bugs are to mine, not theirs. +But so are good design ideas and elegant implementation tricks. + + --- Francois-Rene Rideau @email{fare@@tunes.org}, Mon, 22 Feb 2010 00:07:33 -0500 + + + +@node Controlling where ASDF saves compiled files, Error handling, Controlling where ASDF searches for systems, Top +@comment node-name, next, previous, up +@chapter Controlling where ASDF saves compiled files +@cindex asdf-output-translations +@vindex ASDF_OUTPUT_TRANSLATIONS + +Each Common Lisp implementation has its own format +for compiled files (fasls for short, short for ``fast loading''). +If you use multiple implementations +(or multiple versions of the same implementation), +you'll soon find your source directories +littered with various @file{fasl}s, @file{dfsl}s, @file{cfsl}s and so on. +Worse yet, some implementations use the same file extension +while changing formats from version to version (or platform to platform) +which means that you'll have to recompile binaries +as you switch from one implementation to the next. + +ASDF 2 includes the @code{asdf-output-translations} facility +to mitigate the problem. + +@section Configurations + +Configurations specify mappings from input locations to output locations. +Once again we rely on the XDG base directory specification for configuration. +@xref{Controlling where ASDF searches for systems,,XDG base directory}. + +@enumerate + +@item +Some hardcoded wrapping output translations configuration may be used. +This allows special output translations (or usually, invariant directories) +to be specified corresponding to the similar special entries in the source registry. + +@item +An application may explicitly initialize the output-translations +configuration using the Configuration API +in which case this takes precedence. +(@pxref{Controlling where ASDF saves compiled files,,Configuration API}.) +It may itself compute this configuration from the command-line, +from a script, from its own configuration file, etc. + +@item +The source registry will be configured from +the environment variable @code{ASDF_OUTPUT_TRANSLATIONS} if it exists. + +@item +The source registry will be configured from +user configuration file +@file{$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.conf} +(which defaults to +@file{~/.config/common-lisp/asdf-output-translations.conf}) +if it exists. + +@item +The source registry will be configured from +user configuration directory +@file{$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.conf.d/} +(which defaults to +@file{~/.config/common-lisp/asdf-output-translations.conf.d/}) +if it exists. + +@item +The source registry will be configured from +system configuration file +@file{/etc/common-lisp/asdf-output-translations.conf} +if it exists. + +@item +The source registry will be configured from +system configuration directory +@file{/etc/common-lisp/asdf-output-translations.conf.d/} +if it exists. + +@end enumerate + +Each of these configurations is specified as a SEXP +in a trival domain-specific language (defined below). +Additionally, a more shell-friendly syntax is available +for the environment variable (defined yet below). + +Each of these configurations is only used if the previous +configuration explicitly or implicitly specifies that it +includes its inherited configuration. + +Note that by default, a per-user cache is used for output files. +This allows the seamless use of shared installations of software +between several users, and takes files out of the way of the developers +when they browse source code, +at the expense of taking a small toll when developers have to clean up +output files and find they need to get familiar with output-translations first. + + +@section Backward Compatibility + +@c FIXME -- I think we should provide an easy way +@c to get behavior equivalent to A-B-L and +@c I will propose a technique for doing this. + +We purposefully do NOT provide backward compatibility with earlier versions of +@code{ASDF-Binary-Locations} (8 Sept 2009), +@code{common-lisp-controller} (7.0) or +@code{cl-launch} (2.35), +each of which had similar general capabilities. +The previous APIs of these programs were not designed +for configuration by the end-user +in an easy way with configuration files. +Recent versions of same packages use +the new @code{asdf-output-translations} API as defined below: +@code{common-lisp-controller} (7.1) and @code{cl-launch} (3.00); +@code{ASDF-Binary-Locations} is fully superseded and not to be used anymore. + +This incompatibility shouldn't inconvenience many people. +Indeed, few people use and customize these packages; +these few people are experts who can trivially adapt to the new configuration. +Most people are not experts, could not properly configure these features +(except inasmuch as the default configuration of +@code{common-lisp-controller} and/or @code{cl-launch} +might have been doing the right thing for some users), +and yet will experience software that ``just works'', +as configured by the system distributor, or by default. + +Nevertheless, if you are a fan of @code{ASDF-Binary-Locations}, +we provide a limited emulation mode: + +@defun asdf:enable-asdf-binary-locations-compatibility @&key centralize-lisp-binaries default-toplevel-directory include-per-user-information map-all-source-files source-to-target-mappings +This function will initialize the new @code{asdf-output-translations} facility in a way +that emulates the behavior of the old @code{ASDF-Binary-Locations} facility. +Where you would previously set global variables +@var{*centralize-lisp-binaries*}, +@var{*default-toplevel-directory*}, +@var{*include-per-user-information*}, +@var{*map-all-source-files*} or @var{*source-to-target-mappings*} +you will now have to pass the same values as keyword arguments to this function. +Note however that as an extension the @code{:source-to-target-mappings} keyword argument +will accept any valid pathname designator for @code{asdf-output-translations} +instead of just strings and pathnames. +@end defun + +If you insist, you can also keep using the old @code{ASDF-Binary-Locations} +(the one available as an extension to load of top of ASDF, +not the one built into a few old versions of ASDF), +but first you must disable @code{asdf-output-translations} +with @code{(asdf:disable-output-translations)}, +or you might experience ``interesting'' issues. + +Also, note that output translation is enabled by default. +To disable it, use @code{(asdf:disable-output-translations)}. + + +@section Configuration DSL + +Here is the grammar of the SEXP DSL +for @code{asdf-output-translations} configuration: + +@verbatim +;; A configuration is single SEXP starting with keyword :source-registry +;; followed by a list of directives. +CONFIGURATION := (:output-translations DIRECTIVE ...) + +;; A directive is one of the following: +DIRECTIVE := + ;; include a configuration file or directory + (:include PATHNAME-DESIGNATOR) | + + ;; Your configuration expression MUST contain + ;; exactly one of either of these: + :inherit-configuration | ; splices contents of inherited configuration + :ignore-inherited-configuration | ; drop contents of inherited configuration + + ;; enable global cache in ~/.common-lisp/cache/sbcl-1.0.35-x86-64/ or something. + :enable-user-cache | + ;; Disable global cache. Map / to / + :disable-cache | + + ;; add a single directory to be scanned (no recursion) + (DIRECTORY-DESIGNATOR DIRECTORY-DESIGNATOR) + + ;; use a function to return the translation of a directory designator + (DIRECTORY-DESIGNATOR (:function TRANSLATION-FUNCTION)) + +DIRECTORY-DESIGNATOR := + T | ;; as source matches anything, as destination leaves pathname unmapped. + ABSOLUTE-COMPONENT-DESIGNATOR | + (ABSOLUTE-COMPONENT-DESIGNATOR RELATIVE-COMPONENT-DESIGNATOR ...) + +ABSOLUTE-COMPONENT-DESIGNATOR := + NULL | ;; As source: skip this entry. As destination: same as source + :ROOT | ;; magic: paths that are relative to the root of the source host and device + STRING | ;; namestring (directory is assumed, better be absolute or bust, ``/**/*.*'' added) + PATHNAME | ;; pathname (better be an absolute directory or bust) + :HOME | ;; designates the user-homedir-pathname ~/ + :USER-CACHE | ;; designates the default location for the user cache + :SYSTEM-CACHE | ;; designates the default location for the system cache + :CURRENT-DIRECTORY ;; the current directory + +RELATIVE-COMPONENT-DESIGNATOR := + STRING | ;; namestring, directory is assumed. If the last component, /**/*.* is added + PATHNAME | ;; pathname unless last component, directory is assumed. + :IMPLEMENTATION | ;; a directory based on implementation, e.g. sbcl-1.0.32.30-linux-x86-64 + :IMPLEMENTATION-TYPE | ;; a directory based on lisp-implementation-type only, e.g. sbcl + :CURRENT-DIRECTORY | ;; all components of the current directory, without the :absolute + :UID | ;; current UID -- not available on Windows + :USER ;; current USER name -- NOT IMPLEMENTED(!) + +TRANSLATION-FUNCTION := + SYMBOL | ;; symbol of a function that takes two arguments, + ;; the pathname to be translated and the matching DIRECTORY-DESIGNATOR + LAMBDA ;; A form which evalutates to a function taking two arguments consisting of + ;; the pathname to be translated and the matching DIRECTORY-DESIGNATOR + +@end verbatim + +Relative components better be either relative +or subdirectories of the path before them, or bust. + +The last component, if not a pathname, is notionally completed by @file{/**/*.*}. +You can specify more fine-grained patterns +by using a pathname object as the last component +e.g. @file{#p"some/path/**/foo*/bar-*.fasl"} + +You may use @code{#+features} to customize the configuration file. + +The second designator of a mapping may be @code{NIL}, indicating that files are not mapped +to anything but themselves (same as if the second designator was the same as the first). + +When the first designator is @code{t}, +the mapping always matches. +When the first designator starts with @code{:root}, +the mapping matches any host and device. +In either of these cases, if the second designator +isn't @code{t} and doesn't start with @code{:root}, +then strings indicating the host and pathname are somehow copied +in the beginning of the directory component of the source pathname +before it is translated. + +When the second designator is @code{t}, the mapping is the identity. +When the second designator starts with @code{root}, +the mapping preserves the host and device of the original pathname. + +@code{:include} statements cause the search to recurse with the path specifications +from the file specified. + +If the @code{translate-pathname} mechanism cannot achieve a desired +translation, the user may provide a function which provides the +required algorithim. Such a translation function is specified by +supplying a list as the second @code{directory-designator} +the first element of which is the keyword @code{:function}, +and the second element of which is +either a symbol which designates a function or a lambda expression. +The function designated by the second argument must take two arguments, +the first being the pathname of the source file, +the second being the wildcard that was matched. +The result of the function invocation should be the translated pathname. + +An @code{:inherit-configuration} statement cause the search to recurse with the path +specifications from the next configuration. +@xref{Controlling where ASDF saves compiled files,,Configurations}, above. + +@itemize +@item +@code{:enable-user-cache} is the same as @code{(t :user-cache)}. +@item +@code{:disable-cache} is the same as @code{(t t)}. +@item +@code{:user-cache} uses the contents of variable @code{asdf::*user-cache*} +which by default is the same as using +@code{(:home ".cache" "common-lisp" :implementation)}. +@item +@code{:system-cache} uses the contents of variable @code{asdf::*system-cache*} +which by default is the same as using +@code{("/var/cache/common-lisp" :uid :implementation-type)} +(on Unix and cygwin), or something semi-sensible on Windows. +@end itemize + + +@section Configuration Directories + +Configuration directories consist in files each contains +a list of directives without any enclosing +@code{(:output-translations ...)} form. +The files will be sorted by namestring as if by @code{string<} and +the lists of directives of these files with be concatenated in order. +An implicit @code{:inherit-configuration} will be included +at the end of the list. + +This allows for packaging software that has file granularity +(e.g. Debian's @command{dpkg} or some future version of @command{clbuild}) +to easily include configuration information about software being distributed. + +The convention is that, for sorting purposes, +the names of files in such a directory begin with two digits +that determine the order in which these entries will be read. +Also, the type of these files is conventionally @code{"conf"} +and as a limitation of some implementations, the type cannot be @code{NIL}. + +Directories may be included by specifying a directory pathname +or namestring in an @code{:include} directive, e.g.: +@verbatim + (:include "/foo/bar/") +@end verbatim + +@section Shell-friendly syntax for configuration + +When considering environment variable @code{ASDF_OUTPUT_TRANSLATIONS} +ASDF will skip to next configuration if it's an empty string. +It will @code{READ} the string as an SEXP in the DSL +if it begins with a paren @code{(} +and it will be interpreted as a list of directories. +Directories should come by pairs, indicating a mapping directive. +Entries are separated +by a @code{:} (colon) on Unix platforms (including cygwin), +by a @code{;} (semicolon) on other platforms (mainly, Windows). + +The magic empty entry, +if it comes in what would otherwise be the first entry in a pair, +indicates the splicing of inherited configuration. +If it comes as the second entry in a pair, +it indicates that the directory specified first is to be left untranslated +(which has the same effect as if the directory had been repeated). + + +@section Semantics of Output Translations + +From the specified configuration, +a list of mappings is extracted in a straightforward way: +mappings are collected in order, recursing through +included or inherited configuration as specified. +To this list is prepended some implementation-specific mappings, +and is appended a global default. + +The list is then compiled to a mapping table as follows: +for each entry, in order, resolve the first designated directory +into an actual directory pathname for source locations. +If no mapping was specified yet for that location, +resolve the second designated directory to an output location directory +add a mapping to the table mapping the source location to the output location, +and add another mapping from the output location to itself +(unless a mapping already exists for the output location). + +Based on the table, a mapping function is defined, +mapping source pathnames to output pathnames: +given a source pathname, locate the longest matching prefix +in the source column of the mapping table. +Replace that prefix by the corresponding output column +in the same row of the table, and return the result. +If no match is found, return the source pathname. +(A global default mapping the filesystem root to itself +may ensure that there will always be a match, +with same fall-through semantics). + +@section Caching Results + +The implementation is allowed to either eagerly compute the information +from the configurations and file system, or to lazily re-compute it +every time, or to cache any part of it as it goes. +To explicitly flush any information cached by the system, use the API below. + + +@section Output location API + +The specified functions are exported from package ASDF. + +@defun initialize-output-translations @&optional PARAMETER + will read the configuration and initialize all internal variables. + You may extend or override configuration + from the environment and configuration files + with the given @var{PARAMETER}, which can be + @code{NIL} (no configuration override), + or a SEXP (in the SEXP DSL), + a string (as in the string DSL), + a pathname (of a file or directory with configuration), + or a symbol (fbound to function that when called returns one of the above). +@end defun + +@defun disable-output-translations + will initialize output translations in a way + that maps every pathname to itself, + effectively disabling the output translation facility. +@end defun + +@defun clear-output-translations + undoes any output translation configuration + and clears any cache for the mapping algorithm. + You might want to call that before you + dump an image that would be resumed with a different configuration, + and return an empty configuration. + Note that this does not include clearing information about + systems defined in the current image, only about + where to look for systems not yet defined. +@end defun + +@defun ensure-output-translations @&optional PARAMETER + checks whether output translations have been initialized. + If not, initialize them with the given @var{PARAMETER}. + This function will be called before any attempt to operate on a system. +@end defun + +@defun apply-output-translations PATHNAME + Applies the configured output location translations to @var{PATHNAME} + (calls @code{ensure-output-translations} for the translations). +@end defun + + +@section Credits for output translations + +Thanks a lot to Bjorn Lindberg and Gary King for @code{ASDF-Binary-Locations}, +and to Peter van Eynde for @code{Common Lisp Controller}. + +All bad design ideas and implementation bugs are to mine, not theirs. +But so are good design ideas and elegant implementation tricks. + + --- Francois-Rene Rideau @email{fare@@tunes.org} + +@c @section Default locations +@c @findex output-files-for-system-and-operation + +@c The default binary location for each Lisp implementation +@c is a subdirectory of each source directory. +@c To account for different Lisps, Operating Systems, Implementation versions, +@c and so on, ASDF borrows code from SLIME +@c to create reasonable custom directory names. +@c Here are some examples: + +@c @itemize +@c @item +@c SBCL, version 1.0 on Mac OS X for intel: @code{sbcl-1.0-darwin-x86} + +@c @item +@c Franz Allegro, version 8.0, ANSI Common Lisp: @code{allegro-8.0a-macosx-x86} + +@c @item +@c Franz Allegro, version 8.1, Modern (case sensitive) Common Lisp: @code{allegro-8.1m-macosx-x86} +@c @end itemize + +@c By default, all output file pathnames will be relocated +@c to some thus-named subdirectory of @file{~/.cache/common-lisp/}. + +@c See the document @file{README.asdf-output-translations} +@c for a full specification on how to configure @code{asdf-output-translations}. + +@node Error handling, Miscellaneous additional functionality, Controlling where ASDF saves compiled files, Top +@comment node-name, next, previous, up +@chapter Error handling +@findex SYSTEM-DEFINITION-ERROR +@findex OPERATION-ERROR + +@section ASDF errors + +If ASDF detects an incorrect system definition, it will signal a generalised instance of +@code{SYSTEM-DEFINITION-ERROR}. + +Operations may go wrong (for example when source files contain errors). +These are signalled using generalised instances of +@code{OPERATION-ERROR}. + +@section Compilation error and warning handling +@vindex *compile-file-warnings-behaviour* +@vindex *compile-file-errors-behavior* + +ASDF checks for warnings and errors when a file is compiled. +The variables @var{*compile-file-warnings-behaviour*} and +@var{*compile-file-errors-behavior*} +control the handling of any such events. +The valid values for these variables are +@code{:error}, @code{:warn}, and @code{:ignore}. + +@node Miscellaneous additional functionality, Getting the latest version, Error handling, Top +@comment node-name, next, previous, up +@chapter Miscellaneous additional functionality + +@emph{FIXME: Add discussion of @code{run-shell-command}? Others?} + +ASDF includes several additional features that are generally +useful for system definition and development. These include: + +@defun system-relative-pathname system name @&key type + +It's often handy to locate a file relative to some system. +The @code{system-relative-pathname} function meets this need. +It takes two arguments: the name of a system and a relative pathname. +It returns a pathname built from the location of the system's source file +and the relative pathname. For example + +@lisp +> (asdf:system-relative-pathname 'cl-ppcre #p"regex.data") +#P"/repository/other/cl-ppcre/regex.data" +@end lisp + +Instead of a pathname, you can provide a symbol or a string, +and optionally a keyword argument @code{type}. +The arguments will then be interpreted in the same way +as pathname specifiers for components. +@xref{The defsystem grammar,,Pathname specifiers}. +@end defun + +@defun system-source-directory system-designator + +ASDF does not provide a turnkey solution for locating data (or other +miscellaneous) files that are distributed together with the source code +of a system. Programmers can use @code{system-source-directory} to find +such files. Returns a pathname object. The @var{system-designator} may +be a string, symbol, or ASDF system object. +@end defun + + +@node Getting the latest version, FAQ, Miscellaneous additional functionality, Top +@comment node-name, next, previous, up +@chapter Getting the latest version + +Decide which version you want. +HEAD is the newest version and usually OK, whereas +RELEASE is for cautious people +(e.g. who already have systems using ASDF that they don't want broken), +a slightly older version about which none of the HEAD users have complained. +There is also a STABLE version, which is earlier than release. + +You may get the ASDF source repository using git: +@kbd{git clone http://common-lisp.net/project/asdf/asdf.git} + +You will find the above referenced tags in this repository. +You can also browse the repository on +@url{http://common-lisp.net/gitweb?p=projects/asdf/asdf.git}. + +Discussion of ASDF development is conducted on the +mailing list +@kbd{asdf-devel@@common-lisp.net}. +@uref{http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel} + + +@node FAQ, TODO list, Getting the latest version, Top +@comment node-name, next, previous, up +@chapter FAQ + +@section ``Where do I report a bug?'' + +ASDF bugs are tracked on launchpad: @url{https://launchpad.net/asdf}. + +If you're unsure about whether something is a bug, of for general discussion, +use the @uref{http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel,asdf-devel mailing list} + + +@section ``What has changed between ASDF 1 and ASDF 2?'' + +@subsection What are ASDF 1 and ASDF 2? + +We are preparing for a release of ASDF 2, +which will have version 2.000 and later. +While the code and documentation are essentially complete +we are still working on polishing them before release. + +Releases in the 1.700 series and beyond +should be considered as release candidates. +For all practical purposes, +ASDF 2 refers to releases later than 1.656, +and ASDF 1 to any release earlier than 1.369 or so. +If your ASDF doesn't have a version, it's old. + +ASDF 2 release candidates and beyond will have +@code{:asdf2} onto @code{*features*} so that if you are writing +ASDF-dependent code you may check for this feature +to see if the new API is present. +@emph{All} versions of ASDF should have the @code{:asdf} feature. + +If you are experiencing problems or limitations of any sort with ASDF 1, +we recommend that you should upgrade to ASDF 2 or its latest release candidate. + + +@subsection ASDF can portably name files in subdirectories + +Common Lisp namestrings are not portable, +except maybe for logical pathnamestrings, +that themselves require a lot of setup that is itself ultimately non-portable. +The only portable ways to refer to pathnames inside systems and components +were very awkward, using @code{#.(make-pathname ...)} and +@code{#.(merge-pathnames ...)}. +Even the above were themselves were inadequate in the general case +due to host and device issues, unless horribly complex patterns were used. +Plenty of simple cases that looked portable actually weren't, +leading to much confusion and greavance. + +ASDF 2 implements its own portable syntax for strings as pathname specifiers. +Naming files within a system definition becomes easy and portable again. +@xref{Miscellaneous additional functionality,asdf:system-relative-pathname}, +@code{asdf-utilities:merge-pathnames*}, +@code{asdf::merge-component-name-type}. + +@xref{The defsystem grammar,,Pathname specifiers}. + +@subsection Output translations + +A popular feature added to ASDF was output pathname translation: +@code{asdf-binary-locations}, @code{common-lisp-controller}, +@code{cl-launch} and other hacks were all implementing it in ways +both mutually incompatible and difficult to configure. + +Output pathname translation is essential to share +source directories of portable systems across multiple implementations +or variants thereof, +or source directories of shared installations of systems across multiple users, +or combinations of the above. + +In ASDF 2, a standard mechanism is provided for that, +@code{asdf-output-translations}, +with sensible defaults, adequate configuration languages, +a coherent set of configuration files and hooks, +and support for non-Unix platforms. + +@xref{Controlling where ASDF saves compiled files}. + +@subsection Source Registry Configuration + +Configuring ASDF used to require special magic +to be applied just at the right moment, +between the moment ASDF is loaded and the moment it is used, +in a way that is specific to the user, +the implementation he is using and the application he is building. + +This made for awkward configuration files and startup scripts +that could not be shared between users, managed by administrators +or packaged by distributions. + +ASDF 2 provides a well-documented way to configure ASDF, +with sensible defaults, adequate configuration languages, +and a coherent set of configuration files and hooks. + +At the same time, ASDF 2 remains compatible +with the old magic you may have in your build scripts +to tailor the ASDF configuration to your build automation needs, +and also allows for new magic, simpler and more powerful magic. + +@xref{Controlling where ASDF searches for systems}. + +@subsection Usual operations are made easier to the user + +In ASDF 1, you had to use the awkward syntax +@code{(asdf:oos 'asdf:load-op :foo)} +to load a system, +and similarly for @code{compile-op}, @code{test-op}. + +In ASDF 2, you can use shortcuts for the usual operations: +@code{(asdf:load-system :foo)}, and +similarly for @code{compile-system}, @code{test-system}. + + +@subsection Many bugs have been fixed + +These issues and many others have been fixed, +including the following: + +The infamous TRAVERSE function has been revamped significantly, +with many bugs squashed. +In particular, dependencies were not correctly propagated +across submodules within a system but now are. +The :version and :feature features and +the :force (system1 .. systemN) feature have been fixed. + +Many features used to not be portable, +especially where pathnames were involved. + +The internal test suite used to massively fail +on many implementations. + +Support was broken for some implementations (notably ABCL). + +The documentation was grossly out of date. + +ECL extensions were not integrated in the ASDF release. + + +@subsection ASDF itself is versioned + +Between new features, old bugs fixed, and new bugs introduced, +there were various releases of ASDF in the wild, +and no simple way to check which release had which feature set. +People using or writing systems had to either make worst-case assumptions +as to what features were available and worked, +or take great pains to have the correct version of ASDF installed. + +With ASDF 2, we provide a new stable set of working features +that everyone can rely on from now on. +Use @code{#+asdf2} to detect presence of ASDF 2, +@code{(asdf:version-satisfies (asdf:asdf-version) "1.703")} +to check the availability of a version no earlier than required. + +@subsection ASDF can be upgraded + +When an old version of ASDF was loaded, +it was very hard to upgrade ASDF in your current image +without breaking everything. +Instead you have to exit the Lisp process and +somehow arrange to start a new one from a simpler image. +Something that can't be done from within Lisp, +making automation of it difficult, +which compounded with difficulty in configuration, +made the task quite hard. +Yet as we saw before, the task would have been required +to not have to live with the worst case or non-portable +subset of ASDF features. + +With ASDF 2, it is easy to upgrade +from ASDF 2 to later versions from within Lisp, +and not too hard to upgrade from ASDF 1 to ASDF 2 from within Lisp. +We support hot upgrade of ASDF and any breakage is a bug +that we will do our best to fix. +There are still limitations on upgrade, though, +most notably the fact that after you upgrade ASDF, +you must also reload or upgrade all ASDF extensions. + +@subsection Decoupled release cycle + +When vendors were releasing their Lisp implementations with ASDF, +they had to basically never change version +because neither upgrade nor downgrade was possible +without breaking something for someone, +and no obvious upgrade path was visible and recommendable. + +With ASDF 2, upgrade is possible, easy and can be recommended. +This means that vendors can safely ship a recent version of ASDF, +confident that if a user isn't fully satisfied, +he can easily upgrade ASDF and deal +with a supported recent version of it. +This means that release cycles will be causally decoupled, +the practical consequence of which will mean faster convergence +towards the latest version for everyone. + +@section Issues with installing the proper version of ASDF + +@subsection ``My Common Lisp implementation comes with an outdated version of ASDF. What to do?'' + +We recommend you upgrade ASDF. +@xref{Loading ASDF,,Upgrading ASDF}. + +If this does not work, it is a bug, and you should report it. +@xref{FAQ, report-bugs, Where do I report a bug}. +In the meantime, you can load @file{asdf.lisp} directly. +@xref{Loading ASDF,Loading an otherwise installed ASDF}. + + +@subsection ``I'm a Common Lisp implementation vendor. When and how should I upgrade ASDF?'' + +Starting with current candidate releases of ASDF 2, +it should always be a good time to upgrade to a recent version of ASDF. +You may consult with the maintainer for which specific version they recommend, +but the latest RELEASE should be correct. +We trust you to thoroughly test it with your implementation before you release it. +If there are any issues with the current release, +it's a bug that you should report upstream and that we will fix ASAP. + +As to how to include ASDF, we recommend that +if you do have a few magic systems in your implementation path, +that are specially treated in @code{wrapping-source-registry}, +like SBCL does. +In this case, we explicitly ask you to @emph{NOT} distribute +@file{asdf.asd} together with your implementation's ASDF, +least you separate it from the other systems in this path, +or otherwise rename the system and its @file{asd} file +to e.g. @code{asdf-sbcl} and @file{asdf-sbcl.asd}. + +If you do not have any such magic systems, or have other non-magic systems +that you want to bundle with your implementation, +then you may add them to the @code{default-source-registry}, +and you are welcome to include @file{asdf.asd} amongst them. + +Please send upstream any patches you make to ASDF itself, +so we can merge them back in for the benefit of your users +when they upgrade to the upstream version. + + +@section Issues with configuring ASDF + +@subsection ``How can I customize where fasl files are stored?'' + +@xref{Controlling where ASDF saves compiled files}. + +Note that in the past there was an add-on to ASDF called +@code{ASDF-binary-locations}, developed by Gary King. +That add-on has been merged into ASDF proper, +then superseded by the @code{asdf-output-translations} facility. + +Note that use of @code{asdf-output-translations} +can interfere with one aspect of your systems +--- if your system uses @code{*load-truename*} to find files +(e.g., if you have some data files stored with your program), +then the relocation that this ASDF customization performs +is likely to interfere. +Use @code{asdf:system-relative-pathname} to locate a file +in the source directory of some system, and +use @code{asdf:apply-output-translations} to locate a file +whose pathname has been translated by the facility. + +@subsection ``How can I wholly disable the compiler output cache?'' + +To permanently disable the compiler output cache +for all future runs of ASDF, you can: + +@example +mkdir -p ~/.config/common-lisp/asdf-output-translations.conf.d/ +echo ':disable-cache' > ~/.config/common-lisp/asdf-output-translations.conf.d/99-disable-cache.conf +@end example + +This assumes that you didn't otherwise configure the ASDF files +(if you did, edit them again), +and don't somehow override the configuration at runtime +with a shell variable (see below) or some other runtime command +(e.g. some call to @code{asdf:initialize-output-translations}). + +To disable the compiler output cache in Lisp processes +run by your current shell, try (assuming @code{bash} or @code{zsh}) +(on Unix and cygwin only): + +@example +export ASDF_OUTPUT_TRANSLATIONS=/: +@end example + +To disable the compiler output cache just in the current Lisp process, +use (after loading ASDF but before using it): + +@example +(asdf:disable-output-translations) +@end example + +@section Issues with using and extending ASDF to define systems + +@subsection ``How can I cater for unit-testing in my system?'' + +ASDF provides a predefined test operation, @code{test-op}. +@xref{Predefined operations of ASDF, test-op}. +The test operation, however, is largely left to the system definer to specify. +@code{test-op} has been +a topic of considerable discussion on the +@uref{http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel,asdf-devel mailing list}, +and on the +@uref{https://launchpad.net/asdf,launchpad bug-tracker}. + +Here are some guidelines: + +@itemize +@item +For a given system, @var{foo}, you will want to define a corresponding +test system, such as @var{foo-test}. The reason that you will want this +separate system is that ASDF does not out of the box supply components +that are conditionally loaded. So if you want to have source files +(with the test definitions) that will not be loaded except when testing, +they should be put elsewhere. + +@item +The @var{foo-test} system can be defined in an asd file of its own or +together with @var{foo}. An aesthetic preference against cluttering up +the filesystem with extra asd files should be balanced against the +question of whether one might want to directly load @var{foo-test}. +Typically one would not want to do this except in early stages of +debugging. + +@item +Record that testing is implemented by @var{foo-test}. For example: +@example +(defsystem @var{foo} + :in-order-to ((test-op (test-op @var{foo-test}))) + ....) + +(defsystem @var{foo-test} + :depends-on (@var{foo} @var{my-test-library} ...) + ....) +@end example +@end itemize + +This procedure will allow you to support users who do not wish to +install your test framework. + +One oddity of ASDF is that @code{operate} (@pxref{Operations,operate}) +does not return a value. So in current versions of ASDF there is no +reliable programmatic means of determining whether or not a set of tests +has passed, or which tests have failed. The user must simply read the +console output. This limitation has been the subject of much +discussion. + +@subsection ``How can I cater for documentation generation in my system?'' + +The ASDF developers are currently working to add a @code{doc-op} +to the set of predefined ASDF operations. +@xref{Predefined operations of ASDF}. +See also @url{https://bugs.launchpad.net/asdf/+bug/479470}. + + + +@subsection ``How can I maintain non-Lisp (e.g. C) source files?'' + +See @code{cffi}'s @code{cffi-grovel}. + +@anchor{report-bugs} + + +@subsection ``I want to put my module's files at the top level. How do I do this?'' + +By default, the files contained in an asdf module go +in a subdirectory with the same name as the module. +However, this can be overridden by adding a @code{:pathname ""} argument +to the module description. +For example, here is how it could be done +in the spatial-trees ASDF system definition for ASDF 2: + +@example +(asdf:defsystem :spatial-trees + :components + ((:module base + :pathname "" + :components + ((:file "package") + (:file "basedefs" :depends-on ("package")) + (:file "rectangles" :depends-on ("package")))) + (:module tree-impls + :depends-on (base) + :pathname "" + :components + ((:file "r-trees") + (:file "greene-trees" :depends-on ("r-trees")) + (:file "rstar-trees" :depends-on ("r-trees")) + (:file "rplus-trees" :depends-on ("r-trees")) + (:file "x-trees" :depends-on ("r-trees" "rstar-trees")))) + (:module viz + :depends-on (base) + :pathname "" + :components + ((:static-file "spatial-tree-viz.lisp"))) + (:module tests + :depends-on (base) + :pathname "" + :components + ((:static-file "spatial-tree-test.lisp"))) + (:static-file "LICENCE") + (:static-file "TODO"))) +@end example + +All of the files in the @code{tree-impls} module are at the top level, +instead of in a @file{tree-impls/} subdirectory. + +Note that the argument to @code{:pathname} can be either a pathname object or a string. +A pathname object can be constructed with the @file{#p"foo/bar/"} syntax, +but this is discouraged because the results of parsing a namestring are not portable. +A pathname can only be portably constructed with such syntax as +@code{#.(make-pathname :directory '(:relative "foo" "bar"))}, +and similarly the current directory can only be portably specified as +@code{#.(make-pathname :directory '(:relative))}. +However, as of ASDF 2, you can portably use a string to denote a pathname. +The string will be parsed as a @code{/}-separated path from the current directory, +such that the empty string @code{""} denotes the current directory, and +@code{"foo/bar"} (no trailing @code{/} required in the case of modules) +portably denotes the same subdirectory as above. +When files are specified, the last @code{/}-separated component is interpreted +either as the name component of a pathname +(if the component class specifies a pathname type), +or as a name component plus optional dot-separated type component +(if the component class doesn't specifies a pathname type). + + +@node TODO list, Inspiration, FAQ, Top +@comment node-name, next, previous, up +@chapter TODO list + +Here is an old list of things to do, +in addition to the bugs that are now tracked on launchpad: +@url{https://launchpad.net/asdf}. + +@section Outstanding spec questions, things to add + +** packaging systems + +*** manual page component? + +** style guide for .asd files + +You should either use keywords or be careful +with the package that you evaluate defsystem forms in. +Otherwise @code{(defsystem partition ...)} +being read in the @code{cl-user} package +will intern a @code{cl-user:partition} symbol, +which will then collide with the @code{partition:partition} symbol. + +Actually there's a hairier packages problem to think about too. +@code{in-order-to} is not a keyword: +if you read @code{defsystem} forms in a package that doesn't use ASDF, +odd things might happen. + + +** extending defsystem with new options + +You might not want to write a whole parser, +but just to add options to the existing syntax. +Reinstate @code{parse-option} or something akin. + + +** document all the error classes + +** what to do with compile-file failure + +Should check the primary return value from compile-file and see if +that gets us any closer to a sensible error handling strategy + +** foreign files + +lift unix-dso stuff from db-sockets + +** Diagnostics + +A ``dry run'' of an operation can be made with the following form: + +@lisp +(traverse (make-instance '<operation-name>) + (find-system <system-name>) + 'explain) +@end lisp + +This uses unexported symbols. +What would be a nice interface for this functionality? + +@section Missing bits in implementation + +** all of the above + +** reuse the same scratch package whenever a system is reloaded from disk + +** rules for system pathname defaulting are not yet implemented properly + +** proclamations probably aren't + +** when a system is reloaded with fewer components than it previously had, odd things happen + +We should do something inventive when processing a @code{defsystem} form, +like take the list of kids and @code{setf} the slot to @code{nil}, +then transfer children from old to new list as they're found. + +** traverse may become a normal function + +If you're defining methods on @code{traverse}, speak up. + + +** a lot of load-op methods can be rewritten to use input-files + +so should be. + + +** (stuff that might happen later) + +*** Propagation of the @code{:force} option. + +``I notice that + + @code{(asdf:compile-system :araneida :force t)} + +also forces compilation of every other system the @code{:araneida} system depends on. +This is rarely useful to me; +usually, when I want to force recompilation of something more than a single source file, +I want to recompile only one system. +So it would be more useful to have @code{make-sub-operation} +refuse to propagate @code{:force t} to other systems, and +propagate only something like @code{:force :recursively}. + +Ideally what we actually want is some kind of criterion that says +to which systems (and which operations) a @code{:force} switch will propagate. + +The problem is perhaps that ``force'' is a pretty meaningless concept. +How obvious is it that @code{load :force t} should force @emph{compilation}? +But we don't really have the right dependency setup +for the user to compile @code{:force t} and expect it to work +(files will not be loaded after compilation, so the compile +environment for subsequent files will be emptier than it needs to be) + +What does the user actually want to do when he forces? +Usually, for me, update for use with a new version of the Lisp compiler. +Perhaps for recovery when he suspects that something has gone wrong. +Or else when he's changed compilation options or configuration +in some way that's not reflected in the dependency graph. + +Other possible interface: have a ``revert'' function akin to @code{make clean}. + +@lisp +(asdf:revert 'asdf:compile-op 'araneida) +@end lisp + +would delete any files produced by @code{(compile-system :araneida)}. +Of course, it wouldn't be able to do much about stuff in the image itself. + +How would this work? + +@code{traverse} + +There's a difference between a module's dependencies (peers) +and its components (children). +Perhaps there's a similar difference in operations? +For example, @code{(load "use") depends-on (load "macros")} is a peer, +whereas @code{(load "use") depends-on (compile "use")} +is more of a ``subservient'' relationship. + +@node Inspiration, Concept Index, TODO list, Top +@comment node-name, next, previous, up +@chapter Inspiration + +@section mk-defsystem (defsystem-3.x) + +We aim to solve basically the same problems as @code{mk-defsystem} does. +However, our architecture for extensibility +better exploits CL language features (and is documented), +and we intend to be portable rather than just widely-ported. +No slight on the @code{mk-defsystem} authors and maintainers is intended here; +that implementation has the unenviable task +of supporting pre-ANSI implementations, which is no longer necessary. + +The surface defsystem syntax of asdf is more-or-less compatible with +@code{mk-defsystem}, except that we do not support +the @code{source-foo} and @code{binary-foo} prefixes +for separating source and binary files, and +we advise the removal of all options to specify pathnames. + +The @code{mk-defsystem} code for topologically sorting +a module's dependency list was very useful. + +@section defsystem-4 proposal + +Marco and Peter's proposal for defsystem 4 served as the driver for +many of the features in here. Notable differences are: + +@itemize +@item +We don't specify output files or output file extensions +as part of the system. + +If you want to find out what files an operation would create, +ask the operation. + +@item +We don't deal with CL packages + +If you want to compile in a particular package, use an @code{in-package} form +in that file (ilisp / SLIME will like you more if you do this anyway) + +@item +There is no proposal here that @code{defsystem} does version control. + +A system has a given version which can be used to check dependencies, +but that's all. +@end itemize + +The defsystem 4 proposal tends to look more at the external features, +whereas this one centres on a protocol for system introspection. + +@section kmp's ``The Description of Large Systems'', MIT AI Memu 801 + +Available in updated-for-CL form on the web at +@url{http://nhplace.com/kent/Papers/Large-Systems.html} + +In our implementation we borrow kmp's overall @code{PROCESS-OPTIONS} +and concept to deal with creating component trees +from @code{defsystem} surface syntax. +[ this is not true right now, though it used to be and +probably will be again soon ] + + +@c ------------------- + + +@node Concept Index, Function and Class Index, Inspiration, Top +@unnumbered Concept Index + +@printindex cp + +@node Function and Class Index, Variable Index, Concept Index, Top +@unnumbered Function and Class Index + +@printindex fn + +@node Variable Index, , Function and Class Index, Top +@unnumbered Variable Index + +@printindex vr + +@bye diff --git a/website/source/cclan.png b/doc/cclan.png similarity index 100% rename from website/source/cclan.png rename to doc/cclan.png diff --git a/website/source/favicon.ico b/doc/favicon.ico similarity index 100% rename from website/source/favicon.ico rename to doc/favicon.ico diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000000000000000000000000000000000000..a4de63ca636f00c8fee9a3a97fcb9ebe68adfeb3 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,130 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'> + <head> + <title>ASDF - Another System Definition Facility</title> + <link type='text/css' href='style.css' rel='stylesheet' /> + <meta name="author" content="the ASDF group"/> + </head> + <body> + <div class="header"> + <span class="logo"> + <a href="http://common-lisp.net/project/asdf/" title="ASDF Homepage"> + <img src="lisp-logo120x80.png" title="ASDF homepage" width="100" alt="Lisp Lizzard" /> + </a> + </span> + <h2>ASDF</h2><h4>Another System Definition Facility</h4> + </div> + <div class="system-links"> + <ul> + <li><a href="#what_it_is">What it is</a></li> + <li><a href="#documentation">Documentation</a></li> + <li><a href="#downloads">Getting it</a></li> + <li><a href="#bugs">Reporting Bugs</a></li> + <li><a href="#mailing-list">Mailing List</a></li> + <li><a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;a=log" + >Changelog</a></li> + </ul> + </div> + <div class="contents"> + <a id="what_it_is"></a> + <h3>What it is</h3> + <p>ASDF is a tool for describing how source files are organized: + what depends on which and when. + </p> + <p>It is roughly what Common-Lisp hackers use to build software + where C hackers would use say GNU Make. + </p> + <p>ASDF stands for <em>A</em>nother <em>S</em>ystem <em>D</em>efinition <em>F</em>acility, + in the continuity of the Lisp DEFSYSTEM of yore. + </p> + <a id="documentation"></a> + <h3>Documentation</h3> + <p>You can read our manual:</p> + <ul> + <li><a href="asdf.html">as one HTML file</a></li> + <li><a href="asdf/">split into one HTML file per section</a></li> + <li><a href="asdf.pdf">as a PDF document</a></li> + <li><a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;a=blob;f=asdf.texinfo">as texinfo source</a></li> + </ul> + + <a href="downloads"></a> + <h3>Getting it</h3> + <p>Though they may lag behind the version here, ASDF comes bundled with most Lisps. + To get the greatest and latest, you can: + </p> + <ul> + <li>download just the source for + <!--<a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;a=blob_plain;tag=RELEASE;f=asdf.lisp">asdf.lisp</a>,--> + <a href="http://common-lisp.net/project/asdf/asdf.lisp">asdf.lisp</a>, + </li> + <li>download the + <!--<a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;tag=RELEASE;a=snapshot;sf=tgz">tarball</a>,--> + <a href="http://common-lisp.net/project/asdf/asdf.tar.gz">tarball</a> + to get all bells and whistles, + </li> + <li>pull the latest from our git repository + (<a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git">browse</a>) + <pre>git clone http://common-lisp.net/project/asdf/asdf.git</pre> + </li></ul> + + <a id="bugs"> + <h3>Reporting Bugs</h3> + <p>To report bugs, you can use our + <a href="https://launchpad.net/asdf/">launchpad project</a>. + If you're unsure about the bug or want to discuss how to fix it, + you can send email to the project mailing-list below. + </p> + + <a id="mailing-list"></a> + <h3>Mailing List</h3> + <ul> + <li><a href="http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel">asdf-devel</a>: + A list for questions, patches, bug reports, and so on; It's for everything. + </li></ul> + + <h3>Contributing</h3> + <p>Join our mailing list, check the code out from git, + send questions, ideas and patches! + </p> + + <a id="news"></a> + <h3>What is happening</h3> + <dl> + <dt>December 2009 to April 2010</dt> + <dd>François-René Rideau is de facto maintainer, + with notable contributions from Robert P. Goldman. + Push towards an ASDF 2 release + with many clean-ups, better configurability + and updated documentation. + </dd> + <dt>May 2006 to November 2009</dt> + <dd>Gary King is de facto maintainer, + with notable contributions from + Robert P. Goldman, Nikodemus Siivola, Christophe Rhodes, D Herring. + Many small features and bug fixes, + trying to make the project more usable, + moving to using git and common-lisp.net. + </dd> + <dt>May 2004 to April 2006</dt> + <dd>Christophe Rhodes is de facto maintainer, + with notable contributions from + Nikodemus Siivola, Peter Van Eynde, Edi Weitz, Kevin Rosenberg. + The system made more robust, a few more features. + <dt>August 2001 to May 2004</dt> + <dd>Developed by Daniel Barlow, with notable contributions from + Christophe Rhodes, Kevin Rosenberg, Edi Weitz, Rahul Jain. + <dt>August 2001</dt> + <dd>Created by Daniel Barlow</dd> + </dl> + </div> + <div id="footer"> + <a class="nav" href="http://validator.w3.org/check/referer" title="xhtml1.1"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/xhtml.gif" width="80" height="15" title="valid xhtml button" alt="valid xhtml" /></a> + <a class="nav" href="http://www.catb.org/hacker-emblem/" title="hacker"> <img src="http://common-lisp.net/project/cl-containers/shared/buttons/hacker.png" width="80" height="15" title="hacker emblem" alt="hacker button" /></a> + <a class="nav" href="http://www.lisp.org/" title="Association of Lisp Users"> <img src="http://common-lisp.net/project/cl-containers/shared/buttons/lambda-lisp.png" width="80" height="15" title="ALU emblem" alt="ALU button" /></a> + <a class="nav" href="http://common-lisp.net/" title="Common-Lisp.net"> <img src="http://common-lisp.net/project/cl-containers/shared/buttons/lisp-lizard.png" width="80" height="15" title="Common-Lisp.net" alt="Common-Lisp.net button" /></a> + <p><span class="copyright"Copyright © 2001-2010 Daniel Barlow and contributors</span></p> + <p>ASDF has an <a href="http://www.opensource.org/licenses/mit-license.php">MIT style</a> license</p> + <div id="timestamp">Last updated 2010-04-08</div> + </div> + </body> +</html> diff --git a/doc/lisp-logo120x80.png b/doc/lisp-logo120x80.png new file mode 100644 index 0000000000000000000000000000000000000000..88403d6e27fa605a2a71d11d104f12ff878301f8 Binary files /dev/null and b/doc/lisp-logo120x80.png differ diff --git a/website/source/style.css b/doc/style.css similarity index 97% rename from website/source/style.css rename to doc/style.css index 96dcfcf17da5cbfba307020740a72f643e194d54..4ee51fc83e078cbb2601c2265518472b46478fc9 100644 --- a/website/source/style.css +++ b/doc/style.css @@ -244,10 +244,12 @@ a:hover { font-family: Arial, Helvetica, sans-serif; } -.system-description { - width: 75%; +.contents { + // width: 90%; padding-left: 2em; - float: left; + padding-right: 2em; + padding-top: 2em; + padding-bottom: 1.5em; } .check {font-size: x-small; diff --git a/test/asdf-pathname-test.script b/test/asdf-pathname-test.script new file mode 100644 index 0000000000000000000000000000000000000000..460ce9327c4e5c79374e4607df4ed535a2851f99 --- /dev/null +++ b/test/asdf-pathname-test.script @@ -0,0 +1,286 @@ +;;; -*- Lisp -*- +(load "script-support") +(load-asdf) + +;;; test asdf pathname specifications +;;; +;;; this is intended to run in a directory like '/test/20100310T000000Z/sbcl', in which it creates a +;;; test transcript. the tests construct a simple system/module/source-file system definition and +;;; verify that component pathname specifications refere to lisp source files in the directories +;;; '/test/{system1,system2}/' +;;; any failure is recorded as a transcript entry which indicates +;;; ( ( system-pathname module-pathname file-pathname ) missing-pathnames ) +;;; where missing pathnames is a list of the component-pathname values which failed to designate an intended file. +;;; of there, if the pathname is logical, both the logical and physical pathname appears. +;;; where NIL appears, the probe-file succeeded. +;;; +;;; the test creates the logical host "ASDFTEST" and the system "test-system". +;;; both are removed at the conclusion. +;;; +;;; 20100314: +;;; (:module "a/b/c.d") => (make-pathname :directory '(:relative "a" "b" "c.d")) +;;; (:file "a/b/c.d") => (make-pathname :directory '(:relative "a" "b") :name "c.d" :type "lisp")) +;;; (:static-file "a/b/c.d") => (make-pathname :directory '(:relative "a" "b") :name "c" :type "d")) +;;; (:module "/a/b/c.d") => (make-pathname :directory '(:absolute "a" "b" "c.d")) +;;; (:file "/a/b/c.d") => (make-pathname :directory '(:absolute "a" "b") :name "c.d" :type "lisp")) +;;; (:static-file "/a/b/c.d") => (make-pathname :directory '(:absolute "a" "b") :name "c" :type "d")) +;;; +;;; (:file "file2.lisp") means #p"file2.lisp.lisp" +;;; (:static-file "file2.lisp") means #p"file2.lisp" +;;; (:file "module1-1/file3.lisp") means #p"module1-1/file3.lisp.lisp" (assuming /) +;;; (:static-file "module1-1/file3.lisp") means #p"module1-1/file3.lisp" + +(defun test-component-pathnames (&key (root (asdf::pathname-directory-pathname *asdf-fasl*)) + (delete-host t) + (support-string-pathnames nil)) + (unwind-protect + (let* ((bin-type (pathname-type (compile-file-pathname "test.lisp"))) + (root-directory-namestring (format nil "~{/~a~}/" (rest (pathname-directory root)))) + (system-count 0) + (system-failures 0) + (file-count 0) + (file-failures 0) + (directory-count 0) + (directory-failures 0) + (*print-pretty* nil) + (start-time 0)) + + (with-open-file (result-stream (asdf::merge-pathnames* "output.txt" root) + :direction :output + :if-exists :supersede :if-does-not-exist :create) + (flet ((src-dir (&rest path) (append (pathname-directory root) (cons "asdf-src" path))) + (bin-dir (&rest path) (append (pathname-directory root) (cons "asdf-bin" path)))) + (setf (logical-pathname-translations "ASDFTEST") + `((,(format nil "**;*.~a" bin-type) + ,(make-pathname :directory (bin-dir :wild-inferiors) + :name :wild :type bin-type :version nil)) + (,(format nil "**;*.~a.*" bin-type) + ,(make-pathname :directory (bin-dir :wild-inferiors) + :name :wild :type bin-type)) + ("**;*.*" + ,(make-pathname :directory (src-dir :wild-inferiors) + :name :wild :type :wild :version nil)) + ("**;*.*.*" + ,(make-pathname :directory (src-dir :wild-inferiors) + :name :wild :type :wild)))) + + (let ((failures nil) + (systems `(,(make-pathname :directory (src-dir "system1") :name nil :type nil) + ,(make-pathname :host "ASDFTEST" :directory '(:absolute "system1")) + ,(parse-namestring "ASDFTEST:system1;") + ,@(when support-string-pathnames + `(,(format nil "~{/~a~}/asdf-src/system1" (rest (pathname-directory root))))))) + + (modules `(nil + ,(make-pathname :directory '(:relative) :name nil :type nil) + ,(make-pathname :directory '(:relative "module2") :name nil :type nil) + ,(make-pathname :directory '(:relative "module2" "module3") :name nil :type nil) + ,(make-pathname :directory (src-dir "system2" "module4") :name nil :type nil) + ,(make-pathname :host "ASDFTEST" :directory '(:absolute "system2" "module4") :name nil :type nil) + ,(parse-namestring "ASDFTEST:system2;module4;") + ,@(when support-string-pathnames + `("" + "module2" + "module2/" + "module2/module3" + "module2/module3/" + ,(concatenate 'string root-directory-namestring "asdf-src/system1/module1") + ,(concatenate 'string root-directory-namestring "asdf-src/system1/module1/") + ,(concatenate 'string root-directory-namestring "asdf-src/system1/module2/") + ,(concatenate 'string root-directory-namestring "asdf-src/system1/module2/module3/") + ,(concatenate 'string root-directory-namestring "asdf-src/system2/module4/"))))) + + (files `(nil + ,(make-pathname :directory '(:relative) :name "untyped-file" :type nil) + "file" + ,(make-pathname :directory '(:relative) :name "file" :type "lisp") + "typed-file.type" + ,(make-pathname :directory '(:relative "module2") :name "untyped-file" :type nil) + ,(make-pathname :directory '(:relative "module2") :name "file" :type "lisp") + ,(make-pathname :directory '(:relative "module2" "module3") :name "file" :type "lisp") + ,(make-pathname :directory (src-dir "system1" "module1") :name "untyped-file" :type nil) + ,(make-pathname :directory (src-dir "system1" "module1") :name "file" :type "lisp") + ,(make-pathname :directory (src-dir "system1" "module2") :name "untyped-file" :type nil) + ,(make-pathname :directory (src-dir "system1" "module2") :name "file" :type "lisp") + ,(make-pathname :directory (src-dir "system1" "module2" "module3") :name "file" :type "lisp") + ,(make-pathname :directory (src-dir "system2" "module4") :name "file" :type "lisp") + ,(make-pathname :host "ASDFTEST" :directory '(:absolute "system2" "module4") :name "file" :type "lisp") + ,(parse-namestring "ASDFTEST:system2;module4;file.lisp") + ,@(when support-string-pathnames + `(,(concatenate 'string root-directory-namestring "asdf-src/system1/module1/file.lisp"))))) + + (test-files (remove-duplicates + (sort (loop + ;; enumerate (system x module x file) pathname variations for relative + ;; file component names. no additions for the absolute specifications, + ;; as they should reiterate one of the relative names + for directory in (list (src-dir "system1") + (src-dir "system1" "module1") + (src-dir "system1" "module2") + (src-dir "system1" "module2" "module3") + (src-dir "system2" "module4")) + ;; :pathname #p"untyped-file" + collect (make-pathname :directory directory :name "untyped-file" :type nil) + ;; :file "file" + collect (make-pathname :directory directory :name "file" :type "lisp") ; for source files + collect (make-pathname :directory directory :name "file" :type nil) ; for static files + ;; :file "typed-file.type" + collect (make-pathname :directory directory :name "typed-file.type" :type "lisp") ; for source files + collect (make-pathname :directory directory :name "typed-file" :type "type") ; for static files for :pathname arg + ;; :static-file "static-file.type" + collect (make-pathname :directory directory :name "static-file" :type "type") + ;; :file "module2/file" + collect (make-pathname :directory directory :name "file" :type "lisp") + ;; :file "module2/typed-file.type" + collect (make-pathname :directory directory :name "typed-file.type" :type "lisp") ; for source files + ;;collect (make-pathname :directory directory :name "typed-file.type" :type nil) ; for static files ;; invalid as static file, unlike the below. + ;; :static-file "module2/static-file.type" + collect (make-pathname :directory directory :name "static-file" :type "type") + + ;;; source file pathname variations + collect (make-pathname :directory (append directory '("module2")) :name "untyped-file" :type nil) + collect (make-pathname :directory (append directory '("module2")) :name "file" :type "lisp") + collect (make-pathname :directory (append directory '("module2")) :name "typed-file.type" :type "lisp") + collect (make-pathname :directory (append directory '("module2")) :name "static-file" :type "type") + collect (make-pathname :directory (append directory '("module2" "module3")) :name "file" :type "lisp") + collect (make-pathname :directory (append directory '("module2" "module3")) :name "file" :type "lisp")) + #'string-lessp + ;; generate an alternative key in case namestring fails on a name w/ a dot + :key #'(lambda (p) (format nil "~{~a~^.~}~@[.~a~]~@[.~a~]" (rest (pathname-directory p)) (pathname-name p) (pathname-type p)))) + :test #'equalp :from-end t))) + + (dolist (file test-files) + (ensure-directories-exist file) + (with-open-file (stream file :direction :output :if-exists :supersede :if-does-not-exist :create) stream)) + + (multiple-value-bind (second minute hour day month year) (decode-universal-time (setf start-time (get-universal-time)) 0) + (let ((header (format nil "~4,'0d~2,'0d~2,'0dT~2,'0d~2,'0d~2,'0dZ : ~a ~a ~a" + year month day hour minute second + (lisp-implementation-type) + (lisp-implementation-version) + asdf::*asdf-version*))) + (format result-stream ";;; ~a~%~%" header) + (format *trace-output* "~%;;; ~a~%~%" header))) + (sleep 1) + + (dolist (system-pathname systems) + (dolist (module-pathname modules) + (dolist (file-pathname files) + (let ((configuration (list system-pathname module-pathname file-pathname)) + (system-definition `(asdf:defsystem :system1 :pathname ,system-pathname + :components ((:module :module1 :pathname ,module-pathname + :components ((:file "file" :pathname ,file-pathname) + (:file "module2/file" :pathname ,file-pathname) + ,@(unless (or (typep system-pathname 'logical-pathname) + (typep module-pathname 'logical-pathname)) + `((:file "typed-file.type" :pathname ,file-pathname) + (:static-file "static-file.type" :pathname ,file-pathname) + (:file "module2/typed-file.type" :pathname ,file-pathname) + (:static-file "module2/static-file.type" :pathname ,file-pathname) + (:static-file ,(concatenate 'string root-directory-namestring + "asdf-src/system1/module1/file.lisp") + :pathname ,file-pathname))))))))) + + (block :test-system + (handler-bind + ((error (lambda (c) + (incf system-failures) + (format *error-output* "~&error! ~a~%sysdef:~% ~W~%" c system-definition) + #+sbcl (sb-debug:backtrace 69) + #+clozure (ccl:print-call-history :count 69 :start-frame-number 1) + #+clisp (system::print-backtrace) + (format result-stream "~&~%***~%error: ~a~%~:w" + c system-definition) + (return-from :test-system)))) + (unless (and (or (typep system-pathname 'logical-pathname) + (typep module-pathname 'logical-pathname)) + (and (stringp file-pathname) (find #\. file-pathname))) + (incf system-count) + (let* ((system (eval system-definition)) + (module (first (asdf:module-components system))) + (file-components (asdf:module-components module))) + (incf file-count (length file-components)) + (incf directory-count 2) + (labels ((translate-if-needed (pathname) + (if (typep pathname 'logical-pathname) + (cons (translate-logical-pathname pathname) pathname) + pathname)) + (test-module (module) + (incf directory-count) + (unless #-clisp (probe-file (asdf:component-pathname module)) + #+clisp (ext:probe-directory (asdf:component-pathname module)) + (incf directory-failures) + (push (list (type-of module) (asdf:component-name module) + (translate-if-needed (asdf:component-pathname module)) + configuration + (list (when (asdf:component-parent module) (asdf:component-pathname (asdf:component-parent module))))) + failures))) + (test-file (file) + (incf file-count) + (unless (ignore-errors + (with-open-file (stream (asdf:component-pathname file) :direction :output :if-exists :supersede :if-does-not-exist :error) + (print start-time stream))) + (incf file-failures) + (push (list (type-of file) (asdf:component-name file) + (translate-if-needed (asdf:component-pathname file)) + configuration + (list (asdf:component-pathname (asdf:component-system file)) + (asdf:component-pathname (asdf:component-parent file)))) + failures)))) + (test-module system) + (test-module module) + (dolist (file file-components) (test-file file))))))))))) + + (format result-stream "~% target files [~s]~:{~% ~s -> ~s~}~%" + (length test-files) + (mapcar #'(lambda (file) + (list file (if (probe-file file) + (if (> (file-write-date file) start-time) + :ok + :untouched) + :missing))) + test-files)) + + (format result-stream "~&~%~% translations: ~a: ~:w" "ASDFTEST" (logical-pathname-translations "ASDFTEST")) + + (format result-stream "~&~%~% variations:~% systems: ~:w~% modules: ~:w~% files: ~:w" + systems modules files) + + (let ((homogeneous-failures 0) (*print-length* nil)) + (format result-stream "~&~%~% pathname failures [~s]:" (length failures)) + (dolist (failure failures) + (destructuring-bind (type name intended-pathname configuration parent-pathnames) failure + (format result-stream "~&~%~:[ ~;!~]~a~24T~s~% missing:~24T~s~% configuration:~24T~s~% parent pathnames:~24T~s" + (flet ((logical-p (p) (typep p 'logical-pathname))) + (when (or (every #'logical-p configuration) (notany #'logical-p configuration)) + (incf homogeneous-failures))) + type name intended-pathname configuration parent-pathnames))) + (terpri result-stream) + (print (print `(:result :type ,(lisp-implementation-type) :version ,(lisp-implementation-version) + :file ,(pathname result-stream) + :system-failures (,system-failures ,system-count) + :directory-failures (,directory-failures ,directory-count) + :file-failures (,file-failures ,file-count) + :homogeneous ,homogeneous-failures) + result-stream) + *trace-output*) + (terpri *trace-output*) + (force-output *trace-output*) + (and (zerop system-failures) (zerop directory-failures) + (zerop file-failures) (zerop homogeneous-failures))))))) + (when delete-host + (setf (logical-pathname-translations "ASDFTEST") nil)) + (remhash "test-system" asdf::*defined-systems*))) + +(quit-on-error + (asdf:initialize-source-registry) + (format t "source registry: ~S~%" (asdf::source-registry)) + (asdf:initialize-output-translations) + (format t "output translations: ~S~%" (asdf::output-translations)) + + (or (test-component-pathnames :delete-host t :support-string-pathnames nil) + (leave-lisp "test failed" 1))) + +;;; (load "LIBRARY:de;setf;utility;asdf;cp-test.lisp") +;;; (logical-pathname-translations "ASDFTEST") +;;; (gethash "test-system" asdf::*defined-systems*) diff --git a/test/compile-asdf.lisp b/test/compile-asdf.lisp index dc171c2c95e2945fb0e58066843a9fcf44a7d40b..3285c7c2ebb74a63817fb81604c078a9735285df 100644 --- a/test/compile-asdf.lisp +++ b/test/compile-asdf.lisp @@ -1,26 +1,36 @@ (in-package #:common-lisp-user) -(load (merge-pathnames "script-support" *load-pathname*)) +(load (make-pathname :name "script-support" :defaults *load-pathname*)) -(cond ((probe-file *asdf-lisp*) - (ensure-directories-exist *asdf-fasl*) - (multiple-value-bind (result warnings-p errors-p) - ;; style warnings shouldn't abort the compilation [2010/02/03:rpg] - (handler-bind ((style-warning - #'(lambda (w) - (princ w *error-output*) - (muffle-warning w)))) - (compile-file *asdf-lisp* - :output-file *asdf-fasl*)) - (declare (ignore result)) - (cond (warnings-p - ;;; ECL gives warnings that it shouldn't! - #+ecl (leave-lisp "ASDF compiled with warnings. Please fix ECL." 0) - #-ecl - (leave-lisp "Testsuite failed: ASDF compiled with warnings" 1)) - (errors-p - (leave-lisp "Testsuite failed: ASDF compiled with ERRORS" 2)) - (t - (leave-lisp "ASDF compiled cleanly" 0))))) - (t - (leave-lisp "Testsuite failed: unable to find ASDF source" 3))) +(cond + ((not (probe-file *asdf-lisp*)) + (leave-lisp "Testsuite failed: unable to find ASDF source" 3)) + ((and (probe-file *asdf-fasl*) + (> (file-write-date *asdf-fasl*) (file-write-date *asdf-lisp*)) + (ignore-errors (load *asdf-fasl*))) + (leave-lisp "Reusing previously-compiled ASDF" 0)) + (t + (let ((tmp (make-pathname :name "asdf-tmp" :defaults *asdf-fasl*))) + (ensure-directories-exist *asdf-fasl*) + (multiple-value-bind (result warnings-p errors-p) + ;; style warnings shouldn't abort the compilation [2010/02/03:rpg] + (handler-bind ((style-warning + #'(lambda (w) + (princ w *error-output*) + (muffle-warning w)))) + (compile-file *asdf-lisp* :output-file tmp)) + (declare (ignore result)) + (cond + #-ecl + (warnings-p + (leave-lisp "Testsuite failed: ASDF compiled with warnings" 1)) + (errors-p + (leave-lisp "Testsuite failed: ASDF compiled with ERRORS" 2)) + (t + #+ecl + (when warnings-p + (format t "~&ASDF compiled with warnings. Please fix ECL.~%")) + (when (probe-file *asdf-fasl*) + (delete-file *asdf-fasl*)) + (rename-file tmp *asdf-fasl*) + (leave-lisp "ASDF compiled cleanly" 0))))))) diff --git a/test/run-tests.sh b/test/run-tests.sh index a170ec7b34155e0e05da711e3a2d835304354616..da2ba6821e18db1695cc061a7574e33c7459b958 100755 --- a/test/run-tests.sh +++ b/test/run-tests.sh @@ -5,7 +5,18 @@ # - quit with exit status 0 on getting eof # - quit with exit status >0 if an unhandled error occurs -export CL_SOURCE_REGISTRY="$PWD" +usage () { + echo "$0 [lisp invocation] [scripts-regex]" + echo " - read lisp forms one at a time from matching scripts" + echo " - quit with exit status 0 on getting eof" + echo " - quit with exit status >0 if an unhandled error occurs" + echo " you need to supply the .script in the second argument" + echo " lisps include sbcl, clisp, allegro and allegromodern" + echo "OPTIONS:" + echo " -d -- debug mode" + echo " -u -h -- show this message." +} + unset DEBUG_ASDF_TEST while getopts "duh" OPTION @@ -39,24 +50,16 @@ fi sok=1 -usage () { - echo "$0 [lisp invocation] [scripts-regex]" - echo " - read lisp forms one at a time from matching scripts" - echo " - quit with exit status 0 on getting eof" - echo " - quit with exit status >0 if an unhandled error occurs" - echo " you need to supply the .script in the second argument" - echo " lisps include sbcl, clisp, allegro and allegromodern" - echo "OPTIONS:" - echo " -d -- debug mode" - echo " -u -h -- show this message." -} +DO () { ( set -x ; "$@" ); } do_tests() { - command=$1 eval=$2 fasl_ext=$3 - rm -f *.$fasl_ext ~/.cache/common-lisp/"`pwd`"/*.$fasl_ext || true - ( cd .. && $command $eval '(load "test/compile-asdf.lisp")' ) - if [ $? -eq 0 ] ; then - echo "Compiled OK" + command="$1" eval="$2" + rm -f ~/.cache/common-lisp/"`pwd`"/* || true + ( cd .. && DO $command $eval '(load "test/compile-asdf.lisp")' ) + if [ $? -ne 0 ] ; then + echo "Compilation FAILED" >&2 + else + echo "Compiled OK" >&2 test_count=0 test_pass=0 test_fail=0 @@ -65,8 +68,8 @@ do_tests() { do echo "Testing: $i" >&2 test_count=`expr "$test_count" + 1` - rm -f *.$fasl_ext ~/.cache/common-lisp/"`pwd`"/*.$fasl_ext || true - if $command $eval "(load \"$i\")" ; then + rm -f ~/.cache/common-lisp/"`pwd`"/* || true + if DO $command $eval "(load \"$i\")" ; then echo "Using $command, $i passed" >&2 test_pass=`expr "$test_pass" + 1` else @@ -99,78 +102,81 @@ if [ -z $1 ] ; then lisp="sbcl" fi +command= flags= nodebug= eval= case "$lisp" in sbcl) - if type sbcl ; then - fasl_ext="fasl" - command="sbcl --noinform --userinit /dev/null --sysinit /dev/null" - nodebug="--disable-debugger" - eval="--eval" - fi ;; + command="${SBCL:-sbcl}" + flags="--noinform --userinit /dev/null --sysinit /dev/null" + nodebug="--disable-debugger" + eval="--eval" ;; clisp) - if type clisp ; then - fasl_ext="fas" - command=`which clisp` - command="$command -norc -ansi -I " - nodebug="-on-error exit" - eval="-x" - fi ;; + command="${CLISP:-clisp}" + flags="-norc -ansi -I " + nodebug="-on-error exit" + eval="-x" ;; allegro) - if type alisp ; then - fasl_ext="fasl" - command="alisp -q " - nodebug="-batch" - eval="-e" - fi ;; + command="${ALLEGRO:-alisp}" + flags="-q" + nodebug="-batch" + eval="-e" ;; allegromodern) - if type mlisp ; then - fasl_ext="fasl" - command="mlisp -q" - nodebug="-batch" - eval="-e" - fi ;; + command="${ALLEGROMODERN:-mlisp}" + flags="-q" + nodebug="-batch" + eval="-e" ;; ccl) - if type ccl ; then - case `uname -s` in - Linux) fasl_os=lx ;; - Darwin) fasl_os=dx ;; - esac - case `uname -m` in - x86_64|ppc64) fasl_bits=64 ;; - i?86|ppc) fasl_bits=32 ;; - esac - fasl_ext="${fasl_os}${fasl_bits}fsl" - command="ccl --no-init --quiet" - nodebug="--batch" - eval="--eval" - fi ;; + command="${CCL:-ccl}" + flags="--no-init --quiet" + nodebug="--batch" + eval="--eval" ;; cmucl) - if type lisp ; then - fasl_ext="x86f" - command="lisp -noinit" - nodebug="-batch" - eval="-eval" - fi ;; + command="${CMUCL:-lisp}" + flags="-noinit" + nodebug="-batch" + eval="-eval" ;; ecl) - if type ecl ; then - fasl_ext="fas" - command=`which ecl` - command="$command -norc" - eval="-eval" - fi ;; + command="${ECL:-ecl}" + flags="-norc" + eval="-eval" ;; lispworks) - if type lispworks ; then - fasl_ext="ofasl" - command=`which ecl` - command="$command -siteinit - -init -" - eval="-eval" - fi ;; + command="${LISPWORKS:-lispworks}" + # If you have a licensed copy of lispworks, + # you can obtain the "lispworks" binary with, e.g. + # echo '(hcl:save-image "/lispworks" :environment nil)' > /tmp/build.lisp ; + # ./lispworks-6-0-0-x86-linux -siteinit - -init - -build /tmp/build.lisp + flags="-siteinit - -init -" + eval="-eval" ;; + gclcvs) + export GCL_ANSI=t + command="${GCL:-gclcvs}" + flags="-batch" + eval="-eval" ;; + abcl) + command="${ABCL:-abcl}" + flags="--noinit --noinform" + eval="--eval" ;; + *) + echo "Unsupported lisp: $1" >&2 + echo "Please add support to run-tests.sh" >&2 + exit 42 ;; esac +if ! type "$command" ; then + echo "lisp implementation not found: $command" >&2 + exit 43 +fi + +ASDFDIR="$(cd .. ; /bin/pwd)" +export CL_SOURCE_REGISTRY="${ASDFDIR}" +export ASDF_OUTPUT_TRANSLATIONS="(:output-translations (\"${ASDFDIR}\" (\"${ASDFDIR}/tmp/fasls\" :implementation)) :ignore-inherited-configuration)" +env | grep asdf + +command="$command $flags" if [ -z "${DEBUG_ASDF_TEST}" ] ; then command="$command $nodebug" fi + create_config () { mkdir -p ../tmp/test-source-registry-conf.d ../tmp/test-asdf-output-translations-conf.d } @@ -183,10 +189,9 @@ if [ -z "$command" ] ; then echo "Error: cannot find or do not know how to run Lisp named $lisp" else create_config - mkdir -p results - echo $command + mkdir -p ../tmp/results thedate=`date "+%Y-%m-%d"` - do_tests "$command" "$eval" "$fasl_ext" 2>&1 | \ - tee "results/${lisp}.text" "results/${lisp}-${thedate}.save" + do_tests "$command" "$eval" 2>&1 | \ + tee "../tmp/results/${lisp}.text" "../tmp/results/${lisp}-${thedate}.save" clean_up fi diff --git a/test/script-support.lisp b/test/script-support.lisp index 37c63eb8684e2ebaaf865cdbac11f5ae2126ab2a..edecd3be3bb1b6b452a8e3b6f8a214dff744a906 100644 --- a/test/script-support.lisp +++ b/test/script-support.lisp @@ -1,8 +1,41 @@ (in-package #:common-lisp-user) -(defvar *asdf-lisp* (truename (merge-pathnames "../asdf.lisp" *load-truename*))) -(defvar *asdf-fasl* (compile-file-pathname (merge-pathnames "tmp/" *asdf-lisp*))) -(defun load-asdf () (load *asdf-fasl*)) +;; We can't use asdf:merge-pathnames* because ASDF isn't loaded yet. +;; We still want to work despite and host/device funkiness. +(defparameter *test-directory* + (make-pathname :name nil :type nil :version nil + :defaults (or *load-truename* *compile-file-truename*))) +(defparameter *asdf-directory* + (merge-pathnames + (make-pathname :directory '(:relative :back) :defaults *test-directory*) + *test-directory*)) +(defparameter *asdf-lisp* + (make-pathname :name "asdf" :type "lisp" :defaults *asdf-directory*)) +(defparameter *asdf-fasl* + (compile-file-pathname + (let ((impl (string-downcase + (or #+allegro + (ecase excl:*current-case-mode* + (:case-sensitive-lower :mlisp) + (:case-insensitive-upper :alisp)) + #+armedbear :abcl + #+clisp :clisp + #+clozure :ccl + #+cmu :cmucl + #+corman :cormanlisp + #+digitool :mcl + #+ecl :ecl + #+gcl :gcl + #+lispworks :lispworks + #+sbcl :sbcl + #+scl scl)))) + (merge-pathnames + (make-pathname :directory `(:relative "tmp" "fasls" ,impl) + :defaults *asdf-directory*) + *asdf-lisp*)))) + +(defun load-asdf () + (load *asdf-fasl*)) #+allegro (setf excl:*warn-on-nested-reader-conditionals* nil) @@ -11,23 +44,28 @@ ;; http://www.cliki.net/cl-launch (defun leave-lisp (message return) (when message - (format *error-output* message)) + (format *error-output* message) + (terpri *error-output*)) + (finish-output *error-output*) + (finish-output *standard-output*) #+allegro (excl:exit return) #+clisp (ext:quit return) #+(or cmu scl) - (unix:unix-exit code) + (unix:unix-exit return) #+ecl (si:quit return) #+gcl - (lisp:quit code) + (lisp:quit return) #+lispworks - (lispworks:quit :status code :confirm nil :return nil :ignore-errors-p t) + (lispworks:quit :status return :confirm nil :return nil :ignore-errors-p t) #+(or openmcl mcl) (ccl::quit return) #+sbcl (sb-ext:quit :unix-status return) + #+abcl + (ext:quit :status return) (error "Don't know how to quit Lisp; wanting to use exit code ~a" return)) @@ -41,8 +79,15 @@ is bound, write a message and exit on an error. If (handler-bind ((error (lambda (c) (format *error-output* "~a" c) - (if (ignore-errors (funcall (find-symbol "GETENV" :asdf) "DEBUG_ASDF_TEST")) - (break) - (leave-lisp "~&Script failed~%" 1))))) + (cond + ((ignore-errors (funcall (find-symbol "GETENV" :asdf) "DEBUG_ASDF_TEST")) + (break)) + (t + (format *error-output* "ABORTING:~% ~S~%" c) + #+sbcl (sb-debug:backtrace 69) + #+clozure (ccl:print-call-history :count 69 :start-frame-number 1) + #+clisp (system::print-backtrace) + (format *error-output* "ABORTING:~% ~S~%" c) + (leave-lisp "~&Script failed~%" 1)))))) (funcall thunk) (leave-lisp "~&Script succeeded~%" 0))) diff --git a/test/test-configuration.script b/test/test-configuration.script new file mode 100644 index 0000000000000000000000000000000000000000..9b6406cf3e5cc99908d05057b5c12ec49406e44e --- /dev/null +++ b/test/test-configuration.script @@ -0,0 +1,78 @@ +;;; -*- Lisp -*- +(load "script-support") +(load-asdf) +(in-package :asdf) + +(defparameter *test-directory* + (merge-pathnames* + (make-pathname :directory '(:relative "tmp")) + cl-user::*asdf-directory*)) + +(defun under-test-directory (designator &optional (defaults *test-directory*)) + (namestring (merge-pathnames* (merge-component-name-type designator) defaults))) + +(defun create-conf-files (&optional (path *test-directory*)) + (let ((v `(("conf.d/conf1.conf" + ((:directory ,(under-test-directory "dir1/")))) + ("conf.d/conf2.conf" + ((:tree ,(under-test-directory "dir2/"))))))) + (loop + :for (file contents) :in v + :for name = (under-test-directory file path) + :do + (ensure-directories-exist name) + (with-open-file (out name + :direction :output + :if-exists :supersede) + (with-standard-io-syntax + (format out "~{~S~}" contents)))))) + +(defparameter *test-asd-directories* + (loop + :for dir + :in '("dir1/" "dir2/dir3/" "dir2/dir4/" "dir2/") + :collect (under-test-directory dir))) + +(defun create-asd-files () + (loop + :for d :in *test-asd-directories* + :for i :from 0 :do + (ensure-directories-exist d) + (with-open-file (s (merge-pathnames* (format nil "foo~D.asd" i) d) + :direction :output + :if-exists :rename-and-delete + :if-does-not-exist :create) + (format s "(asdf:defsystem :foo~D)~%" i)))) + +(cl-user::quit-on-error + +(assert + (equal (parse-output-translations-string "/foo:/bar::/baz:/quux") + '(:output-translations + ("/foo" "/bar") + :inherit-configuration + ("/baz" "/quux")))) +(assert + (equal (parse-output-translations-string "/:") + '(:output-translations ("/" nil) :ignore-inherited-configuration))) +(assert + (equal (parse-output-translations-string "/::") + '(:output-translations ("/" nil) :inherit-configuration))) +(assert + (equal (parse-output-translations-string "/:/") + '(:output-translations ("/" "/") :ignore-inherited-configuration))) + +(assert + (equal (parse-output-translations-string + "(:output-translations (\"/\" \"/\") :ignore-inherited-configuration)") + '(:output-translations ("/" "/") :ignore-inherited-configuration))) + +(create-asd-files) +(create-conf-files) +(initialize-source-registry + `(:source-registry (:include ,(under-test-directory "conf.d/")) + :ignore-inherited-configuration)) +(assert + (equal (namestring (system-relative-pathname :foo3 "bar/baz.lisp")) + (under-test-directory "dir2/bar/baz.lisp"))) +) diff --git a/test/test-missing-lisp-file.asd b/test/test-missing-lisp-file.asd new file mode 100644 index 0000000000000000000000000000000000000000..5c32bb8f7196b33526f118cf619b8c7bd5be3567 --- /dev/null +++ b/test/test-missing-lisp-file.asd @@ -0,0 +1,5 @@ +;;; -*- Lisp -*- +(asdf:defsystem test-missing-lisp-file + :components ((:file "file2" :in-order-to ((compile-op (load-op "fileMissing")))) + (:file "fileMissing"))) + diff --git a/test/test-missing-lisp-file.script b/test/test-missing-lisp-file.script new file mode 100644 index 0000000000000000000000000000000000000000..acb4580cd71e263c4cf3222d8b16d4e86144a50f --- /dev/null +++ b/test/test-missing-lisp-file.script @@ -0,0 +1,49 @@ +;;; -*- Lisp -*- + +;;;--------------------------------------------------------------------------- +;;; This is supposed to verify that if a lisp file is lost, then any attempt to +;;; make the system will fail. I.e., we verify that we won't just load a stale +;;; fasl when the source file is lost. +;;;--------------------------------------------------------------------------- + +(load "script-support") +(load-asdf) + +(quit-on-error + (setf asdf:*central-registry* '(*default-pathname-defaults*)) + (let ((missing-name (namestring + (make-pathname :name "fileMissing" + :type "lisp" + :defaults + *default-pathname-defaults*))) + (template-file (namestring + (make-pathname :name "file1" + :type "lisp" + :defaults + *default-pathname-defaults*)))) + (asdf::run-shell-command + (format nil "cp ~a ~a" template-file missing-name)) + (unless (probe-file missing-name) + (format t "File copy failed.~%")) + (asdf:operate 'asdf:load-op 'test-missing-lisp-file) + ;; test that it compiled + (let* ((file1 (asdf:compile-file-pathname* "file2")) + (file2 (asdf:compile-file-pathname* "fileMissing")) + (file1-date (file-write-date file1))) + + (assert file1-date) + (assert (file-write-date file2)) + + ;; and loaded + (assert (symbol-value (find-symbol (symbol-name :*file1*) :test-package))) + + ;; now remove the lisp file we created, and wait for an error + (sleep 1) ; mtime has 1-second granularity, so pause here for fast machines + + (asdf::run-shell-command (format nil "rm -f ~A" missing-name)) + ;; we shouldn't be able to find the input-file for the compile-op, and that + ;; should be an error. + (multiple-value-bind (retval err) + (ignore-errors + (asdf:operate 'asdf:load-op 'test-missing-lisp-file)) + (assert err))))) diff --git a/test/test-module-depend.script b/test/test-module-depend.script index 4a810369f9167fccd1d2df41279359120b939f6f..ff6296a6bae2c17f4eabf2d9d52d570234f7cd3c 100644 --- a/test/test-module-depend.script +++ b/test/test-module-depend.script @@ -7,25 +7,21 @@ (asdf:operate 'asdf:load-op 'test-module-depend) ;; test that it compiled - (defvar file1-date (file-write-date (asdf:compile-file-pathname* "file1"))) + (let ((file1-date (file-write-date (asdf:compile-file-pathname* "file1")))) - (assert (and file1-date (file-write-date (asdf:compile-file-pathname* "file2")))) + (assert (and file1-date (file-write-date (asdf:compile-file-pathname* "file2")))) - ;; and loaded - (assert (eval (intern (symbol-name '#:*file1*) :test-package))) - - - ;; now touch file1 and check that file2 _is_ also recompiled - ;; this will only work if the cross-module (intra-system) - ;; dependency bug is fixed. - - (let ((before (file-write-date (asdf:compile-file-pathname* "file2")))) - (asdf::run-shell-command "touch file1.lisp") - (sleep 1) - (asdf:operate 'asdf:load-op 'test-module-depend) - (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) - ;; does this properly go to the second level? - (assert (> (file-write-date (asdf:compile-file-pathname* "file3")) before)) - )) + ;; and loaded + (assert (eval (intern (symbol-name '#:*file1*) :test-package))) + ;; now touch file1 and check that file2 _is_ also recompiled + ;; this will only work if the cross-module (intra-system) + ;; dependency bug is fixed. + (let ((before (file-write-date (asdf:compile-file-pathname* "file2")))) + (sleep 1) + (asdf::run-shell-command "touch file1.lisp") + (asdf:operate 'asdf:load-op 'test-module-depend) + (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) + ;; does this properly go to the second level? + (assert (> (file-write-date (asdf:compile-file-pathname* "file3")) before))))) diff --git a/test/test-module-excessive-depend.asd b/test/test-module-excessive-depend.asd index c7575cd225652ac6f953866960ecfc7fc0298d46..a8d8495c22e28be6ba9ec7272fc4db4715a10166 100644 --- a/test/test-module-excessive-depend.asd +++ b/test/test-module-excessive-depend.asd @@ -10,14 +10,14 @@ :depends-on ("file1") :components ((:file "file2"))))) -(defun find-file2 () - (find-component (find-quux) "file2")) - (defun find-quux () (find-component (find-system :test-module-excessive-depend) "quux")) +(defun find-file2 () + (find-component (find-quux) "file2")) + (defmethod component-depends-on ((op load-op) (c (eql (find-file2)))) (cons (cons 'load-op (list "file3-only")) @@ -31,9 +31,7 @@ (defmethod find-component :around ((m (eql (find-quux))) (c string) &optional version) "FIND-COMPONENT on a component is a no-op --- it's already found." + (declare (ignore version)) (if (string-equal c "file3-only") (asdf:find-system c) (call-next-method))) - - - diff --git a/test/test-module-excessive-depend.script b/test/test-module-excessive-depend.script index b06a561ef946b9b4f648781eb7368a361d0fd5f6..21827890f38cb217d109d2e46ec556191c1c9534 100644 --- a/test/test-module-excessive-depend.script +++ b/test/test-module-excessive-depend.script @@ -20,46 +20,43 @@ (asdf:operate 'asdf:load-op 'test-module-excessive-depend) ;; test that it compiled - (defvar file1-date (file-write-date (asdf:compile-file-pathname* "file1"))) - (defvar file2-date (file-write-date (asdf:compile-file-pathname* "file2"))) - (defvar file3-date (file-write-date (asdf:compile-file-pathname* "file3"))) - - (unless (and file1-date file2-date file3-date) - (error "Failed to compile one of the three files that should be compiled for this test: ~{~a~}" - (mapcar #'cdr - (remove-if #'car - (pairlis (list file1-date file2-date file3-date) - '("file1" "file2" "file3")))))) - - ;; and loaded - (assert (eval (intern (symbol-name '#:*file1*) :test-package))) - (assert (eval (intern (symbol-name '#:*file3*) :test-package))) - - - ;; now touch file1 and check that file2 _is_ also recompiled - ;; but that file3 is _not_ recompiled. - ;; this will only work if the cross-module (intra-system) - ;; dependency bug is fixed and the excessive compilation bug is fixed. - - (let ((before file2-date)) - (asdf::run-shell-command "touch file1.lisp") - (sleep 1) - (let ((plan (asdf::traverse - (make-instance 'asdf:load-op) - (asdf:find-system 'test-module-excessive-depend))) - (file3-only (asdf:find-system 'file3-only))) + (let ((file1-date (file-write-date (asdf:compile-file-pathname* "file1"))) + (file2-date (file-write-date (asdf:compile-file-pathname* "file2"))) + (file3-date (file-write-date (asdf:compile-file-pathname* "file3")))) + (unless (and file1-date file2-date file3-date) + (error "Failed to compile one of the three files ~ + that should be compiled for this test: ~{~a~}" + (mapcar #'cdr + (remove-if #'car + (pairlis (list file1-date file2-date file3-date) + '("file1" "file2" "file3")))))) + + ;; and loaded + (assert (eval (intern (symbol-name '#:*file1*) :test-package))) + (assert (eval (intern (symbol-name '#:*file3*) :test-package))) + + + ;; now touch file1 and check that file2 _is_ also recompiled + ;; but that file3 is _not_ recompiled. + ;; this will only work if the cross-module (intra-system) + ;; dependency bug is fixed and the excessive compilation bug is fixed. + + (let ((before file2-date)) + (sleep 1) + (asdf::run-shell-command "touch file1.lisp") + (let ((plan (asdf::traverse + (make-instance 'asdf:load-op) + (asdf:find-system 'test-module-excessive-depend))) + (file3-only (asdf:find-system 'file3-only))) ;;; (format t "~%Operation plan is:~%") ;;; (pprint plan) ;;; (format t "Target system is: ~a" file3-only) - (when (find file3-only plan :key #'cdr) - (error "Excessive operations on file3-only system. Bad propagation of dependencies."))) - (asdf:operate 'asdf:load-op 'test-module-excessive-depend) - (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) - (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) - ) - (unless (= (file-write-date (asdf:compile-file-pathname* "file3")) - file3-date) - (error "Excessive compilation of file3.lisp: traverse bug.")) - ) - - + (when (find file3-only plan :key #'cdr) + (error "Excessive operations on file3-only system. Bad propagation of dependencies."))) + (asdf:operate 'asdf:load-op 'test-module-excessive-depend) + (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) + (assert (> (file-write-date (asdf:compile-file-pathname* "file2")) before)) + ) + (unless (= (file-write-date (asdf:compile-file-pathname* "file3")) + file3-date) + (error "Excessive compilation of file3.lisp: traverse bug.")))) diff --git a/test/test-source-registry.s b/test/test-source-registry.s deleted file mode 100644 index 51b348217b1723c98d4a434f015912ecd87271dc..0000000000000000000000000000000000000000 --- a/test/test-source-registry.s +++ /dev/null @@ -1,82 +0,0 @@ -(load "script-support") -(load "../asdf") - -(defvar *test-directory* - (ensure-directory-pathname - (getenv "PWD"))) - -(defvar *test-conf-directory* - (merge-pathnames "conf.d/" (getenv "PWD"))) - -(defun under-test-directory (path &optional (defaults *test-directory*)) - (merge-pathnames path defaults)) - -(defun ensure-recursive-directory (path) - (concatenate - 'string - (namestring - (ensure-directory-pathname path)) - "/")) - -(defun create-conf-files (&optional (path *test-conf-directory*)) - (let ((v `(("conf1.conf" - ((:directory ,(namestring (under-test-directory "dir1/"))))) - ("conf2.conf" - ((:tree ,(namestring (under-test-directory "dir2/")))))))) - (loop - :for (file contents) :in v :do - (with-open-file (out (merge-pathnames file path) - :direction :output - :if-exists :supersede) - (with-standard-io-syntax - (format out "~{~S~}" contents)))))) - -(defvar *test-config-1* - `(:source-registry - (:tree ,(getenv "PWD")) - (:ignore-inherited-configuration))) - -(defvar *test-expect-1* - (append - (loop - :for dir - :in '("dir1/" "dir2/dir3/" "dir2/dir4/" "dir2/") - :collect (merge-pathnames dir *test-directory*)) - (list *test-directory*))) - -(defvar *test-source-registries* - '(test-environment-source-registry - test-environment-source-registry-recursive - test-something-3)) - -(defun test-environment-source-registry () - (process-source-registry (getenv "CL_SOURCE_REGISTRY") - :inherit *test-source-registries*)) - -(defun test-environment-source-registry-recursive () - (process-source-registry - (ensure-recursive-directory - (getenv "CL_SOURCE_REGISTRY")) - :inherit *test-source-registries*)) - -(defun test-directory-source-registry - (&optional (directory *test-conf-directory*)) - (process-source-registry - (validate-source-registry-directory directory))) - -(defun test-something-3 () nil) - -(cl-user::quit-on-error - (create-conf-files) - (assert (every #'pathname (test-environment-source-registry))) - (assert (every #'pathname (test-environment-source-registry-recursive))) - (assert (equal (test-directory-source-registry) *test-expect-1*)) - - ;; FIXME: - ;; (assert (equal (process-source-registry - ;; *test-config-1*) - ;; *test-expect-1*)) - - ;; FIXME: add more tests - ;; (assert (equal ...)) - ) \ No newline at end of file diff --git a/test/test-utilities.script b/test/test-utilities.script index 9475d2b127b50e739fb40255e43c10497b8f8362..5be682f6f909f50b37ceecde874ad2a1212197b1 100644 --- a/test/test-utilities.script +++ b/test/test-utilities.script @@ -20,9 +20,10 @@ (every (complement #'directory-pathname-p) (list (make-pathname :name "foo" :type nil :directory '(:absolute "tmp")) - (make-pathname :name nil :type "bar" :directory '(:absolute "tmp")) - (make-pathname :name "." :type nil :directory '(:absolute "tmp")) - (make-pathname :name "." :type "" :directory '(:absolute "tmp"))))) + (make-pathname :name nil :type "bar" :directory '(:absolute "tmp"))))) +;; These are funky and non portable. Omit from tests. +;; (make-pathname :name "." :type nil :directory '(:absolute "tmp")) +;; (make-pathname :name "." :type "" :directory '(:absolute "tmp")) (assert (equal (multiple-value-list (component-name-to-pathname-components "" t)) '(:relative nil nil))) (assert (equal (multiple-value-list (component-name-to-pathname-components "" nil)) @@ -41,4 +42,5 @@ (asdf::version-satisfies (asdf:asdf-version) "1.608")) (assert (not (asdf::version-satisfies (asdf:asdf-version) "666"))) + ) diff --git a/test/test1.asd b/test/test1.asd index b1d7bff01ba773a9cb74fa0cb5ba5de5da61427d..3acad96ee4cabb2cc67ba2ea89e7c87f9c279d6d 100644 --- a/test/test1.asd +++ b/test/test1.asd @@ -1,7 +1,7 @@ ;;; -*- Lisp -*- (asdf:defsystem test1 - :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) - (:file "file1"))) + :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) + (:file "file1"))) #| 1) from clean, check that all fasl files build and that some function diff --git a/test/test1.script b/test/test1.script index d22190c73bee4bbd38710471ab8f706b561fff61..fe6955f8148277e42558cf4ad641cddfe5606bb0 100644 --- a/test/test1.script +++ b/test/test1.script @@ -4,8 +4,8 @@ (quit-on-error (setf asdf:*central-registry* '(*default-pathname-defaults*)) + ;;(trace ASDF::MERGE-COMPONENT-RELATIVE-PATHNAME ASDF::COMPONENT-RELATIVE-PATHNAME) (asdf:operate 'asdf:load-op 'test1) - ;; test that it compiled (let* ((file1 (asdf:compile-file-pathname* "file1")) (file2 (asdf:compile-file-pathname* "file2")) @@ -22,6 +22,7 @@ ;; recompiled (sleep 1) ; mtime has 1-second granularity, so pause here for fast machines + (trace asdf::operation-done-p asdf::traverse) (asdf::run-shell-command "rm -f ~A" (namestring file2)) (asdf:operate 'asdf:load-op 'test1) (assert (= file1-date (file-write-date file1))) @@ -33,7 +34,7 @@ ;; unix cwd. this is not a problem for run-tests.sh, but can be in general (let ((before (file-write-date file2))) - (asdf::run-shell-command "touch file1.lisp") (sleep 1) + (asdf::run-shell-command "touch file1.lisp") (asdf:operate 'asdf:load-op 'test1) - (assert (> (file-write-date file2) before))))) + (assert (> (file-write-date file2) before))))) diff --git a/test/test2.asd b/test/test2.asd index 31135f099dd68c014139a4d8a425b52f3a6a25f8..bbfe4056b515cbdad0702dbc6a759464fc740cef 100644 --- a/test/test2.asd +++ b/test/test2.asd @@ -4,5 +4,3 @@ :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) (:file "file1")) :depends-on (version 'test2a "1.1")) - - diff --git a/test/test2b1.asd b/test/test2b1.asd index bc94d5d9dcbe389bbc37e18a48de831210707325..f91f21e3a13dbb87b894c97bdee63d71e3f4954d 100644 --- a/test/test2b1.asd +++ b/test/test2b1.asd @@ -3,4 +3,4 @@ :version "1.0" :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) (:file "file1")) - :in-order-to ((load-op (load-op (version test2a "1.1"))))) + :in-order-to ((load-op (load-op (:version test2a "1.1"))))) diff --git a/test/test2b2.asd b/test/test2b2.asd index 6b294c30ed0fc52d3498c9ba010a878e82facc5b..4c4cfb918b87ab6c9c614c594434e0343ec784a2 100644 --- a/test/test2b2.asd +++ b/test/test2b2.asd @@ -3,6 +3,6 @@ :version "1.0" :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) (:file "file1")) - :in-order-to ((load-op (load-op (version test2a "1.2"))))) + :in-order-to ((load-op (load-op (:version test2a "1.2"))))) diff --git a/test/test2b3.asd b/test/test2b3.asd index 9bc997fdc2a44e8cbb6f9b35a6a77f2de24d0344..3ec8ae6b1a0a5ca5b759b47e6dcebf902e93c401 100644 --- a/test/test2b3.asd +++ b/test/test2b3.asd @@ -4,5 +4,3 @@ :components ((:file "file2" :in-order-to ((compile-op (load-op "file1")))) (:file "file1")) :depends-on (bet-you-cant-find-this)) - - diff --git a/test/test3.asd b/test/test3.asd index 23848d6584ba9aa8a176a22250dec53730a8ebea..b655bef82a32a1739f61a3f5a949e8e75f39dc3f 100644 --- a/test/test3.asd +++ b/test/test3.asd @@ -4,8 +4,7 @@ :components ((:module "deps" :if-component-dep-fails :try-next - :pathname "." + :pathname "" :components ((:file "file1" :in-order-to ((compile-op (feature :f1)))) (:file "file2" :in-order-to ((compile-op (feature :f2)))))))) - diff --git a/test/wild-module.asd b/test/wild-module.asd index b12f351c4e2fb90671a684170a8bf61f5dc765d7..8baf5233f192fb745344afeb968c866bd73d2531 100644 --- a/test/wild-module.asd +++ b/test/wild-module.asd @@ -2,4 +2,4 @@ (asdf:defsystem :wild-module :version "0.0" - :components ((:wild-module "systems" :pathname "*.asd"))) + :components ((:wild-module "systems" :pathname #p"*.asd"))) diff --git a/test/wild-module.script b/test/wild-module.script index 997203b1182760e333eb87be5b11ff966d477eae..40a3a7811051f2df1bdc81d3887d084e7f4b432e 100644 --- a/test/wild-module.script +++ b/test/wild-module.script @@ -2,9 +2,6 @@ (load "script-support") (load-asdf) (quit-on-error - (load-asdf) (load "../wild-modules") - (setf asdf:*central-registry* '(*default-pathname-defaults*)) - (asdf:operate 'asdf:load-op 'wild-module) - ) \ No newline at end of file + (asdf:operate 'asdf:load-op 'wild-module)) diff --git a/website/source/contributors.md b/website/source/contributors.md deleted file mode 100644 index e3fb4d35350043a26760e03c70ad17be004250d3..0000000000000000000000000000000000000000 --- a/website/source/contributors.md +++ /dev/null @@ -1,25 +0,0 @@ -{include "resources/header.md"} - -<div class="contents"> - -### Contributors - -* crhodes - Christophe Rhodes <csr21@cantab.net> -* dan_b - Daniel Barlow -* demoss - Nikodemus Siivola <nikodemus@random-state.net> -* gwking - Gary King <gwking@metabang.com> -* kevinrosenberg - Kevin Rosenberg -* nhabedi - Edi Weitz -* pvaneynd - Peter Van Eynde -* rjain - Rahul Jain - -(If I've left off your name, please accept my apologies and -[let me know](mailto:gwking@metabang.com)) - -</div> - -</div> -{include "resources/footer.md"} -</div> - - diff --git a/website/source/copyright.md b/website/source/copyright.md deleted file mode 100644 index 22decf5b7020319878fc6eb3ab087325fa2a3fca..0000000000000000000000000000000000000000 --- a/website/source/copyright.md +++ /dev/null @@ -1,38 +0,0 @@ -{include "resources/header.md"} - -<div class="contents"> - -### Copyright - -ASDF Copyright (C) 2001-2009 Daniel Barlow and contributors - -This website Copyright (C) 2001-2009 Daniel Barlow and -contributors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to -whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall -be included in all copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -</div> - -</div> -{include "resources/footer.md"} -</div> diff --git a/website/source/getting-started.mmd b/website/source/getting-started.mmd deleted file mode 100644 index b4b16b13cd7cbc6d16ccd2cdbe272f22b18613e1..0000000000000000000000000000000000000000 --- a/website/source/getting-started.mmd +++ /dev/null @@ -1,186 +0,0 @@ -{include resources/header.md} - -{set-property title "Getting Started | ASDF"} -{set-property docs-package asdf} - -# Table of Contents - -{table-of-contents :start 2 :depth 3} - -## Introduction - -Simple Lisp programs are defined in a single file; to use -them, you load the file and go. More complicated systems, -however, are made up of many files (and possibly depend on -other systems made up of their own files). Using these -requires loading the sub-systems and files in the right -order -- an error-prone and tedious task. - -ASDF is *a*nother *s*ystem *d*efinition *f*acility: a tool -for describing the sub-systems and files that comprise a -system and for operating on these components in the right -order so that they can be compiled, loaded, tested, etc. - -ASDF presents two faces: one for system implementors who need -to be able to describe their systems and one for -Lisp programmers who want to use those systems. -This document describes ASDF for the second audience. If you -want to build your own systems with ASDF, then you'll also -want to read the ASDF [system definition guide][sys-guide]. - -## Installing ASDF - -### Download - -Many Lisp implementations include a copy of ASDF. You can -usually load this copy using Common-Lisp's -[require][hs-require] function: - - * (require 'asdf) - ("ASDF") - -Consult your Lisp's documentation for details. If ASDF -doesn't come bundled with your Lisp or if you want to make -sure that you have the most recent version, then you'll want -to download it from the [common-lisp.net][asdf-home] website. - -### Setup - -The single file [asdf.lisp][] is all you need to use ASDF. -Once you load it in a running Lisp, you're ready to go. For -maximum convenience you will want to have ASDF loaded -whenever you start your Lisp. check your -implementation's manual for details on how to load it from -the startup script or creating a custom Lisp image. - -### Using - -ASDF provides three commands for the most common system -operations: [load-system][], [compile-system][], and -[test-system][]: - -{docs load-system} -{docs compile-system} -{docs test-system} - -Because ASDF is an extensible system for defining -*operations* on *components*, it also provides a generic -function: [operate][] (which is usually abbreviated by -[oos][]). You'll use `oos` whenever you want to do something -beyond compiling, loading and testing. - -{docs operate} -{docs oos} - -Before ASDF can operate on a system, however, it must be able -to find that system's definition. - -### How ASDF finds systems - -You can load system definition files by hand: - - (load "/path/to/my/system/my-system.asd") - -or you can tell ASDF where to look to find them using the -[`*central-registry*`][*central-registry*] parameter. - -{docs *central-registry*} - -You'll need to set this variable up to match your system setup and will probably want to include it in your Lisp's startup script. - -## Controlling where ASDF saves compiled files - -Each Common Lisp implementation has its own format for -compiled files (fasls for short). If you use multiple -implementations (or multiple versions of the same -implementation), you'll soon find your source directories -littered with various `DFSL`s, `FASL`s, `CFSL`s and so on. -Worse yet, some implementations use the same file extension -or change formats from version to version which means that -you'll have to recompile binaries as you switch from one -implementation to the next. - -As of version 1.365, ASDF includes [ASDF-binary-locations][asdf-binary-locations] to mitigate the problem. - -#### Default Locations - -The default binary location for each Lisp implementation is a subdirectory of each source directory. To account for different Lisps, Operating Systems, Implementation versions, and so on, ASDF borrows code from [SLIME][] to create reasonable custom directory names. Here are some examples: - - * SBCL, version 1.0 on Mac OS X for intel: sbcl-1.0-darwin-x86 - - * Franz Allegro, version 8.0, ANSI Common Lisp: allegro-8.0a-macosx-x86 - - * Franz Allegro, version 8.1, Modern (case sensitive) Common Lisp: allegro-8.1m-macosx-x86 - -If you want to keep `FASL` files out of source tree entirely \*centralize-lisp-binaries\* to put compiled files into sub-directories of a single central location (see - - [cbl]: #centralize-lisp-binaries - -Here is a summary of the variables that control ASDF's source-to-binary mappings: - - * \*enable-asdf-binary-locations\*: - If false, then ASDF will place binaries in the same - directory as the source. If true, then ASDF will move the - binaries using the rest of the configuration. Defaults to - `nil`. - - * \*centralize-lisp-binaries\*: If true, compiled lisp - files without an explicit mapping (see - \*source-to-target-mappings\*) will be placed in - subdirectories of \*default-toplevel-directory\*. If - false, then compiled lisp files without an explicit - mapping will be placed in subdirectories of their - sources. Defaults to `nil`. - - * \*default-toplevel-directory\*. If - \*centralize-lisp-binaries\* is true, then compiled lisp - files without an explicit mapping (see - \*source-to-target-mappings\*) will be placed in - subdirectories of \*default-toplevel-directory\*. - Defaults to a sub-directory named `.fasls` in the current - user's home directory. - - * \*include-per-user-information\*. specifies whether or - not to include user information in the directory. Only - used when \*centralize-lisp-binaries\* is true. Defaults - to `nil`. - - * \*map-all-source-files\*. If true, then all source files - will be mapped by ASDF. If `nil`, then only Common - Lisp Source Files (i.e., instances of cl-source-file or - its subclasses) will be. Defaults to `nil`. - - * \*source-to-target-mappings\*. This specifies mappings - from source to target. If the target is nil, then it - means to *not* map the source to anything. I.e., to leave - it as is. This has the effect of turning off - ASDF-Binary-Locations for the given source directory. The - default depends on the Lisp implementation. - -These variables are used by -`output-files-for-system-and-operation` to determine where to -place a source file's binary. You can further customize ABL -by writing additional methods on the generic function -`output-files-for-system-and-operation`. - -See the [manual][binary-locations] for more details. - -## Summary - -To use ASDF: - -* load `asdf.lisp` into your Lisp image, - -* make sure ASDF can find system definitions by loading them - yourself or setting up [`asdf:*central-registry*`][*central-registry*], - -* use [operate][] to tell ASDF what you'd like to - do to what systems (for simple operations, you can use - [load-system][], [compile-system][] or [test-system][] - instead). - -## Indices - -{docs-index all} - -{include resources/footer.md} diff --git a/website/source/git.mmd b/website/source/git.mmd deleted file mode 100644 index 66eaf557105f132a04bb55b6501c5755ad8bcc5e..0000000000000000000000000000000000000000 --- a/website/source/git.mmd +++ /dev/null @@ -1,45 +0,0 @@ -## git recipes - -### sychronize with the central repository - - # downloads the latest commits from CL.net - git fetch origin - - # removes any local changes and synchronizes with - # what we just downloaded from the remote repo - git reset --hard origin/master - - -## git resources - -Here are a few links that might be helpful to people learning -git. Its culturally different than CVS; but once you learn -it, there's no turning back. The ability to experiment with -local branches, back up, rewrite history, and only publish -the sanitized history is quite liberating. - -* http://progit.org/book/ (recommended) -* http://tomayko.com/writings/the-thing-about-git -* http://git-scm.com/documentation -* http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html -* http://www.kernel.org/pub/software/scm/git/docs/everyday.html -* http://www.kernel.org/pub/software/scm/git/docs/user-manual.html - -My suggestions - -* always have `gitk --all` loaded for the repo you're working on - -* update (not reload) `gitk` after modifying any branches or - tags; this leaves the old objects displayed - -* `git reset --hard $OLDSHA1` can clean many messes, letting - you try again. - -* reload gitk to hide deleted branches you're comfortable - with losing - -* learn by playing around with local clones of a larger - repository - -* install some of the git contribs, especially - git-completion.bash and git-new-workdir diff --git a/website/source/glossary.md b/website/source/glossary.md deleted file mode 100644 index 2e94f8e167a8e53b09e9ce146317273d0beeaa90..0000000000000000000000000000000000000000 --- a/website/source/glossary.md +++ /dev/null @@ -1,11 +0,0 @@ -{include "resources/header.md"} - -## Glossary - -{glossary} - - [fixture]> glossary The fixture is the environment in which a testcase runs. The -fixture is the code that prepares the environment for the test and that resets -the environment after the test. Fixtures can be shared by many test-cases. - -{include "resources/footer.md"} diff --git a/website/source/index.mmd b/website/source/index.mmd deleted file mode 100644 index 207fb3e895058fc2fd0523788bef037c42854e5d..0000000000000000000000000000000000000000 --- a/website/source/index.mmd +++ /dev/null @@ -1,72 +0,0 @@ -{include resources/header.md} - -<div class="contents"> -<div class="system-links"> - * [Mailing Lists][] - * [Getting it][] - * [Changelog][] - * [System Tests][tests] -</div> - -<div class="system-description"> - -### What it is - -ASDF is a tool for describing how source files are organized: -what depends on which and when. I.e., it is *A*nother -*S*ystem *D*efinition *F*acility. - -If you want to use ASDF with other people's systems, see the -[getting started][getting-started] guide. For details on -defining your own systems, you should browse the [manual][]. - -<a id="mailing-lists"></a> - -### Mailing Lists - - * [asdf-devel][]: A list for questions, patches, - bug reports, and so on; It's for everything. - -<a href="downloads"></a> - -### Where is it - -Though they may lag behind the version here, ASDF comes -bundled with most Lisps. To get the greatest and latest, you -can: - -* download just the source for [asdf.lisp][], -* download the [tarball][] to get all bells and whistles, -* pull the latest from our git repository ([browse][git-browse]) - - git clone http://common-lisp.net/project/asdf/asdf.git - -{remark * use [ASDF-install][] to update yourself } - -### Contributing - -We're still working out the details but you should take a look at our [git resources][git-resources]. -For now, send email to -gwking@metabang.com or the project [mailing-list][asdf-devel] -with questions or patches. - - -<a id="news"></a> - -### What is happening - -Sunday; September 13, 2009 -: Added documentation for ASDF-Binary-Locations - -Saturday; June 6, 2009 -: updated website and added [oos][] shortcuts like [load-system][]. - -Sunday; May 3, 2009 -: moving towards git and Common-Lisp.net - - -</div> -{include resources/footer.md} -</div> - - diff --git a/website/source/manual.mmd b/website/source/manual.mmd deleted file mode 100644 index 58c49c59e2f80a902b4f4c77173df7a2091b9257..0000000000000000000000000000000000000000 --- a/website/source/manual.mmd +++ /dev/null @@ -1,1137 +0,0 @@ -{include resources/header.md} -{set-property docs-package asdf} - -# ASDF Manual - -# Table of Contents - -{table-of-contents :start 2 :depth 3} - -## Introduction - -ASDF is *a*nother *s*ystem *d*efinition *f*acility: a tool -for describing the sub-systems and files that comprise a -system and for operating on these components in the right -order so that they can be compiled, loaded, tested, etc. - -ASDF presents two faces: one for system implementors who need -to be able to describe their systems and one for -Lisp programmers who want to use those systems. See -the [getting started][getting-started] guide to learn how to -use ASDF to load a system. This document describes how to -write your own system definitions for ASDF. - -## Defining systems with defsystem - -This chapter describes how to use ASDF to define systems and -develop software. First, some definitions: - -* component - a piece of a system. Systems are made of - components. They range from individual files, to groups of - files called modules, to entire sub-systems. - -* module - a component comprised of a group of components. A - module is smaller than a system but (usually) larger - than a single file. - -* operation - something done to a system. The most typical - operations are [*compile*][compile-system], - [*load*][load-system], and [*test*][test-system] but ASDF - is extensible so many others are possible. - -* system - a group of files, modules and sub-systems - (i.e., components) packaged into a coherent whole. - -### The `defsystem` form - -Systems are defined with the `defsystem` macro{footnote -"Systems can be constructed programmatically by instantiating -components using make-instance. Most of the time, however, it -is much more practical to use `defsystem`."} This section -begins with a simple example, then gives the full grammar of -`defsystem`. Here is a complete system definition that should -be saved in the file `hello-lisp.asd`{footnote -"Though it can be changed, ASDF's standard system finding -algorithms looks for a system named `foo` in a file named -`foo.asd`."}: - - (defpackage hello-lisp-system (:use :common-lisp :asdf)) - - (in-package :hello-lisp-system) - - (defsystem "hello-lisp" - :description "hello-lisp: a sample Lisp system." - :version "0.2" - :author "Joe User <joe@example.com>" - :licence "Public Domain" - :components ((:file "packages") - (:file "macros" :depends-on ("packages")) - (:file "hello" :depends-on ("macros")))) - -Some notes about this example: - - * The file starts with `defpackage` and `in-package` forms - to define and use a package expressly for this system - definition. By convention, this package is named by - taking the system name and suffixing `-system` - note - that it is _not_ the same package that you will use for the - application code. - - This is not required by ASDF, but helps avoid - namespace pollution and so is considered good form. - - * The `defsystem` form defines a system named "hello-lisp" - that contains three source files: packages, macros and - hello. - - * The `:depends-on` clauses tell ASDF that the file - `macros` depends on `packages` (presumably because the - package it's in is defined in `packages`), and the file - `hello` depends on `macros` (and hence transitively on - `packages`). This means that ASDF will compile and load - `packages` and `macros` before starting the compilation - of the file `hello`. - - * The files are located in the same directory as the file - with the system definition. ASDF resolves symbolic links - before loading the system definition file and stores its - location in the resulting system{footnote "It is - possible, though almost never necessary, to override this - behaviour."}. This is a good thing because the user can - move the system sources without having to edit the system - definition. - - * the `:version`, `:author`, `description` and other fields - are not required but they provide documentation and - information for people that want to use this system. - -### A more involved example - -Let's illustrate some more involved uses of `defsystem` via a -slightly convoluted example: - - (defsystem "foo" - :version "1.0" - :components - ((:module "foo" - :components ((:file "bar") - (:file "baz") - (:file "quux")) - :perform (compile-op :after (op c) (do-something c)) - :explain (compile-op :after (op c) (explain-something c))) - (:file "blah"))) - -The `:perform` tokens need explaining: essentially, these clauses: - - :perform (compile-op :after (op c) - (do-something c)) - :explain (compile-op :after (op c) - (explain-something c)) - -have the effect of - - (defmethod perform :after ((op compile-op) (c (eql ...))) - (do-something c)) - - (defmethod explain :after ((op compile-op) (c (eql ...))) - (explain-something c)) - -where `...` is the component in question.{footnote "note that -although this also supports `:before` methods, they may not -do what you want them to - a `:before` method on perform -`((op compile-op) (c (eql ...)))` will run after all the -dependencies and sub-components have been processed, but -before the component in question has been compiled."} - -### The defsystem grammar - - system-definition := ( defsystem system-designator {option}* ) - - option := :components component-list - | :pathname pathname - | :default-component-class - | :perform method-form - | :explain method-form - | :output-files method-form - | :operation-done-p method-form - | :depends-on ( {dependency-def}* ) - | :serial [ t | nil ] - | :weakly-depends-on ( {dependency-def}* ) - | :in-order-to ( {dependency}+ ) - | :class symbol - - component-list := ( {component-def}* ) - - component-def := component-name - | ( component-type name {option}* ) - - component-type := :module | :file - | :system | other-component-type - - dependency-def := component-name - | ( :feature feature-name ) - | ( :version component-name version-specifier) - - dependency := ( dependent-op {requirement}+ ) - requirement := ( required-op {required-component}+ ) - | ( feature feature-name ) - dependent-op := operation-name - required-op := operation-name - - component-name := string - | symbol - - operation-name := symbol - feature-name := symbol - - version-specifier := number[.number]* - -#### Serial dependencies - -If the `:serial t` option is specified for a module, ASDF -will add dependencies for each 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"))) - -{anchor section-weakly-depends-on} - -#### Weakly-depends-on - -The `weakly-depends-on` argument says of the component -depended on "if this component can be found, then impose a -dependency, otherwise ignore it." At the top level, this can -be useful for allowing a system to take advantage of another -system, if it is present. For example, a system might -generate on-line documentation if a documentation generation -system (like [TINAA][tinaa-project], -[Albert][albert-project], etc.) was present. A system that -does text matching might do only simple matching but weakly -depend on a regular expression system and, if that system is -installed, would be able to also do regular expression -matching. - -##### Notes: - -* It's not entirely clear that this option is very - helpful at a level below the top-level. Since it uses - find-component, the behavior differs between top level and - lower level; perhaps that needs to be explained. - -* Presumably this will be most useful when complemented by - some conditional loading construct in the system that has - the dependency. What is that construct? - -#### Source location - -The `:pathname` option is optional in all cases for systems -defined via `defsystem`, and in the usual case the user is -recommended not to supply it. - -Instead, 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: - - * The host/device/directory parts of `*load-truename*`, if - it is bound - - * `*default-pathname-defaults*`, otherwise - -If a system is being redefined, the top-level pathname will -be - - * changed, if explicitly supplied or obtained from - `*load-truename*` (so that an updated source location is - reflected in the system definition) - - * changed if it had previously been set from - `*default-pathname-defaults*` - - * left as before, if it had previously been set from - `*load-truename*` and `*load-truename*` is currently - unbound (so that a developer can evaluate a `defsystem` - form from within an editor without clobbering its source - location) - -### Other code in .asd files - -Files containing defsystem forms are regular Lisp files that -are executed by `load`. Consequently, you can put whatever -Lisp code you like into these files (e.g., code that examines -the compile-time environment and adds appropriate features to -`*features*`). However, some conventions should be followed, -so that users can control certain details of execution of the -Lisp in .asd files: - - * Any informative output (other than warnings and errors, - which will be handled by the condition system) should be - sent to the Common Lisp stream - [`*standard-output*`][hs-standard-output], so that users - can easily control the disposition of output from ASDF - operations. - -## System dependencies - -**Warning**: As of July 20, 2009, this aspect of ASDF is in flux. -We suggest not relying too heavily on the current syntax. - -There are several ways in which one system can depend on -another: - -* simple dependency (the default) -* `weak` dependency (using [`:weakly-depends-on`][weakly-depends-on]) -* `contingent` dependency (not currently implemented) - - [weakly-depends-on]: #section-weakly-depends-on - -The following tables illustrate how these dependencies work: - -#### Simple - -<table caption="Simple"> -<tr><th><th colspan="2">component status</th><th rowspan="2" valign="top">effect</th></tr> -<tr><th>expression</th><th>component a</th><th>component/feature b</th></tr> - -<tr><td>a (:depends-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">present</td><td>(operate b), (operate a)</td> -<tr><td>a (:depends-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">absent</td><td>error</td> -<tr><td>a (:depends-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">present</td><td> error</td> -<tr><td>a (:depends-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">absent</td><td> error</td></tr> -</table> -</p> - - -#### `:weakly-depends-on` - -<table> -<tr><th><th colspan="2">component status</th><th rowspan="2" valign="top">effect</th></tr> -<tr><th>expression</th><th>component a</th><th>component/feature b</th></tr> - -<tr><td>a (:weakly-depends-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">present</td><td> (operate b), (operate a)</td> -<tr><td>a (:weakly-depends-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">absent</td><td> (operate a)</td> -<tr><td>a (:weakly-depends-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">present</td><td> error</td> -<tr><td>a (:weakly-depends-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">absent</td><td> error</td></tr> -</table> -</p> - -#### Contingent - -<table> -<tr><th><th colspan="2">component status</th><th rowspan="2" valign="top">effect</th></tr> -<tr><th>expression</th><th>component a</th><th>component/feature b</th></tr> - -<tr><td>a (:contingent-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">present</td><td> (operate b), (operate a)</td> -<tr><td>a (:contingent-on (:b))</td><td style="text-align:center">present</td><td style="text-align:center">absent</td> -<tr><td>a (:contingent-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">present</td><td> error</td> -<tr><td>a (:contingent-on (:b))</td><td style="text-align:center">absent</td><td style="text-align:center">absent</td><td> error</td></tr> -</table> -</p> -<hr /> - - -## The object model of ASDF - -ASDF is entirely object-oriented. Both a system's -structure and the operations that can be performed on systems -follow a protocol. ASDF is extensible to new operations and -to new component types. This allows the addition of -behaviours: for example, a new component could be added for -Java JAR archives, and methods specialised on `compile-op` -added for it that would accomplish the relevant actions. - -This chapter deals with _components_, the building blocks of -a system, and _operations_, the actions that can be performed -on a system. - - -### Operations - -An operation object of the appropriate type is instantiated -whenever the user wants to do something with a system like: - - * compile all its files, - - * load the files into a running lisp environment, - - * copy its source files somewhere else. - -Operations can be invoked directly, or examined to see what -their effects would be without performing them. _FIXME: -document how!_ There are a bunch of methods specialised on -operation and component type that actually do the grunt work. - -The operation object contains whatever state is relevant for -this purpose (perhaps a list of visited nodes, for example) -but primarily is a nice thing to specialise operation methods -on and easier than having them all be EQL methods. - -Operations are invoked on systems via `operate` or its -synonym `oos`: - -{docs operate} -{docs oos} - - -#### Predefined operations of ASDF - -All the operations described in this section are in the -`ASDF` package. They are invoked via the `operate` generic -function. - - (asdf:operate 'asdf:operation-name 'system-name {operation-options ...}) - --- Operation: **compile-op** &key proclamations - - -This operation compiles the specified component. If -proclamations are supplied, they will be proclaimed. This is -a good place to specify optimization settings. - -When creating a new component type, you should provide -methods for `compile-op`. - -When `compile-op` is invoked, component dependencies often -cause some parts of the system to be loaded as well as -compiled. Invoking `compile-op` does not necessarily load all -the parts of the system, though; use `load-op` to load a -system. - --- Operation: **load-op** &key proclamations - -This operation loads a system. - -The default methods for `load-op` compile files before -loading them. For parity, your own methods on new component -types should probably do so too. - --- Operation: **load-source-op** - - -This operation will load the source for the files in a module -even if the source files have been compiled. Systems -sometimes have knotty dependencies which require that sources -are loaded before they can be compiled. This is how you do -that. - -If you are creating a component type, you need to implement -this operation - at least, where meaningful. - --- Operation: **test-op** - -This operation should test the specified component. ASDF does -not (currently) provide a method for returning a value from -the test-op (or any other operation), so the implementor must -ensure that executing `test-op` has the side effect -of printing useful information about the tests to the CL -stream `*standard-output*`. - -The default method for `test-op` does nothing, and will have -to be overridden, but invoking `test-op` on a component for -which nothing is defined will be a simple no-op. - -The default dependency for `test-op` on a system is to -require `load-op` on that system. - -The default method for `operation-done-p` for `test-op` is to return `nil` regardless of component because tests should usually be run whether or not they have been run previously. - - -#### Creating new operations - -ASDF was designed to be extensible in an object-oriented -fashion. To teach ASDF new tricks, a programmer can implement -the behaviour he wants by creating a subclass of `operation`. - -ASDF's pre-defined operations are in no way "privileged", but -it is requested that developers never use the `ASDF` package -for operations they develop themselves. The rationale for -this rule is that we don't want to establish a "global ASDF -operation name registry", but also want to avoid name -clashes. - -An operation must provide methods for the following generic -functions when invoked with an object of type `source-file`: -_FIXME describe this better_ - - * `output-files` - * `perform` The `perform` method must call `output-files` to find out where to put its files, because the user is allowed to override - * `output-files` for local policy `explain` - * `operation-done-p`, if you don't like the default one - -Operations that print output should send that output to the -standard CL stream `*standard-output*`, as the Lisp compiler -and loader do. - - -### Components - -A component represents a source file or (recursively) a -collection of components. A system is (roughly speaking) a -top-level component that can be found via `find-system`. - -A system designator is a string or symbol and behaves just -like any other component name (including with regard to the -case conversion rules for component names). - --- Function: **find-system** system-designator &optional (error-p t) - - -Given a system designator, `find-system` finds and returns a -system. If no system is found, an error of type -`missing-component` is thrown, or `nil` is returned if -`error-p` is false. - -To find and update systems, `find-system` funcalls each -element in the `*system-definition-search-functions*` list, -expecting a pathname to be returned. The resulting pathname -is loaded if either of the following conditions is true: - - * there is no system of that name in memory - * the file's last-modified time exceeds the last-modified time of the system in memory - -When system definitions are loaded from .asd files, a new -scratch package is created for them to load into, so that -different systems do not overwrite each others operations. -The user may also wish to (and is recommended to) include -`defpackage` and `in-package` forms in his system definition -files, however, so that they can be loaded manually if need -be. - -The default value of `*system-definition-search-functions*` -is a function that looks in each of the directories given by -evaluating members of `*central-registry*` for a file whose -name is the name of the system and whose type is asd. The -first such file is returned, whether or not it turns out to -actually define the appropriate system. Hence, it is strongly -advised to define a system foo in the corresponding file -foo.asd. - - -#### Common attributes of components - -All components, regardless of type, have the following -attributes. All attributes except `name` are optional. - -##### Name - -A component name is a string or a symbol. If a symbol, its -name is taken and lowercased. The name must be a suitable -value for the `:name` initarg to `make-pathname` in whatever -filesystem the system is to be found. - -The lower-casing-symbols behaviour is unconventional, but was -selected after some consideration. Observations suggest that -the type of systems we want to 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 - -##### Version identifier - -This optional attribute is used by the test-system-version -operation. See [Predefined operations of ASDF][10]. For the -default method of test-system-version, the version should be -a string of integers separated by dots, for example -'1.0.11'. - -##### Required features - -Traditionally defsystem users have used reader conditionals -to include or exclude specific per-implementation files. This -means that any single implementation cannot read the entire -system, which becomes a problem if it doesn't wish to compile -it, but instead for example to create an archive file -containing all the sources, as it will omit to process the -system-dependent sources for other systems. - -Each component in an ASDF system may therefore specify -features using the same syntax as #+ does, and it will -(somehow) be ignored for certain operations unless the -feature conditional is a member of `*features*`. - -##### Dependencies - -This attribute specifies dependencies of the component on its -siblings. It is optional but often necessary. - -There is an excitingly complicated relationship between the -initarg and the method that you use to ask about dependencies - -Dependencies are between (operation component) pairs. In your -initargs for the component, you can say - - :in-order-to ((compile-op (load-op "a" "b") - (compile-op "c")) - (load-op (load-op "foo"))) - -This means the following things: - - * before performing compile-op on this component, we must - perform load-op on a and b, and compile-op on c, - - * before performing `load-op`, we have to load foo - -The syntax is approximately - - (this-op {(other-op required-components)}+) - - required-components := component-name - | (required-components required-components) - - component-name := string - | (:version string minimum-version-object) - - -Side note: - -This is on a par with what ACL defsystem does. mk-defsystem -is less general: it has an implied dependency - - for all x, (load x) depends on (compile x) - -and using a `:depends-on` argument to say that b depends on a -_actually_ means that - - (compile b) depends on (load a) - -This is insufficient for e.g. the McCLIM system, which -requires that all the files are loaded before any of them can -be compiled ] - -End side note - -In ASDF, the dependency information for a given component and -operation can be queried using `(component-depends-on -operation component)`, which returns a list - - ((load-op "a") (load-op "b") (compile-op "c") ...) - -`component-depends-on` can be subclassed for more specific -component/operation types: these need to `(call-next-method)` -and append the answer to their dependency, unless they have a -good reason for completely overriding the default -dependencies - -##### pathname - -This attribute is optional and if absent will be inferred -from the component's name, type (the subclass of -source-file), and the location of its parent. - -The rules for this inference are: - -(for source-files) - - * the host is taken from the parent - * pathname type is `(source-file-type component system)` - * the pathname case option is `:local` - * the pathname is merged against the parent - -(for modules) - - * the host is taken from the parent - * the name and type are `NIL` - * the directory is `(:relative component-name)` - * the pathname case option is `:local` - * the pathname is merged against the parent - -Note that the DEFSYSTEM operator (used to create a -"top-level" system) does additional processing to set the -filesystem location of the top component in that system. This -is detailed elsewhere, See [Defining systems with -defsystem][3]. - -The answer to the frequently asked question "how do I create -a system definition where all the source files have a .cl -extension" is thus - - (defmethod source-file-type ((c cl-source-file) - (s (eql (find-system 'my-sys)))) - "cl") - - -##### properties - -This attribute is optional. - -Packaging systems often require information about files or -systems in addition to that specified by ASDF's pre-defined -component attributes. Programs that create vendor packages -out of ASDF systems therefore have to create "placeholder" -information to satisfy these systems. Sometimes the creator -of an ASDF system may know the additional information and -wish to provide it directly. - -`(component-property component property-name)` and associated -setf method will allow the programmatic update of this -information. Property names are compared as if by `EQL`, so -symbols or keywords are usually used for property names. - - -#### Pre-defined subclasses of component - --- Component: **source-file** - - -A source file is any file that the system does not know how -to generate from other components of the system. - -Note that this is not necessarily the same thing as "a file -containing data that is typically fed to a compiler". If a -file is generated by some pre-processor stage (e.g. a .h file -from .h.in by autoconf) then it is not, by this definition, a -source file. Conversely, we might have a graphic file that -cannot be automatically regenerated, or a proprietary shared -library that we received as a binary: these do count as -source files for our purposes. - -Subclasses of source-file exist for various languages. -_FIXME: describe these._ - --- Component: **module** - - -A module is a collection of sub-components. - -A module component has the following extra initargs: - - * `:components` the components contained in this module - - * `:default-component-class` All child components which - don't specify their class explicitly are inferred to be - of this type. - - * `:if-component-dep-fails` This attribute takes one of the - values `:fail`, `:try-next`, `:ignore`, its default value - is `:fail`. The other values can be used for implementing - conditional compilation based on implementation - `*features*`, for the case where it is not necessary for - all files in a module to be compiled. - - * `:serial` When this attribute is set, each subcomponent - of this component is assumed to depend on all - subcomponents before it in the list given to - `:components`, i.e. all of them are loaded before a - compile or load operation is performed on it. - -The default operation knows how to traverse a module, so most -operations will not need to provide methods specialised on -modules. - -`module` may be subclassed to represent components such as -foreign-language linked libraries or archive files. - --- Component: **system** - - -`system` is a subclass of `module`. - -A system is a module with a few extra attributes for -documentation purposes; these are given elsewhere. See [The -defsystem grammar][6]. - -Users can create new classes for their systems: the default -`defsystem` macro takes a `:classs` keyword argument. - - -#### Creating new component types - -New component types are defined by subclassing one of the -existing component classes and specializing methods on the -new component class. - -_FIXME: this should perhaps be explained more throughly, not -only by example ..._ - -As an example, suppose we have some implementation-dependent -functionality that we want to isolate in one subdirectory per -Lisp implementation our system supports. We create a subclass -of `cl-source-file`: - - (defclass unportable-cl-source-file (cl-source-file) ()) - -A hypothetical function `system-dependent-dirname` gives us -the name of the subdirectory. All that's left is to define -how to calculate the pathname of an -`unportable-cl-source-file`. - - (defmethod component-pathname ((component unportable-cl-source-file)) - (let ((pathname (call-next-method)) - (name (string-downcase (system-dependent-dirname)))) - (merge-pathnames (make-pathname - :directory (list :relative name)) pathname))) - -The new component type is used in a `defsystem` form in this -way: - - (defsystem :foo - :components - ((:file "packages") - ... - (:unportable-cl-source-file "threads" - :depends-on ("packages" ...)) - ... - ) - -{anchor binary-locations} - -## Controlling where ASDF saves compiled files - -ASDF includes code to control where the binaries files are places. The location depends on the the Common Lisp implementation and platform. The getting-started guides summaries the variables that control the mapping. The details for each variable can be found below: - -#### Customization - -{anchor centralize-lisp-binaries} - -{docs *centralize-lisp-binaries*} - -{docs *default-toplevel-directory*} - -{docs *include-per-user-information*} - -{docs *map-all-source-files*} - -{docs *enable-asdf-binary-locations*} - -{docs *source-to-target-mappings*} - -{docs implementation-specific-directory-name} - -{docs output-files-for-system-and-operation} - -### Notes and Issues - - * SBCL ships with several included ASDF libraries which used to confuse ABL because it would try to recompile the `FASL`s and then run into directory permission problems. ASDF knows about these and uses a mapping like `/usr/local/lib/sbcl` to `nil` so that the `FASL`s are not relocated. - -## Special variables - -{docs *central-registry*} - -{docs *compile-file-warnings-behaviour*} - -{docs *compile-file-failure-behaviour*} - -{docs *resolve-symlinks*} - -{docs *system-definition-search-functions*} - -## Error handling - -It is an error to define a system incorrectly: an -implementation may detect this and signal a generalised -instance of `SYSTEM-DEFINITION-ERROR`. - -Operations may go wrong (for example when source files -contain errors). These are signalled using generalised -instances of `OPERATION-ERROR`. - -## Compilation error and warning handling - -ASDF checks for warnings and errors when a file is compiled. -The variables `*compile-file-warnings-behaviour*` and -`*compile-file-errors-behavior*` controls the handling of any -such events. The valid values for these variables are -`:error`, `:warn`, and `:ignore`. - - -## Additional Functionality - -ASDF includes several additional features that are generally -useful for system definition and development. These include: - - 1. system-relative-pathname - -It's often handy to locate a file relative to some system. -The system-relative-pathname function meets this need. It -takes two arguments: the name of a system and a relative -pathname. It returns a pathname built from the location of -the system's source file and the relative pathname. For -example - - > (asdf:system-relative-pathname 'cl-ppcre "regex.data") - #P"/repository/other/cl-ppcre/regex.data" - - -## Getting the latest version - -{remark 1. Decide which version you want. HEAD is the newest - version and usually OK, whereas RELEASE is for cautious - people (e.g. who already have systems using ASDF that they - don't want broken), a slightly older version about which - none of the HEAD users have complained. -} - -To get the greatest and latest, you can: - - * download just the source for [asdf.lisp][], - * download the [tarball][] to get all bells and whistles, - * pull the latest from our git repository - - git clone http://common-lisp.net/project/asdf/asdf.git - - -## TODO list - -## Outstanding spec questions, things to add - -* packaging systems - -* manual page component? - -* style guide for .asd files - - You should either use keywords or be careful with the - package that you evaluate defsystem forms in. Otherwise - (defsystem partition ...) being read in the cl-user - package will intern a cl-user:partition symbol, which - will then collide with the partition:partition symbol. - - Actually there's a hairier packages problem to think - about too. in-order-to is not a keyword: if you read - defsystem forms in a package that doesn't use ASDF, odd - things might happen - -* extending defsystem with new options - - You might not want to write a whole parser, but just to - add options to the existing syntax. Reinstate - parse-option or something akin - -* document all the error classes - -* what to do with compile-file failure - - Should check the primary return value from compile-file - and see if that gets us any closer to a sensible error - handling strategy - -* foreign files - - lift unix-dso stuff from db-sockets - -* Diagnostics - - A "dry run" of an operation can be made with the - following form: - - (traverse (make-instance '<operation-name>) (find-system <system-name>) 'explain) - - This uses unexported symbols. What would be a nice - interface for this functionality? - -## missing bits in implementation - -* all of the above - -* reuse the same scratch package whenever a system is - reloaded from disk - -* rules for system pathname defaulting are not yet - implemented properly - -* proclamations probably aren't - -* when a system is reloaded with fewer components than it - previously had, odd things happen - -we should do something inventive when processing a defsystem -form, like take the list of kids and setf the slot to nil, -then transfer children from old to new list as they're found - -* traverse may become a normal function - - If you're defining methods on traverse, speak up. - -* a lot of load-op methods can be rewritten to use - input-files and so perhaps they should be. - -* (stuff that might happen later) - -* david lichteblau's patch for symlink resolution? - -* Propagation of the :force option. "I notice that - - (oos 'compile-op :araneida :force t) - - also forces compilation of every other system the - :araneida system depends on. This is rarely useful to me; - usually, when I want to force recompilation of something - more than a single source file, I want to recompile only - one system. So it would be more useful to have - make-sub-operation refuse to propagate `:force t` to - other systems, and propagate only something like `:force - :recursively`. - - Ideally what we actually want is some kind of criterion - that says to which systems (and which operations) a - `:force` switch will propagate. - - The problem is perhaps that `force' is a pretty - meaningless concept. How obvious is it that `load :force - t` should force _compilation_? But we don't really have - the right dependency setup for the user to compile - `:force t` and expect it to work (files will not be - loaded after compilation, so the compile environment for - subsequent files will be emptier than it needs to be) - - What does the user actually want to do when he forces? - Usually, for me, update for use with a new version of the - lisp compiler. Perhaps for recovery when he suspects that - something has gone wrong. Or else when he's changed - compilation options or configuration in some way that's - not reflected in the dependency graph. - -* Other possible interface: have a 'revert' function akin to - 'make clean' - - (asdf:revert 'asdf:compile-op 'araneida) - - would delete any files produced by 'compile-op 'araneida. - Of course, it wouldn't be able to do much about stuff in - the image itself. - - How would this work? - -* traverse - - There's a difference between a module's dependencies - (peers) and its components (children). Perhaps there's a - similar difference in operations? For example, `(load - "use") depends-on (load "macros")` is a peer, whereas - `(load "use") depends-on (compile "use")` is more of a - `subservient' relationship. - -## Inspiration - -### mk-defsystem (defsystem-3.x) - -We aim to solve basically the same problems as mk-defsystem -does. However, our architecture for extensibility better -exploits CL language features (and is documented), and we -intend to be portable rather than just widely-ported. No -slight on the mk-defsystem authors and maintainers is -intended here; that implementation has the unenviable task of -supporting pre-ANSI implementations, which is no longer -necessary. - -The surface defsystem syntax of ASDF is more-or-less -compatible with mk-defsystem, except that we do not support -the `source-foo` and `binary-foo` prefixes for separating -source and binary files, and we advise the removal of all -options to specify pathnames. - -The mk-defsystem code for topologically sorting a module's -dependency list was very useful. - -### defsystem-4 proposal - -Marco and Peter's proposal for defsystem 4 served as the -driver for many of the features in here. Notable differences -are: - - * We don't specify output files or output file extensions - as part of the system. - -If you want to find out what files an operation would create, -ask the operation. - -* We don't deal with CL packages - -If you want to compile in a particular package, use an -in-package form in that file (ilisp / SLIME will like you -more if you do this anyway) - -* There is no proposal here that defsystem does version - control. - -A system has a given version which can be used to check -dependencies, but that's all. - -The defsystem 4 proposal tends to look more at the external -features, whereas this one centres on a protocol for system -introspection. - -### kmp's "The Description of Large Systems", MIT AI Memo 801 - -Available in updated for Common Lisp form [on the web][kmp-large]. - -In our implementation we borrow kmp's overall PROCESS-OPTIONS -and concept to deal with creating component trees from -defsystem surface syntax. [ this is not true right now, -though it used to be and probably will be again soon ]. - - -{remark ## Concept Index - - * [component][34]: [Components][12] - * [operation][35]: [Operations][9] - * [system][36]: [Components][12] - * [system designator][37]: [Components][12] - * [system directory designator][38]: [Using ASDF to load systems][2] - -* * * - -Next: [Variable Index][25], Previous: [Concept Index][23], Up: [Top][1] - -## Function and Class Index - - * [`compile-op`][39]: [Predefined operations of ASDF][10] - * [`feature-dependent-op`][40]: [Predefined operations of ASDF][10] - * [`find-system`][41]: [Components][12] - * [`load-op`][42]: [Predefined operations of ASDF][10] - * [`load-source-op`][43]: [Predefined operations of ASDF][10] - * [`module`][44]: [Pre-defined subclasses of component][14] - * [`oos`][45]: [Operations][9] - * [`operate`][46]: [Operations][9] - * [`OPERATION-ERROR`][47]: [Error handling][16] - * [`source-file`][48]: [Pre-defined subclasses of component][14] - * [`system`][49]: [Pre-defined subclasses of component][14] - * [`SYSTEM-DEFINITION-ERROR`][50]: [Error handling][16] - * [`test-system-version`][51]: [Predefined operations of ASDF][10] - -* * * - -Previous: [Function and Class Index][24], Up: [Top][1] - -## Variable Index - - * [`*central-registry*`][52]: [Using ASDF to load systems][2] - * [`*compile-file-errors-behavior*`][53]: [Compilation error and warning handling][17] - * [`*compile-file-warnings-behaviour*`][54]: [Compilation error and warning handling][17] - * [`*system-definition-search-functions*`][55]: [Components][12] - -* * * -} - -#### Footnotes - -{footnotes} - -* * * - - -</div> -{include resources/footer.md} -</div> - - - - diff --git a/website/source/proposals/cl-systems-path.mmd b/website/source/proposals/cl-systems-path.mmd deleted file mode 100644 index 03efa03e3a8ddd9d4f5f101fe73a908ed53089d2..0000000000000000000000000000000000000000 --- a/website/source/proposals/cl-systems-path.mmd +++ /dev/null @@ -1,96 +0,0 @@ - -# Proposal Summary - -These are independent but make the most sense if taken as a -whole. - - * Specify new defaults for `*central-registry*`, - - * use an environment variable named `CL_SYSTEMS_PATH` to - override ASDF's defaults, - - * use `*central-registry*` to find and load an ASDF - configuration file (if present) and - - * modify the default behavior of - `sysdef-central-registry-search` to search deeply. - -## default value for *central-registry* - -CL_SYSTEMS_PATH environment variable with syntax like - - path[:path]* (on *nix) - -or - - path[;path]* (on Windows) - -I.e., path designators separated by an OS dependent marker -(colons or semi-colons). This is used by ASDF to help build -the default value for `*central-registry*`. Each `path` will -be interpreted as follows: - -* a `!` will be replaced by the default value (see below) so - that it is easy to extend the path rather than just - replacing it. - -* empty ones will be removed from the list, - -* since these represent _directories_, trailing slashes will - be added if necessary, - -* if the last component is a `*` or `**` then sub-directories - of this path will be searched recursively either one level - deep (if `*`) or all the way down (if `**`). - -If not specified, `*central-registry*` defaults to (on -*nix-like systems): - - (list - *default-pathname-defaults* - (merge-pathnames - (make-pathname :directory '(:relative ".common-lisp")) - (user-homedir-pathname))) - (merge-pathnames - (make-pathname :directory '(:relative ".local" "share" -"common-lisp")) - (user-homedir-pathname))) - (make-pathname :directory '(:absolute "usr" "local" "share" -"common-lisp")) - (make-pathname :directory '(:absolute "usr" "share" "common- -lisp")) - ) - - -(On a windows system, the last two would be replaced by -c:\common-lisp and c:\windows\common-lisp). - -Note that this list would be automatically filtered for any -entries for which the moral equivalent of `(ignore-errors -(truename x))` fails. - -## modification for `sysdef-central-registry-search` - -Currently `sysdef-central-registry-search` searches in the -top-level of each directory in `*central-registry*` to find -systems. If we use the above, then a more useful approach -would be to search in the top-level and in a sub-directory -named `systems`. An even more useful approach would to search -in all sub-directories of each entry. - -## configuration file - -When ASDF starts, it will: - - 1. setup `*central-registry*` using its own defaults and the -`CL_SYSTEMS_PATH` environment variable. - - 2. look for and load the *first* file named -`asdf-config.lisp` in each of these paths traversed in order -(so that user settings take precedence over system ones). -Note that the ASDF package will exist when this file is read. - -I.e., I'm suggesting "if there are any user settings, then -ignore the system settings" one rather than the "user -settings overwrite system settings". - diff --git a/website/source/resources/footer.md b/website/source/resources/footer.md deleted file mode 100644 index 804c96c03a3127027a2131ce52e9274c1e02fbc5..0000000000000000000000000000000000000000 --- a/website/source/resources/footer.md +++ /dev/null @@ -1,13 +0,0 @@ -<div id="footer"> -<a class="nav" href="http://validator.w3.org/check/referer" title="xhtml1.1"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/xhtml.gif" width="80" height="15" title="valid xhtml button" alt="valid xhtml" /></a> -<a class="nav" href="http://common-lisp.net/project/cl-markdown/" title="Mark with CL-Markdown"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/cl-markdown.png" width="80" height="15" title="Made with CL-Markdown" alt="CL-Markdown" /></a> -<a class="nav" href="http://www.catb.org/hacker-emblem/" title="hacker"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/hacker.png" width="80" height="15" title="hacker emblem" alt="hacker button" /></a> -<a class="nav" href="http://www.lisp.org/" title="Association of Lisp Users"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lambda-lisp.png" width="80" height="15" title="ALU emblem" alt="ALU button" /></a> -<a class="nav" href="http://common-lisp.net/" title="Common-Lisp.net"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lisp-lizard.png" width="80" height="15" title="Common-Lisp.net" alt="Common-Lisp.net button" /></a> - -<span class="copyright">[Copyright][] (c) 2001-2009 Daniel Barlow and [contributors][]</span> - -ASDF has an [MIT style][mit-license] license - -<div id="timestamp">Last updated {today} at {now}</div> -</div> diff --git a/website/source/resources/header.md b/website/source/resources/header.md deleted file mode 100644 index 0d4109545b923419cccb620358b83d4486caf248..0000000000000000000000000000000000000000 --- a/website/source/resources/header.md +++ /dev/null @@ -1,14 +0,0 @@ -{include "shared-header.md"} - -<div class="header"> - <span class="logo"> - <a href="http://common-lisp.net/project/asdf/" title="ASDF Homepage"> - <img src="lisp-logo120x80.png" title="ASDF homepage" width="100" alt="Lisp Lizzard" /> - </a> - </span> - -## ASDF - -#### Definitely Definitive Definitions - -</div> diff --git a/website/source/resources/navigation.md b/website/source/resources/navigation.md deleted file mode 100644 index a734edfb8323a332f80e17bda321a6ef4af3d7c1..0000000000000000000000000000000000000000 --- a/website/source/resources/navigation.md +++ /dev/null @@ -1,2 +0,0 @@ -<div id="navigation"> -</div> diff --git a/website/source/resources/shared-header.md b/website/source/resources/shared-header.md deleted file mode 100644 index 230dadd5db0b197f2b9a49e3391da2f4d926c953..0000000000000000000000000000000000000000 --- a/website/source/resources/shared-header.md +++ /dev/null @@ -1,35 +0,0 @@ -{include "shared-links.md"} - -{set-property html yes} -{set-property author "the ASDF group"} -{set-property title "ASDF - Another System Definition Facility"} -{set-property style-sheet "style.css"} - - [user-guide]: user-guide.html - [tutorial]: user-guide.html - [manual]: manual.html - [Overview]: overview.html - [copyright]: copyright.html - [contributors]: contributors.html - [sys-guide]: manual.html - [getting-started]: getting-started.html - [FAQ]: faq.html - [tests]: test-results.html - [git-browse]: http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;a=summary - [git-resources]: git.html - - [asdf-devel]: http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel - [asdf-announce]: http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-announce - - [asdf-home]: http://common-lisp.net/project/asdf/ - [asdf.lisp]: http://common-lisp.net/project/asdf/asdf.lisp - [tarball]: http://common-lisp.net/project/asdf/asdf.tar.gz - - [kmp-large]: http://www.nhplace.com/kent/Papers/Large-Systems.html - - [hs-require]: http://www.lispworks.com/documentation/HyperSpec/Body/f_provid.htm#require - [hs-standard-output]: http://www.lispworks.com/documentation/HyperSpec/Body/v_debug_.htm#STstandard-outputST - - - [tinaa-project]: http://common-lisp.net/project/tinaa/ - [albert-project]: http://www.cliki.net/Albert/ \ No newline at end of file diff --git a/website/source/resources/ug-footer.md b/website/source/resources/ug-footer.md deleted file mode 100644 index cf3c6c210c7de821db4523101066fe6d8bcd31e2..0000000000000000000000000000000000000000 --- a/website/source/resources/ug-footer.md +++ /dev/null @@ -1,15 +0,0 @@ -<div id="footer"> -<div id="buttons"> - <a class="nav" href="http://validator.w3.org/check/referer" title="xhtml1.1"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/xhtml.gif" width="80" height="15" title="valid xhtml button" alt="valid xhtml" /></a> - <a class="nav" href="http://common-lisp.net/project/cl-markdown/" title="Mark with CL-Markdown"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/cl-markdown.png" width="80" height="15" title="Made with CL-Markdown" alt="CL-Markdown" /></a> - <a class="nav" href="http://www.catb.org/hacker-emblem/" title="hacker"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/hacker.png" width="80" height="15" title="hacker emblem" alt="hacker button" /></a> - <a class="nav" href="http://www.lisp.org/" title="Association of Lisp Users"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lambda-lisp.png" width="80" height="15" title="ALU emblem" alt="ALU button" /></a> - <a class="nav" href="http://common-lisp.net/" title="Common-Lisp.net"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lisp-lizard.png" width="80" height="15" title="Common-Lisp.net" alt="Common-Lisp.net button" /></a> -</div> - -<span id="copyright"> Copyright (c) 2001 - 2008 Gary Warren King (gwking@metabang.com)</span> -<span id="license-note">LIFT has an MIT style license</span> -<span id="timestamp">Last updated {today} at {now}</span> - -</div> - diff --git a/website/source/resources/ug-header.md b/website/source/resources/ug-header.md deleted file mode 100644 index 667d2bf6bb5cd35b8670f9f704139422eba336d6..0000000000000000000000000000000000000000 --- a/website/source/resources/ug-header.md +++ /dev/null @@ -1,14 +0,0 @@ -{include "shared-header.md"} - -{set-property style-sheets - "user-guide.css" - "http://common-lisp.net/project/cl-containers/shared/style-200.css"} - -<div id="header"> - <span class="logo"><a href="http://www.metabang.com/" title="metabang.com"><img src="http://common-lisp.net/project/cl-containers/shared/metabang-2.png" title="metabang.com" width="100" alt="Metabang Logo" /></a></span> - -## LIFT - -#### Fighting like cats with AK-47s - -</div> diff --git a/website/source/user-guide.css b/website/source/user-guide.css deleted file mode 100644 index 8b8bebfc3f0f9d980014fc4ebc28f308f44a99ed..0000000000000000000000000000000000000000 --- a/website/source/user-guide.css +++ /dev/null @@ -1,212 +0,0 @@ -/* @group toc */ - -.table-of-contents { - font-size: 90%; - margin-bottom: 1em; - padding-bottom: 1em; -} - -.table-of-contents h2, h3, h4 { - padding-top: 0; - padding-bottom: 0; - margin-top: 0; - margin-bottom: 1px; -} - -.table-of-contents h2 { - font-size: inherit; - font-style: inherit; - position: relative; - left: 2em; -} - -.table-of-contents h3 { - font-size: inherit; - font-style: inherit; - position: relative; - left: 4em; -} - -.table-of-contents h4 { - font-size: inherit; - font-style: inherit; - position: relative; - left: 6em; -} - -.table-of-contents h5 { - font-size: inherit; - font-style: inherit; - position: relative; - left: 8px; -} - -/* @end */ - -/* @group anchors */ - -a.none { - text-decoration: none; - color:black } - -a.none:visited { text-decoration: none; color:black } - -a.none:active { text-decoration: none; color:black } - -a.none:hover { text-decoration: none; color:black } - -a { - text-decoration: none; - } - -a:visited { - text-decoration: none; -} - -a:active { - text-decoration: underline; -} - -a:hover { - text-decoration: underline; -} - -/* @end */ - -/* @group Reference */ - -.reference { - padding-bottom: 1em; -} - -.reference h3 { - margin-top: 2em; - font-size: 110%; - border-bottom: 1px solid silver; - border-top: 4px solid gray; - padding-top: 3px; - padding-bottom: 3px; -} - - - -/* @end */ - -body { - font-family: Georgia, "Times New Roman", Times, serif; - margin-right: 0.5in; - margin-left: 0.5in; - margin-bottom: 0.25px; -} - -h1, h2, h3, h4 { - font-family: "Lucida Grande", Lucida, Verdana, sans-serif; -} - -h2 { -} - -h3, h4 { - font-style: italic; -} - -.hidden { - visibility: hidden; -} - -.documentation { - margin-right: 1em; - margin-left: 1em; -} - -.function { - -} - -.documentation.header { - display: block; - position: relative; - border: none white 0px; - border-top: thin; - border-top-style: solid; - border-top-width: 1pt; - padding-top: 4px; - margin-top: 2em; - font-size: inherit; - background-color: transparent; - color: black; -} - -.documentation.contents { - top: 10px; - position: relative; -} - -.documentation-name { - font-weight: bold; - float: left; - padding-right: 10px; - width: 125px; -} - -.documentation-kind { - float: right; - font-style: italic; - color: gray; - padding-left: 10px; -} - -.documentation-arguments { - float: left; - width: 350px; - font-style: italic; -} - -.documentation p { - clear: both; - margin-right: 1em; - margin-left: 1em; -} - -pre { - background-color: #ffc8ff; - overflow: auto; - padding-bottom: 5px; - margin-right: 1cm; - margin-left: 1cm; - font-family: "Courier New", Courier, mono; -} - -.note { - border: 2px inset gray; - padding: 0.5em; - margin-right: 2em; - margin-left: 2em; -} - -#footer { - margin-top: 2em; - border-top-style: inset; - border-top-width: 2px; -} -#navigation li { - display: inline; - border-right-style: dotted; - border-right-width: 1px; - border-left-style: dotted; - border-left-width: 1px; - border-collapse: collapse; - padding-right: 0.25em; - padding-left: 0.25em; - margin-right: 1em; -} - -#navigation { - text-align: center; -} - -#timestamp { - font-size: 80%; - text-align: right; -} - diff --git a/website/source/user-guide.mmd b/website/source/user-guide.mmd deleted file mode 100644 index cb8f13f64815cf1bbe7015a1637be550b034d548..0000000000000000000000000000000000000000 --- a/website/source/user-guide.mmd +++ /dev/null @@ -1,43 +0,0 @@ -{include resources/ug-header.md} -{set-property title "LIFT User's Guide"} -{set-property style-sheet user-guide} -{set-property docs-package lift} - -# ASDF User's Guide - -# Table of Contents - -{table-of-contents :start 2 :depth 3} - -## Introduction - -## Indices - -### Index of Functions - -{docs-index function} - -### Index of variables - -{docs-index variable} - -### Index of Macros - -{docs-index macro} - -### Full symbol index - -{docs-index :all} - -<hr> - -#### Glossary - -{glossary} - - -#### Footnotes - -{footnotes} - -{include resources/ug-footer.md} diff --git a/website/website.tmproj b/website/website.tmproj deleted file mode 100644 index 9ecfec7f712b3ddbd0f3e792e634243176d7c4d3..0000000000000000000000000000000000000000 --- a/website/website.tmproj +++ /dev/null @@ -1,347 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>currentDocument</key> - <string>source/getting-started.mmd</string> - <key>documents</key> - <array> - <dict> - <key>filename</key> - <string>source/getting-started.mmd</string> - <key>lastUsed</key> - <date>2009-09-27T12:25:04Z</date> - <key>selected</key> - <true/> - </dict> - <dict> - <key>filename</key> - <string>source/index.mmd</string> - <key>lastUsed</key> - <date>2009-09-13T18:47:21Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/git.mmd</string> - <key>lastUsed</key> - <date>2009-09-13T19:03:03Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/manual.mmd</string> - <key>lastUsed</key> - <date>2009-09-13T19:10:07Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/user-guide.mmd</string> - <key>lastUsed</key> - <date>2009-09-13T19:00:02Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/glossary.md</string> - <key>lastUsed</key> - <date>2009-09-27T12:25:04Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/user-guide.css</string> - </dict> - <dict> - <key>filename</key> - <string>source/copyright.md</string> - <key>lastUsed</key> - <date>2009-06-06T17:28:02Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/style.css</string> - <key>lastUsed</key> - <date>2009-06-10T00:54:20Z</date> - </dict> - <dict> - <key>filename</key> - <string>source/contributors.md</string> - <key>lastUsed</key> - <date>2009-06-06T17:26:46Z</date> - </dict> - <dict> - <key>expanded</key> - <true/> - <key>name</key> - <string>resources</string> - <key>regexFolderFilter</key> - <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string> - <key>sourceDirectory</key> - <string>source/resources</string> - </dict> - </array> - <key>fileHierarchyDrawerWidth</key> - <integer>183</integer> - <key>metaData</key> - <dict> - <key>source/contributors.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>19</integer> - </dict> - <key>columnSelection</key> - <false/> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - <key>selectFrom</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>selectTo</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>25</integer> - </dict> - </dict> - <key>source/copyright.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>29</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/getting-started.mmd</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>106</integer> - </dict> - <key>source/git.mmd</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>10</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/glossary.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>19</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/index.mmd</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>47</integer> - <key>line</key> - <integer>58</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>42</integer> - </dict> - <key>source/manual.mmd</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>59</integer> - </dict> - <key>source/resources/footer.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>58</integer> - <key>line</key> - <integer>1</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/resources/header.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>48</integer> - <key>line</key> - <integer>4</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/resources/navigation.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>1</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/resources/shared-header.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>2</integer> - <key>line</key> - <integer>19</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>14</integer> - </dict> - <key>source/resources/ug-footer.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>9</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/resources/ug-header.md</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>6</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/style.css</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - <key>source/user-guide.css</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>0</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>10</integer> - </dict> - <key>source/user-guide.mmd</key> - <dict> - <key>caret</key> - <dict> - <key>column</key> - <integer>0</integer> - <key>line</key> - <integer>8</integer> - </dict> - <key>firstVisibleColumn</key> - <integer>0</integer> - <key>firstVisibleLine</key> - <integer>0</integer> - </dict> - </dict> - <key>openDocuments</key> - <array> - <string>source/style.css</string> - <string>source/index.mmd</string> - <string>source/resources/ug-footer.md</string> - <string>source/resources/shared-header.md</string> - <string>source/copyright.md</string> - <string>source/resources/header.md</string> - <string>source/glossary.md</string> - <string>source/getting-started.mmd</string> - <string>source/manual.mmd</string> - <string>source/git.mmd</string> - <string>source/user-guide.mmd</string> - <string>source/resources/navigation.md</string> - <string>source/contributors.md</string> - <string>source/resources/footer.md</string> - </array> - <key>showFileHierarchyDrawer</key> - <true/> - <key>windowFrame</key> - <string>{{555, 123}, {578, 578}}</string> -</dict> -</plist>