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 27 28 29 30 31 32 33 34 35 36 37 38 39 40
(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"
  ;"if not in debugging mode, just compute and return last value"
  #-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
           (names (cons name nicknames))
           (previous (DBG :p names (mapcar 'find-package names) (remove-duplicates (mapcar #'find-package names) :from-end t)))
154 155
           (discarded (cdr previous))
           (package (or (first previous) (make-package name :nicknames nicknames))))
156
      (DBG :foo)
157 158 159 160 161 162
      (labels
          ((ensure-shadowing-import (sym p)
             (let* ((name (string sym))
                    (i (find-symbol* name p)))
               (cond
                 ((gethash name shadowed)
163
                  (unless (eq i (find-symbol* name package))
164 165 166
                    (error "Conflicting shadowings for ~A" name)))
                 (t
                    (setf (gethash name shadowed) t)
167
                    (setf (gethash name imported) t)
168 169 170 171
                    (shadowing-import package)))))
           (ensure-import (sym p)
             (let* ((name (string sym))
                    (i (find-symbol* name p)))
172
               (multiple-value-bind (x xp) (find-symbol name package)
173
                 (cond
174 175
                   ((gethash name imported)
                    (unless (eq i x)
176
                      (error "Can't import ~S from both ~S and ~S"
177
                             name (package-name (symbol-package x)) (package-name p))))
178 179 180
                   ((gethash name shadowed)
                    (error "Can't both shadow ~S and import it from ~S" name (package-name p)))
                   (t
181 182 183 184
                    (when (and xp (not (eq i x)))
                      (unintern* x package))
                    (setf (gethash name imported) t)
                    (import i package))))))
185 186
           (ensure-mix (sym p)
             (let* ((name (string sym))
187 188 189
                    (sp (string p)))
               (unless (or (gethash name shadowed) (gethash name imported))
                 (let ((ip (gethash name inherited)))
190
                   (cond
191 192
                     ((eq sp ip))
                     (ip
193
                      (remhash name inherited)
194
                      (ensure-shadowing-import name ip))
195
                     (t
196
                      (ensure-inherited sym sp)))))))
197 198
           (ensure-inherited (sym p)
             (let* ((name (string sym))
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
                    (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)))))))
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 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 264 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
           (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)))
                   (t (unintern* name package) (unintern* recycled previous) (import recycled package))))))
           (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))
              :do (DBG (package-names discarded) n)
              :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)))))
295
        (loop :for (p . syms) :in shadowing-import-from :do
296
          (DBG :shaif p syms)
297 298
          (dolist (sym syms) (ensure-shadowing-import sym p)))
        (loop :for p :in mix :do
299
          (DBG :mix p)
300 301
          (do-external-symbols (sym p) (ensure-mix sym p)))
        (loop :for (p . syms) :in import-from :do
302
          (DBG :if p syms)
303
          (dolist (sym syms) (ensure-import sym p)))
304 305 306 307
        (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))
308 309 310 311 312 313
        (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)
314
        (do-symbols (sym package)
315 316 317 318
          (ensure-symbol (symbol-name sym)))
        (DBG :export)
        (loop :for name :being :the :hash-keys :of exported :do
          (ensure-export name package))
319 320 321 322
        ;; 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)
323
        ;;#+DBG (untrace)
324 325 326
        package))))

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

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