load-asdf.lisp 12.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
;;; The code below exemplifies how to load and configure ASDF
;;; as part of your own deterministic build.
;;; See "User-configurable parts" for where you'd customize it to suit your build.
;;;
;;; We have to play games with packages because on some implementations,
;;; an ASDF upgrade from ASDF 2 can throw away the previous package.
;;;
;;; Everything is MUCH simpler if you can assume your implementation has a recent-enough ASDF 3:
;;; see the commented out alternative below.
;;;
;;; To use the user-configured ASDF rather than a deterministic self-contained project build,
;;; see instead how cl-launch 4.0.4 loads ASDF.
;;; Actually, if you can assume that your implementation or distribution provides ASDF 3,
;;; you may simply use cl-launch, and achieve a deterministic self-contained project build
;;; by having your shell configuration or some shell wrapper script export a proper values
;;; for the CL_SOURCE_REGISTRY and ASDF_OUTPUT_TRANSLATIONS environment variables.

(in-package :cl-user) ;; That may be default, but let's make double sure and tell SLIME.

;; Do everything in eval-when, so this works
;; whether this file is being loaded directly or compiled first.
(eval-when (:compile-toplevel :load-toplevel :execute)
  (labels ((asdf-symbol (name)
             (and (find-package :asdf) (find-symbol (string name) :asdf)))
           (asdf-call (name &rest args)
             (apply (asdf-symbol name) args))
           (asdf-version ()
             (when (find-package :asdf)
               (or (symbol-value (or (asdf-symbol '*asdf-version*)
                                     (asdf-symbol '*asdf-revision*)))
                   "1.0")))
           (subpath (parent &key directory name type version)
             ;; We need subpath here, because we can't yet assume ASDF 3 and its subpathname
             (merge-pathnames (make-pathname :defaults parent
                                             :directory (cons :relative directory)
                                             :name name :type type :version version)
37
                              (make-pathname :name nil :type nil :version nil :defaults parent)))
38 39 40
           (here-directory ()
             (subpath (or *compile-file-truename* *load-truename*
                          (truename *default-pathname-defaults*))))
41 42
           (try-load (x)
             (ignore-errors (and (probe-file x) (load x))))
43 44
           (load-and-configure-asdf ()
             ;; First, try to require ASDF from the implementation, if not already loaded.
45 46 47
             ;; All actively maintained implementations now provide ASDF 3.0;
             ;; some old versions of maintained implementations, and some unmaintained
             ;; or obsolete implementations only provide ASDF 2, ASDF 1, or don't provide ASDF.
48
             ;; Note that CLISP is case-sensitive, so we need to specify a lowercase string,
49 50
             ;; and not the keyword :asdf or symbol 'asdf; also old CLISP versions that don't
             ;; provide ASDF may error at compile-time if we call (require "asdf") directly.
51 52 53 54 55
             (ignore-errors (funcall 'require "asdf"))
             ;; If ASDF 2 isn't provided, load our ASDF from source.
             ;; ASDF 1 is not enough, because it won't heed our project's output-translations.
             ;; (Beside, no one serious provides ASDF 1 anymore.)
             (unless (member :asdf2 *features*)
56 57
               (or (and (try-load (asdf-lisp)) (member :asdf2 *features*))
                   (error "This Lisp implementation fails to provide ASDF 2 or later. ~
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
                           Please install it in ~A" (asdf-lisp))))
             ;; Configure ASDF
             (configure-asdf)
             (let ((provided-version (asdf-version)))
               ;; Upgrade ASDF to what we configured it to be.
               (asdf-call 'load-system :asdf)
               ;; If the implementation-provided version was too old,
               ;; we need to re-configure, because old configuration may have been moved away.
               (unless (asdf-call 'version-satisfies provided-version "2.27")
                 (configure-asdf)))
             (unless (asdf-call 'version-satisfies (asdf-version) (required-asdf-version))
               (error "This program needs ASDF ~A but could only find ASDF ~A"
                      (required-asdf-version) (asdf-version)))
             ;; Here, we specifically want the ASDF in the current git checkout over
             ;; whatever quicklisp is providing, so we load quicklisp last.
             ;; If the checkout weren't providing ASDF and we wanted to rely on Quicklisp
             ;; to provide a copy of ASDF that the implementation might be lacking,
             ;; we'd move this form right below the (funcall 'require "asdf") above.
             ;; See also notes in try-load-quicklisp.
             (try-load-quicklisp))
78 79
           ;; User-configurable parts
           (required-asdf-version () "3.1.2") ;; In the end, we want at least ASDF 3.1.2
80
           (asdf-lisp ()
81 82 83 84 85
               ;; Here, define where your Lisp source code hierarchy stores its copy of ASDF.
               ;; In your project, that might be :directory '("libraries" "asdf" "build")
               ;; Or NIL, if you don't do use any fancy ASDF feature, and
               ;; trust your implementation to provide a recent enough copy.
               (subpath (here-directory) :directory '(:back "build") :name "asdf" :type "lisp"))
86 87 88 89 90 91 92 93 94
           (try-load-quicklisp ()
             ;; In a controlled environment, either you'd use your own quicklisp
             ;; instead of the one in the user's homedir, or you wouldn't use quicklisp at all.
             ;; Edit this function as desired to reflect that and/or remove the call above.
             ;; Also, if you rely on quicklisp to load a recent ASDF rather than provide it
             ;; yourself in your code checkout (see above), you should be using the less portable
             ;; (merge-pathnames "..." (user-homedir-pathname)) rather than subpathname.
             (or (try-load (asdf-call 'subpathname (user-homedir-pathname) "quicklisp/setup.lisp"))
                 (try-load (asdf-call 'subpathname (user-homedir-pathname) ".quicklisp/setup.lisp"))))
95 96 97 98 99 100 101 102 103 104 105 106 107 108
           (configure-asdf ()
             (let* ((source-directory
                      ;; Here, define the top of your Lisp source code hierarchy.
                      ;; If you can assume an implementation that has ASDF 2 or later
                      ;; (you should: all serious ones do), you might compute it based on
                      ;;   (asdf-call 'getenv "MY_PROJECT_ROOT") instead of (here-directory).
                      ;; If you can assume an implementation that has ASDF 3 or later
                      ;; (you probably can: most serious ones do), you might use instead
                      ;;   (asdf-call 'getenv-pathname "MY_PROJECT_ROOT"
                      ;;     :want-absolute t :ensure-directory t)
                      (subpath (here-directory) :directory '(:back)))
                    (source-registry
                      (or (asdf-call 'getenv "ASDF_DEVEL_SOURCE_REGISTRY")
                          `(:source-registry
109 110 111 112
                            (:directory ,source-directory)
                            (:directory (,source-directory "uiop"))
                            (:directory (,source-directory "tools"))
                            (:tree (,source-directory "ext"))
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
                            ;; In a fully controlled build, you'd :ignore-inherited-configuration instead:
                            :inherit-configuration)))
                    (output-directory
                      ;; There again, you might want to use some getenv variant.
                      ;; Also, "fasls" might be redundant for your project.
                      (subpath source-directory :directory '("build" "fasls")))
                    (output-translations
                      `(:output-translations
                        ;; Segregate output by ABI.
                        ;; You could replace "asdf" below by the name of your project,
                        ;; or not need it at all if everything is under your source-directory.
                        (,source-directory (,output-directory :implementation "asdf"))
                        ;; In a fully controlled build, we shouldn't be using code outside
                        ;; our source-directory, but in case we do, we still want to control the output,
                        ;; and easily detect the fact by looking at this directory
                        (t (,output-directory :implementation "root"))
                        ;; The above should already cover all paths that we use;
                        ;; we don't want user configuration to interfere with the build.
                        :ignore-inherited-configuration)))
               ;; No more user-configurable parts below.
               (asdf-call 'initialize-source-registry source-registry)
               (asdf-call 'initialize-output-translations output-translations))))
    ;; Configure the printer
    (setf *print-readably* nil ; allegro 5.0 may bork without this
          *print-level* nil)
    ;; Hush the compiler and loader
    (setf *load-verbose* nil *load-print* nil
          *compile-verbose* nil *compile-print* nil)
    ;; Load and configure ASDF
    (load-and-configure-asdf)))

#|
;;; Here is the much simpler code when you can assume
;;; your implementation provides ASDF 3 (i.e. at least pre-release ASDF 2.27).
;;; No package madness.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require "asdf"))

#-asdf3 (error "Your implementation fails to provide ASDF 3")

(in-package :asdf)

(eval-when (:compile-toplevel :load-toplevel :execute)
157
  (let* ((here-directory
158 159 160
           (pathname-directory-pathname
            (or *compile-file-truename* *load-truename*
                (truename *default-pathname-defaults*))))
161 162
         ;; User-configurable parts start here
         (required-asdf-version "3.1.2") ;; In the end, we want at least ASDF 3.1.2
163 164 165 166 167 168 169 170 171 172 173 174 175
         (source-directory
           ;; Here, define the top of your source code hierarchy.
           ;; For your project, it could be something like
           ;;   (or (getenv-pathname "MY_PROJECT_ROOT"
           ;;        :want-absolute t :ensure-directory t)
           ;;       (subpathname here-directory "../../"))
           (subpathname here-directory "../"))
         (source-registry
           ;; Here, define your source registry.
           ;; For your project, it would be based solely on source-directory above,
           ;; and in a fully controlled build, you'd :ignore-inherited-configuration
                  (or (getenvp "ASDF_DEVEL_SOURCE_REGISTRY")
                      `(:source-registry
176 177 178 179 180
                        (:directory ,source-directory)
                        (:directory (,source-directory "uiop"))
                        (:directory (,source-directory "tools"))
                        (:tree (,source-directory "ext"))
                        ;; In a fully controlled build, you'd :ignore-inherited-configuration instead:
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
                        :inherit-configuration)))
         (output-directory
           ;; There again, you might want to use some getenvp variant.
           ;; Also, "fasls" might be redundant for your project.
           (subpathname source-directory "build/fasls/"))
         (output-translations
           `(:output-translations
             ;; Segregate output by ABI.
             ;; You could replace "asdf" below by the name of your project,
             ;; or not need it at all if everything is under your source-directory.
             (,source-directory (,output-directory :implementation "asdf"))
             ;; In a fully controlled build, we shouldn't be using code outside
             ;; our source-directory, but in case we do, we still want to control the output,
             ;; and easily detect the fact by looking at this directory
             (t (,output-directory :implementation "root"))
             ;; The above should already cover all paths that we use;
             ;; we don't want user configuration to interfere with the build.
             :ignore-inherited-configuration)))
         ;; No more user-configurable parts below.
    ;; Configure the printer
    (setf *print-readably* nil ; allegro 5.0 may bork without this
          *print-level* nil)
    ;; Hush the compiler and loader
    (setf *load-verbose* nil *load-print* nil
          *compile-verbose* nil *compile-print* nil)
    ;; Configure ASDF
    (initialize-source-registry source-registry)
    (initialize-output-translations output-translations)
    ;; Upgrade to the latest configured version
    (upgrade-asdf)
211 212 213 214
    ;; Load Quicklisp --- see remarks in uncommented version above
    (if-let (x (or (probe-file (subpathname (user-homedir-pathname) "quicklisp/setup.lisp"))
                   (probe-file (subpathname (user-homedir-pathname) ".quicklisp/setup.lisp"))))
      (load x))
215 216 217 218
    ;; Check that we have a satisfactorily version
    (unless (version-satisfies (asdf-version) required-asdf-version)
      (error "Please install an ASDF ~A or later" required-asdf-version))))
|#