package.lisp 18.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
;;;; ---------------------------------------------------------------------------
;;;; Handle ASDF package upgrade, including implementation-dependent magic.
;;
;; See https://bugs.launchpad.net/asdf/+bug/485687
;;
;; CAUTION: we must handle the first few packages specially for hot-upgrade.
;; asdf/package will be frozen as of 2.27
;; to forever export the same exact symbols.
;; Any other symbol must be import-from'ed
;; and reexported in a different package
;; (alternatively the package may be dropped & replaced by one with a new name).

(defpackage :asdf/package
  (:use :common-lisp)
  (:export
16 17 18
   #:find-package* #:find-symbol* #:intern* #:unintern*
   #:symbol-name-package #:package-data
   #:delete-package* #:ensure-package #:define-package))
19 20 21

(in-package :asdf/package)

22 23 24 25 26
(declaim (optimize (speed 0) (safety 3) (debug 3)))

(defmacro DBG (tag &rest exprs)
  "simple debug statement macro:
outputs a tag plus a list of variable and their values, returns the last value"
27
  ;;"if not in debugging mode, just compute and return last value"
28 29 30 31 32 33 34 35 36 37 38 39 40
  #-DBGXXX (declare (ignore tag)) #-DBGXXX (car (last exprs)) #+DBGXXX
  (let ((res (gensym))(f (gensym)))
  `(let (,res (*print-readably* nil))
    (flet ((,f (fmt &rest args) (apply #'format *error-output* fmt args)))
      (fresh-line *standard-output*) (fresh-line *trace-output*) (fresh-line *error-output*)
      (,f "~&~A~%" ,tag)
      ,@(mapcan
         #'(lambda (x)
            `((,f "~&  ~S => " ',x)
              (,f "~{~S~^ ~}~%" (setf ,res (multiple-value-list ,x)))))
         exprs)
      (apply 'values ,res)))))

41 42 43
;;;; General purpose package utilities

(eval-when (:load-toplevel :compile-toplevel :execute)
44 45 46 47 48 49 50
  (defun find-package* (package-designator &optional (error t))
    (let ((package (find-package package-designator)))
      (cond
        (package package)
        (error (error "No package named ~S" (string package-designator)))
        (t nil))))
  (defun find-symbol* (name package-designator &optional (error t))
51 52 53 54 55 56
    "Find a symbol in a package of given string'ified NAME;
unless CL:FIND-SYMBOL, work well with 'modern' case sensitive syntax
by letting you supply a symbol or keyword for the name;
also works well when the package is not present.
If optional ERROR argument is NIL, return NIL instead of an error
when the symbol is not found."
57 58 59 60 61 62 63 64 65
    (block nil
      (let ((package (find-package* package-designator error)))
        (when package
          (multiple-value-bind (symbol status) (find-symbol (string name) package)
            (cond
              (status (return (values symbol status)))
              (error (error "There is no symbol ~S in package ~S" name (package-name package))))))
        (values nil nil))))
  (defun intern* (name package-designator &optional (error t))
66
    (intern (string name) (find-package* package-designator error)))
67 68 69 70 71 72 73 74 75 76 77
  (defun unintern* (name package-designator &optional (error t))
    (block nil
      (let ((package (find-package* package-designator error)))
        (when package
          (multiple-value-bind (symbol status) (find-symbol* name package error)
            (cond
              (status (unintern symbol package)
                      (return (values symbol status)))
              (error (error "symbol ~A not present in package ~A"
                            (string symbol) (package-name package))))))
        (values nil nil))))
78 79 80 81 82
  (defun symbol-name-package (symbol)
    (cons (symbol-name symbol) (package-name (symbol-package symbol))))
  (defun package-names (package)
    (cons (package-name package) (package-nicknames package)))
  (defun package-data (package-designator &key name-package (error t))
83 84
    (let ((package (find-package* package-designator error)))
      (when package
85 86 87 88
        (labels ((marshall-symbols (symbols)
                   (if name-package (mapcar #'symbol-name-package symbols) symbols))
                 (sort-symbols (symbols)
                   (marshall-symbols (sort symbols #'string<)))
89
                 (sort-packages (packages)
90
                   (sort (mapcar #'package-name packages) #'string<)))
91 92 93 94 95 96 97 98 99 100 101
          (loop :with internal :with external :with inherited
                :for sym :being :the :symbols :in package
                :for status = (nth-value 1 (find-symbol* sym package)) :do
                  (ecase status
                    (:internal (push sym internal))
                    (:external (push sym external))
                    (:inherited (push sym inherited)))
                :finally
                   (return
                     `(:name ,(package-name package)
                       :nicknames ,(package-nicknames package)
102 103 104 105
                       :internal ,(sort-symbols internal)
                       :external ,(sort-symbols external)
                       :inherited ,(sort-symbols inherited)
                       :shadowing ,(sort-symbols (package-shadowing-symbols package))
106 107
                       :use ,(sort-packages (package-use-list package))
                       :used-by ,(sort-packages (package-used-by-list package))))))))))
108 109 110 111 112 113 114

(eval-when (:load-toplevel :compile-toplevel :execute)
  (defun soft-upgrade-p (upgrade)
    (ecase upgrade ((:soft nil) t) (:hard nil)))
  (defun ensure-package-unused (package)
    (loop :for p :in (package-used-by-list package) :do
      (unuse-package package p)))
115
  (defun delete-package* (package)
116 117 118 119
    (let ((p (find-package package)))
      (when p
        (ensure-package-unused p)
        (delete-package package))))
120 121
  (defun ensure-package-fmakunbound (package symbols)
    (loop :for name :in symbols
122
          :for sym = (find-symbol* name package nil)
123 124 125
          :when sym :do (fmakunbound sym)))
  (defun ensure-package-fmakunbound-setf (package symbols)
    (loop :for name :in symbols
126
          :for sym = (find-symbol* name package nil)
127 128
          :when sym :do #-gcl (fmakunbound `(setf ,sym))))
  (defun ensure-package (name &key
129 130 131 132 133 134
                                upgrade
                                nicknames documentation use
                                shadow shadowing-import-from
                                import-from export intern
                                recycle mix reexport
                                unintern fmakunbound fmakunbound-setf)
135 136 137 138 139
    (DBG :ensure-package name nicknames upgrade documentation use
         shadow shadowing-import-from
         import-from export intern
         recycle mix reexport
         unintern fmakunbound fmakunbound-setf)
140 141 142 143 144 145 146 147
    (let* ((nicknames (mapcar #'string nicknames))
           (shadow (mapcar #'string shadow))
           (shadowing-import-from (loop :for sif :in shadowing-import-from
                                        :collect (mapcar #'string sif)))
           (import-from (loop :for if :in import-from
                              :collect (mapcar #'string if)))
           (export (mapcar #'string export))
           (recycle (remove nil (mapcar #'find-package recycle)))
148 149 150 151
           (shadowed (make-hash-table :test 'equal)) ; string to bool
           (imported (make-hash-table :test 'equal)) ; string to bool
           (exported (make-hash-table :test 'equal)) ; string to bool
           (inherited (make-hash-table :test 'equal)) ; string to package name
152 153
           (name (string name))
           (nicknames (mapcar #'string nicknames))
154
           (names (cons name nicknames))
155
           (previous (remove-duplicates (remove nil (mapcar #'find-package names)) :from-end t))
156
           (discarded (cdr previous))
157
           (package (DBG :xxx names previous discarded (or (first previous) (make-package name :nicknames nicknames)))))
158 159 160 161 162 163
      (labels
          ((ensure-shadowing-import (sym p)
             (let* ((name (string sym))
                    (i (find-symbol* name p)))
               (cond
                 ((gethash name shadowed)
164
                  (unless (eq i (find-symbol* name package))
165 166 167
                    (error "Conflicting shadowings for ~A" name)))
                 (t
                    (setf (gethash name shadowed) t)
168
                    (setf (gethash name imported) t)
169 170 171 172
                    (shadowing-import package)))))
           (ensure-import (sym p)
             (let* ((name (string sym))
                    (i (find-symbol* name p)))
173
               (multiple-value-bind (x xp) (find-symbol name package)
174
                 (cond
175 176
                   ((gethash name imported)
                    (unless (eq i x)
177
                      (error "Can't import ~S from both ~S and ~S"
178
                             name (package-name (symbol-package x)) (package-name p))))
179 180 181
                   ((gethash name shadowed)
                    (error "Can't both shadow ~S and import it from ~S" name (package-name p)))
                   (t
182 183 184 185
                    (when (and xp (not (eq i x)))
                      (unintern* x package))
                    (setf (gethash name imported) t)
                    (import i package))))))
186 187
           (ensure-mix (sym p)
             (let* ((name (string sym))
188 189 190
                    (sp (string p)))
               (unless (or (gethash name shadowed) (gethash name imported))
                 (let ((ip (gethash name inherited)))
191
                   (cond
192 193
                     ((eq sp ip))
                     (ip
194
                      (remhash name inherited)
195
                      (ensure-shadowing-import name ip))
196
                     (t
197
                      (ensure-inherited sym sp)))))))
198 199
           (ensure-inherited (sym p)
             (let* ((name (string sym))
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
                    (sp (string p))
                    (s (find-symbol* name sp))
                    (ip (gethash name inherited)))
               (multiple-value-bind (x xp) (find-symbol name package)
                 (cond
                   (ip
                    (unless (eq ip sp)
                      (error "Can't inherit ~S from ~S, it is inherited from ~S"
                             name sp ip)))
                   ((gethash name imported)
                    (unless (eq s x)
                      (error "Can't inherit ~S from ~S, it is imported from ~S"
                             name sp (package-name (symbol-package x)))))
                   ((gethash name shadowed)
                    (error "Can't inherit ~S from ~S, it is shadowed" name sp))
                   (t
                    (when xp
                      (unintern* x package)))))))
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
           (recycle-symbol (name)
             (loop :for r :in recycle
                   :for s = (find-symbol* name r nil)
                   :when s :do (return (values s r))))
           (symbol-recycled-p (sym)
             (loop :for r :in recycle
                   :thereis (multiple-value-bind (s sp) (find-symbol* sym r nil) (and sp (eq sym s)))))
           (ensure-symbol (name &optional intern)
             (unless (or (gethash name shadowed)
                         (gethash name imported)
                         (gethash name inherited))
               (multiple-value-bind (recycled previous) (recycle-symbol name)
                 (cond
                   ((eq previous package))
                   ((or (not previous) (not (member (symbol-package recycled) recycle)))
                    (when intern (intern* name package)))
234
                   (t (unintern* name package nil) (unintern* recycled previous) (import recycled package))))))
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
           (ensure-export (name p)
             (multiple-value-bind (symbol status) (find-symbol name p)
               (assert status)
               (unless (eq status :external)
                 (ensure-exported name symbol p))))
           (ensure-exported (name sym p)
             (dolist (u (package-used-by-list p))
               (ensure-exported-to-user name sym u))
             (export sym p))
           (ensure-exported-to-user (name sym u)
             (multiple-value-bind (usym ustat) (find-symbol name u)
               (unless (eq sym usym)
                 (let ((shadowing (member usym (package-shadowing-symbols u))))
                   (block nil
                     (cond
                       ((not shadowing)
                        (unintern usym u))
                       ((symbol-recycled-p usym)
                        (shadowing-import sym u))
                       (t (return)))
                     (when (eq ustat :external)
                       (ensure-exported name sym u))))))))
        (assert (soft-upgrade-p upgrade))
        (setf (documentation package t) documentation)
        ;;#+DBG (untrace)(trace find-symbol make-package delete-package use-package unuse-package import export intern shadow shadowing-import unintern unexport)
        (DBG :names names package previous discarded (package-data package :name-package t))
        (loop :for p :in discarded
              :for n = (remove-if #'(lambda (x) (member x names :test 'equal))
                                  (package-names p))
264
              :do (DBG :baz (package-names p) n)
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
              :do (if n (rename-package discarded (first n) (rest n))
                      (delete-package* discarded)))
        (rename-package package name nicknames)
        (DBG :unuse)
        (loop :for p :in (set-difference (package-use-list package) (append mix use))
              :do (unuse-package p package))
        (DBG :unintern)
        (dolist (name unintern) (unintern* name package nil))
        (DBG :export?)
        (loop :for sym :in export :for name = (string sym) :do
          (setf (gethash name exported) t))
        (DBG :reexport)
        (loop :for p :in reexport :do
          (do-external-symbols (sym p)
            (let ((name (string sym)))
              (export (find-symbol* name package) package) (setf (gethash name exported) t))))
        (DBG :unexport)
        (do-external-symbols (sym package)
          (unless (gethash (symbol-name sym) exported) (unexport sym package)))
        (DBG :shadow)
        (loop :for s :in shadow :for name = (string s) :do
          (DBG :sha name)
          (setf (gethash name shadowed) t)
          (multiple-value-bind (recycled previous) (recycle-symbol name)
            (cond
              ((or (not previous) (not (member (symbol-package recycle) recycle)))
               (ecase (nth-value 1 (find-symbol* name package nil))
                 ((nil :inherited) (shadow name package))
                 ((:internal :external) (shadowing-import (make-symbol name) package))))
              ((eq previous package) (shadow recycled package))
              (t (unintern* recycled previous) (shadowing-import recycled package)))))
296
        (loop :for (p . syms) :in shadowing-import-from :do
297
          (DBG :shaif p syms)
298 299
          (dolist (sym syms) (ensure-shadowing-import sym p)))
        (loop :for p :in mix :do
300
          (DBG :mix p)
301 302
          (do-external-symbols (sym p) (ensure-mix sym p)))
        (loop :for (p . syms) :in import-from :do
303
          (DBG :if p syms)
304
          (dolist (sym syms) (ensure-import sym p)))
305 306 307 308
        (loop :for p :in use :for sp = (string p) :for pp = (find-package sp) :do
          (DBG :use p sp pp)
          (do-external-symbols (sym pp) (ensure-inherited sym sp))
          (use-package pp package))
309 310 311 312 313 314
        (DBG :intern)
        (loop :for name :being :the :hash-keys :of exported :do
          (ensure-symbol name t))
        (dolist (name (append intern fmakunbound fmakunbound-setf))
          (ensure-symbol (string name) t))
        (DBG :cleanup)
315
        (do-symbols (sym package)
316 317 318 319
          (ensure-symbol (symbol-name sym)))
        (DBG :export)
        (loop :for name :being :the :hash-keys :of exported :do
          (ensure-export name package))
320 321 322 323
        ;; do away with packages with conflicting (nick)names
        ;; note from ASDF 2.26: ECL might not be liking an early fmakunbound (below #-ecl'ed)
        (ensure-package-fmakunbound package fmakunbound)
        (ensure-package-fmakunbound-setf package fmakunbound-setf)
324
        ;;#+DBG (untrace)
325 326 327
        package))))

(eval-when (:load-toplevel :compile-toplevel :execute)
328
  (defun parse-define-package-form (package clauses)
329 330 331 332
    (loop
      :with use-p = nil :with recycle-p = nil
      :with documentation = nil :with upgrade = nil
      :for (kw . args) :in clauses
333
      :when (eq kw :nicknames) :append args :into nicknames :else
334 335 336 337 338 339
      :when (eq kw :documentation)
        :do (cond
              (documentation (error "define-package: can't define documentation twice"))
              ((or (atom args) (cdr args)) (error "define-package: bad documentation"))
              (t (setf documentation (car args)))) :else
      :when (eq kw :use) :append args :into use :and :do (setf use-p t) :else
340
      :when (eq kw :shadow) :append args :into shadow :else
341 342
      :when (eq kw :shadowing-import-from) :collect args :into shadowing-import-from :else
      :when (eq kw :import-from) :collect args :into import-from :else
343 344
      :when (eq kw :export) :append args :into export :else
      :when (eq kw :intern) :append args :into intern :else
345
      :when (eq kw :recycle) :append args :into recycle :and :do (setf recycle-p t) :else
346
      :when (eq kw :mix) :append args :into mix :else
347
      :when (eq kw :reexport) :append args :into reexport :else
348 349 350
      :when (eq kw :unintern) :append args :into unintern :else
      :when (eq kw :fmakunbound) :append args :into fmakunbound :else
      :when (eq kw :fmakunbound-setf) :append args :into fmakunbound-setf :else
351 352 353 354 355
      :when (eq kw :upgrade)
        :do (unless (and (consp args) (null (cdr args)) (member (car args) '(:soft :hard)) (null upgrade))
              (error "define-package: bad :upgrade directive"))
            (setf upgrade (car args)) :else
      :do (error "unrecognized define-package keyword ~S" kw)
356 357
      :finally (return `(,package
                         :nicknames ,nicknames :documentation ,documentation
358 359 360 361 362 363 364
                         :use ,(if use-p use '(:common-lisp))
                         :shadow ,shadow :shadowing-import-from ,shadowing-import-from
                         :import-from ,import-from :export ,export :intern ,intern
                         :recycle ,(if recycle-p recycle (cons package nicknames))
                         :mix ,mix :reexport ,reexport :unintern ,unintern
                         ,@(when upgrade `(:upgrade ,upgrade))
                         :fmakunbound ,fmakunbound :fmakunbound-setf ,fmakunbound-setf)))))
365 366 367

(defmacro define-package (package &rest clauses)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
368
     #+gcl (defpackage ,package (:use))
369
     (apply 'ensure-package ',(parse-define-package-form package clauses))))