fe3b7652987606dce2b307ea25e234eb5bbf7326
[sbcl.git] / src / code / module.lisp
1 ;;;; REQUIRE, PROVIDE, and friends
2 ;;;;
3 ;;;; Officially these are deprecated, but in practice they're probably
4 ;;;; even less likely to actually go away than there is to ever be
5 ;;;; another revision of the standard.
6
7 ;;;; This software is part of the SBCL system. See the README file for
8 ;;;; more information.
9 ;;;;
10 ;;;; This software is derived from the CMU CL system, which was
11 ;;;; written at Carnegie Mellon University and released into the
12 ;;;; public domain. The software is in the public domain and is
13 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
14 ;;;; files for more information.
15
16 (in-package "SB!IMPL")
17 \f
18 ;;;; exported specials
19
20 (defvar *modules* ()
21   #!+sb-doc
22   "This is a list of module names that have been loaded into Lisp so far.
23    It is used by PROVIDE and REQUIRE.")
24
25 (defvar *module-provider-functions* (list 'module-provide-contrib)
26   "See function documentation for REQUIRE.")
27 \f
28 ;;;; PROVIDE and REQUIRE
29
30 (defun provide (module-name)
31   #!+sb-doc
32   "Adds a new module name to *MODULES* indicating that it has been loaded.
33    Module-name is a string designator"
34   (pushnew (string module-name) *modules* :test #'string=)
35   t)
36
37 (defvar *requiring* nil)
38
39 (defun require-error (control &rest arguments)
40   (error 'extension-failure
41          :format-control control
42          :format-arguments arguments
43          :references
44          (list
45           '(:sbcl :variable *module-provider-functions*)
46           '(:sbcl :function require))))
47
48 (defun require (module-name &optional pathnames)
49   #!+sb-doc
50   "Loads a module, unless it already has been loaded. PATHNAMES, if supplied,
51    is a designator for a list of pathnames to be loaded if the module
52    needs to be. If PATHNAMES is not supplied, functions from the list
53    *MODULE-PROVIDER-FUNCTIONS* are called in order with MODULE-NAME
54    as an argument, until one of them returns non-NIL.  User code is
55    responsible for calling PROVIDE to indicate a successful load of the
56    module."
57   (let ((name (string module-name)))
58     (when (member name *requiring* :test #'string=)
59       (require-error "~@<Could not ~S ~A: circularity detected. Please check ~
60                      your configuration.~:@>" 'require module-name))
61     (let ((saved-modules (copy-list *modules*))
62           (*requiring* (cons name *requiring*)))
63       (unless (member name *modules* :test #'string=)
64         (cond (pathnames
65                (unless (listp pathnames) (setf pathnames (list pathnames)))
66                ;; ambiguity in standard: should we try all pathnames in the
67                ;; list, or should we stop as soon as one of them calls PROVIDE?
68                (dolist (ele pathnames t)
69                  (load ele)))
70               (t
71                (unless (some (lambda (p) (funcall p module-name))
72                              *module-provider-functions*)
73                  (require-error "Don't know how to ~S ~A."
74                                 'require module-name)))))
75       (set-difference *modules* saved-modules))))
76
77 \f
78 ;;;; miscellany
79
80 (defun module-provide-contrib (name)
81   "Stringify and downcase NAME, then attempt to load the file
82    $SBCL_HOME/name/name"
83   (let* ((filesys-name (string-downcase (string name)))
84          (unadorned-path
85           (merge-pathnames
86            (make-pathname :directory (list :relative filesys-name)
87                           :name filesys-name)
88            (truename (or (sbcl-homedir-pathname)
89                          (return-from module-provide-contrib nil)))))
90          (fasl-path (merge-pathnames
91                      (make-pathname :type *fasl-file-type*)
92                      unadorned-path))
93          (lisp-path (merge-pathnames (make-pathname :type "lisp")
94                                      unadorned-path)))
95     ;; KLUDGE: there's a race condition here; the file we probe could
96     ;; be removed by the time we get round to trying to load it.
97     ;; Maybe factor out the logic in the LOAD guesser as to which file
98     ;; was meant, so that we can use it here on open streams instead?
99     (when (or (probe-file unadorned-path)
100               (probe-file fasl-path)
101               (probe-file lisp-path))
102       (load unadorned-path)
103       t)))