package.lisp 19.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
                    (setf (gethash name imported) t)
183 184 185
                    (unless (and xp (eq i x))
                      (when xp (unintern* x p))
                      (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
                    (symbol (find-symbol* name p))
                    (sp (symbol-package symbol))
                    (spn (package-name sp))
                    (ipn (gethash name inherited)))
204 205
               (multiple-value-bind (x xp) (find-symbol name package)
                 (cond
206 207
                   (ipn
                    (unless (eq spn ipn)
208
                      (error "Can't inherit ~S from ~S, it is inherited from ~S"
209
                             name spn ipn)))
210
                   ((gethash name imported)
211
                    (unless (eq symbol x)
212 213 214
                      (error "Can't inherit ~S from ~S, it is imported from ~S"
                             name sp (package-name (symbol-package x)))))
                   ((gethash name shadowed)
215
                    (error "Can't inherit ~S from ~S, it is shadowed" name spn))
216
                   (t
217
                    (setf (gethash name inherited) spn)
218 219
                    (when xp
                      (unintern* x package)))))))
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
           (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)))
236
                   (t (unintern* name package nil) (unintern* recycled previous) (import recycled package))))))
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 264 265
           (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))
266
              :do (DBG :baz (package-names p) n)
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 296 297
              :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)))))
298
        (loop :for (p . syms) :in shadowing-import-from :do
299
          (DBG :shaif p syms)
300 301
          (dolist (sym syms) (ensure-shadowing-import sym p)))
        (loop :for p :in mix :do
302
          (DBG :mix p)
303 304
          (do-external-symbols (sym p) (ensure-mix sym p)))
        (loop :for (p . syms) :in import-from :do
305
          (DBG :if p syms)
306
          (dolist (sym syms) (ensure-import sym p)))
307 308 309 310
        (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))
311 312 313 314 315 316
        (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)
317
        (do-symbols (sym package)
318 319 320 321
          (ensure-symbol (symbol-name sym)))
        (DBG :export)
        (loop :for name :being :the :hash-keys :of exported :do
          (ensure-export name package))
322 323 324 325
        ;; 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)
326
        ;;#+DBG (untrace)
327 328 329
        package))))

(eval-when (:load-toplevel :compile-toplevel :execute)
330
  (defun parse-define-package-form (package clauses)
331 332 333 334
    (loop
      :with use-p = nil :with recycle-p = nil
      :with documentation = nil :with upgrade = nil
      :for (kw . args) :in clauses
335
      :when (eq kw :nicknames) :append args :into nicknames :else
336 337 338 339 340 341
      :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
342
      :when (eq kw :shadow) :append args :into shadow :else
343 344
      :when (eq kw :shadowing-import-from) :collect args :into shadowing-import-from :else
      :when (eq kw :import-from) :collect args :into import-from :else
345 346
      :when (eq kw :export) :append args :into export :else
      :when (eq kw :intern) :append args :into intern :else
347
      :when (eq kw :recycle) :append args :into recycle :and :do (setf recycle-p t) :else
348
      :when (eq kw :mix) :append args :into mix :else
349
      :when (eq kw :reexport) :append args :into reexport :else
350 351 352
      :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
353 354 355 356 357
      :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)
358
      (progn fmakunbound fmakunbound-setf)
359 360
      :finally (return `(,package
                         :nicknames ,nicknames :documentation ,documentation
361 362 363 364 365 366
                         :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))
367
                         #|:fmakunbound ,fmakunbound :fmakunbound-setf ,fmakunbound-setf|#)))))
368 369 370

(defmacro define-package (package &rest clauses)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
371
     #+gcl (defpackage ,package (:use))
372
     (apply 'ensure-package ',(parse-define-package-form package clauses))))
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387

;;;; MAGIC FIXUP FOR ASDF.
;; For bootstrapping reason, define-package can't do its magic on the asdf/package package itself,
;; so instead do something ugly and special purpose. However, other packages could have imported
;; from ASDF and be in trouble. There ought to be a better solution to merging packages without tears.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar *extirpated-symbols* ())
  (when (find-package :asdf)
    (let (l)
      (do-external-symbols (sym :asdf/package)
        (multiple-value-bind (symbol lstatus) (find-symbol* sym :asdf nil)
          (when (and lstatus (not (eq sym symbol)))
            (push symbol l))))
      (push (cons :asdf (mapcar #'symbol-name-package (sort l 'string<))) *extirpated-symbols*))))