Handle run-program with :directory nil.
[sbcl.git] / src / code / module.lisp
index 83f129d..a6b47b4 100644 (file)
@@ -22,9 +22,8 @@
   "This is a list of module names that have been loaded into Lisp so far.
    It is used by PROVIDE and REQUIRE.")
 
-(defvar sb!ext::*MODULE-PROVIDER-FUNCTIONS* '(module-provide-contrib)
-  "See function documentation for REQUIRE")
-
+(defvar *module-provider-functions* (list 'module-provide-contrib)
+  "See function documentation for REQUIRE.")
 \f
 ;;;; PROVIDE and REQUIRE
 
   (pushnew (string module-name) *modules* :test #'string=)
   t)
 
+(defvar *requiring* nil)
+
+(defun require-error (control &rest arguments)
+  (error 'extension-failure
+         :format-control control
+         :format-arguments arguments
+         :references
+         (list
+          '(:sbcl :variable *module-provider-functions*)
+          '(:sbcl :function require))))
+
 (defun require (module-name &optional pathnames)
   #!+sb-doc
   "Loads a module, unless it already has been loaded. PATHNAMES, if supplied,
    is a designator for a list of pathnames to be loaded if the module
    needs to be. If PATHNAMES is not supplied, functions from the list
    *MODULE-PROVIDER-FUNCTIONS* are called in order with MODULE-NAME
-   as an argument, until one of them returns non-NIL."
-  (let ((saved-modules (copy-list *modules*)))
-    (unless (member (string module-name) *modules* :test #'string=)
-      (cond (pathnames
-            (unless (listp pathnames) (setf pathnames (list pathnames)))
-            ;; ambiguity in standard: should we try all pathnames in the
-            ;; list, or should we stop as soon as one of them calls PROVIDE?
-            (dolist (ele pathnames t)
-              (load ele))
-             ;; should we do this?  Probably can't hurt, while we're
-             ;; taking the above view of "load everything"...  though
-             ;; maybe having REQUIRE directly call PROVIDE is
-             ;; aesthetically suboptimal.
-            (provide module-name))
-           (t
-            (unless (some (lambda (p) (funcall p module-name))
-                          sb!ext::*module-provider-functions*)
-              (error "Don't know how to load ~A" module-name)))))
-    (set-difference *modules* saved-modules)))
+   as an argument, until one of them returns non-NIL.  User code is
+   responsible for calling PROVIDE to indicate a successful load of the
+   module."
+  (let ((name (string module-name)))
+    (when (member name *requiring* :test #'string=)
+      (require-error "~@<Could not ~S ~A: circularity detected. Please check ~
+                     your configuration.~:@>" 'require module-name))
+    (let ((saved-modules (copy-list *modules*))
+          (*requiring* (cons name *requiring*)))
+      (unless (member name *modules* :test #'string=)
+        (cond (pathnames
+               (unless (listp pathnames) (setf pathnames (list pathnames)))
+               ;; ambiguity in standard: should we try all pathnames in the
+               ;; list, or should we stop as soon as one of them calls PROVIDE?
+               (dolist (ele pathnames t)
+                 (load ele)))
+              (t
+               (unless (some (lambda (p) (funcall p module-name))
+                             *module-provider-functions*)
+                 (require-error "Don't know how to ~S ~A."
+                                'require module-name)))))
+      (set-difference *modules* saved-modules))))
+
 \f
 ;;;; miscellany
 
 (defun module-provide-contrib (name)
   "Stringify and downcase NAME, then attempt to load the file
    $SBCL_HOME/name/name"
-  (let ((filesys-name (string-downcase (string name))))
-    (load
-     (merge-pathnames (make-pathname :directory (list :relative filesys-name)
-                                    :name filesys-name)
-                     (truename (posix-getenv "SBCL_HOME")))))
-  (provide name)
-  t)
-
-
+  (let* ((filesys-name (string-downcase (string name)))
+         (unadorned-path
+          (merge-pathnames
+           (make-pathname :directory (list :relative "contrib")
+                          :name filesys-name)
+           (truename (or (sbcl-homedir-pathname)
+                         (return-from module-provide-contrib nil)))))
+         (fasl-path (merge-pathnames
+                     (make-pathname :type *fasl-file-type*)
+                     unadorned-path))
+         (lisp-path (merge-pathnames (make-pathname :type "lisp")
+                                     unadorned-path)))
+    ;; KLUDGE: there's a race condition here; the file we probe could
+    ;; be removed by the time we get round to trying to load it.
+    ;; Maybe factor out the logic in the LOAD guesser as to which file
+    ;; was meant, so that we can use it here on open streams instead?
+    (let ((file (or (probe-file fasl-path)
+                    (probe-file unadorned-path)
+                    (probe-file lisp-path))))
+      (when file
+        (handler-bind
+            (((or style-warning sb!int:package-at-variance) #'muffle-warning))
+          (load file))
+        t))))