diff options
| author | Philip Kaludercic | 2026-01-02 14:29:46 +0100 |
|---|---|---|
| committer | Philip Kaludercic | 2026-01-02 14:29:46 +0100 |
| commit | 938866b3352fb8a9bf54c1ab94d454a650837a03 (patch) | |
| tree | 36e7ed55ca59fba2b8a9aeccef9f451910e6a8b6 /lisp/emacs-lisp/package-core.el | |
| parent | 83d989c033b893f6eb2fbcc367a4942604df8ad1 (diff) | |
| parent | 7b4dc0eb8401ec43b336c8a501d60f6f320af185 (diff) | |
| download | emacs-938866b3352fb8a9bf54c1ab94d454a650837a03.tar.gz emacs-938866b3352fb8a9bf54c1ab94d454a650837a03.zip | |
Merge branch 'package-core'feature/package-core
Diffstat (limited to 'lisp/emacs-lisp/package-core.el')
| -rw-r--r-- | lisp/emacs-lisp/package-core.el | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/package-core.el b/lisp/emacs-lisp/package-core.el new file mode 100644 index 00000000000..236b9b6a975 --- /dev/null +++ b/lisp/emacs-lisp/package-core.el | |||
| @@ -0,0 +1,477 @@ | |||
| 1 | ;;; package-core.el --- Core of the Emacs Package Manager -*- lexical-binding:t -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2007-2026 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Tom Tromey <tromey@redhat.com> | ||
| 6 | ;; Daniel Hackney <dan@haxney.org> | ||
| 7 | ;; Created: 10 Mar 2007 | ||
| 8 | ;; Version: 1.1.0 | ||
| 9 | ;; Keywords: tools | ||
| 10 | ;; Package-Requires: ((tabulated-list "1.0")) | ||
| 11 | |||
| 12 | ;; This file is part of GNU Emacs. | ||
| 13 | |||
| 14 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 15 | ;; it under the terms of the GNU General Public License as published by | ||
| 16 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 17 | ;; (at your option) any later version. | ||
| 18 | |||
| 19 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | ;; GNU General Public License for more details. | ||
| 23 | |||
| 24 | ;; You should have received a copy of the GNU General Public License | ||
| 25 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 26 | |||
| 27 | ;;; Commentary: | ||
| 28 | |||
| 29 | ;; This file contains the core definitions of package.el, including | ||
| 30 | ;; package descriptors, general user options and the package activation | ||
| 31 | ;; logic. | ||
| 32 | |||
| 33 | ;;; Code: | ||
| 34 | |||
| 35 | (eval-when-compile (require 'cl-lib)) | ||
| 36 | |||
| 37 | (defvar package--default-summary "No description available.") | ||
| 38 | |||
| 39 | (cl-defstruct (package-desc | ||
| 40 | ;; Rename the default constructor from `make-package-desc'. | ||
| 41 | (:constructor package-desc-create) | ||
| 42 | ;; Has the same interface as the old `define-package', | ||
| 43 | ;; which is still used in the "foo-pkg.el" files. Extra | ||
| 44 | ;; options can be supported by adding additional keys. | ||
| 45 | (:constructor | ||
| 46 | package-desc-from-define | ||
| 47 | (name-string version-string &optional summary requirements | ||
| 48 | &rest rest-plist | ||
| 49 | &aux | ||
| 50 | (name (intern name-string)) | ||
| 51 | (version (if (eq (car-safe version-string) 'vc) | ||
| 52 | (version-to-list (cdr version-string)) | ||
| 53 | (version-to-list version-string))) | ||
| 54 | (reqs (mapcar (lambda (elt) | ||
| 55 | (list (car elt) | ||
| 56 | (version-to-list (cadr elt)))) | ||
| 57 | (if (eq 'quote (car requirements)) | ||
| 58 | (nth 1 requirements) | ||
| 59 | requirements))) | ||
| 60 | (kind (plist-get rest-plist :kind)) | ||
| 61 | (archive (plist-get rest-plist :archive)) | ||
| 62 | (extras (let (alist) | ||
| 63 | (while rest-plist | ||
| 64 | (unless (memq (car rest-plist) '(:kind :archive)) | ||
| 65 | (let ((value (cadr rest-plist))) | ||
| 66 | (when value | ||
| 67 | (push (cons (car rest-plist) | ||
| 68 | (if (eq (car-safe value) 'quote) | ||
| 69 | (cadr value) | ||
| 70 | value)) | ||
| 71 | alist)))) | ||
| 72 | (setq rest-plist (cddr rest-plist))) | ||
| 73 | alist))))) | ||
| 74 | "Structure containing information about an individual package. | ||
| 75 | Slots: | ||
| 76 | |||
| 77 | `name' Name of the package, as a symbol. | ||
| 78 | |||
| 79 | `version' Version of the package, as a version list. | ||
| 80 | |||
| 81 | `summary' Short description of the package, typically taken from | ||
| 82 | the first line of the file. | ||
| 83 | |||
| 84 | `reqs' Requirements of the package. A list of (PACKAGE | ||
| 85 | VERSION-LIST) naming the dependent package and the minimum | ||
| 86 | required version. | ||
| 87 | |||
| 88 | `kind' The distribution format of the package. Currently, it is | ||
| 89 | either `single', `tar', or (temporarily only) `dir'. In | ||
| 90 | addition, there is distribution format `vc', which is handled | ||
| 91 | by package-vc.el. | ||
| 92 | |||
| 93 | `archive' The name of the archive (as a string) whence this | ||
| 94 | package came. | ||
| 95 | |||
| 96 | `dir' The directory where the package is installed (if installed), | ||
| 97 | `builtin' if it is built-in, or nil otherwise. | ||
| 98 | |||
| 99 | `extras' Optional alist of additional keyword-value pairs. | ||
| 100 | |||
| 101 | `signed' Flag to indicate that the package is signed by provider." | ||
| 102 | name | ||
| 103 | version | ||
| 104 | (summary package--default-summary) | ||
| 105 | reqs | ||
| 106 | kind | ||
| 107 | archive | ||
| 108 | dir | ||
| 109 | extras | ||
| 110 | signed) | ||
| 111 | |||
| 112 | |||
| 113 | ;;; Installed packages | ||
| 114 | ;; The following variables store information about packages present in | ||
| 115 | ;; the system. The most important of these is `package-alist'. The | ||
| 116 | ;; command `package-activate-all' is also closely related to this | ||
| 117 | ;; section. | ||
| 118 | |||
| 119 | (defvar package--builtins nil | ||
| 120 | "Alist of built-in packages. | ||
| 121 | The actual value is initialized by loading the library | ||
| 122 | `finder-inf'; this is not done until it is needed, e.g. by the | ||
| 123 | function `package-built-in-p'. | ||
| 124 | |||
| 125 | Each element has the form (PKG . PACKAGE-BI-DESC), where PKG is a package | ||
| 126 | name (a symbol) and DESC is a `package--bi-desc' structure.") | ||
| 127 | (put 'package--builtins 'risky-local-variable t) | ||
| 128 | |||
| 129 | (defvar package-alist nil | ||
| 130 | "Alist of all packages available for activation. | ||
| 131 | Each element has the form (PKG . DESCS), where PKG is a package | ||
| 132 | name (a symbol) and DESCS is a non-empty list of `package-desc' | ||
| 133 | structures, sorted by decreasing versions. | ||
| 134 | |||
| 135 | This variable is set automatically by `package-load-descriptor', | ||
| 136 | called via `package-activate-all'. To change which packages are | ||
| 137 | loaded and/or activated, customize `package-load-list'.") | ||
| 138 | (put 'package-alist 'risky-local-variable t) | ||
| 139 | |||
| 140 | ;;;; Public interfaces for accessing built-in package info | ||
| 141 | |||
| 142 | (defun package-versioned-builtin-packages () | ||
| 143 | "Return a list of all the versioned built-in packages. | ||
| 144 | The return value is a list of names of built-in packages represented as | ||
| 145 | symbols." | ||
| 146 | (mapcar #'car package--builtin-versions)) | ||
| 147 | |||
| 148 | (defun package-builtin-package-version (package) | ||
| 149 | "Return the version of a built-in PACKAGE given by its symbol. | ||
| 150 | The return value is a list of integers representing the version of | ||
| 151 | PACKAGE, in the format returned by `version-to-list', or nil if the | ||
| 152 | package is built-in but has no version or is not a built-in package." | ||
| 153 | (alist-get package package--builtin-versions)) | ||
| 154 | |||
| 155 | ;;;###autoload | ||
| 156 | (defvar package-activated-list nil | ||
| 157 | ;; FIXME: This should implicitly include all builtin packages. | ||
| 158 | "List of the names of currently activated packages.") | ||
| 159 | (put 'package-activated-list 'risky-local-variable t) | ||
| 160 | |||
| 161 | ;;;; Populating `package-alist'. | ||
| 162 | |||
| 163 | ;; The following functions are called on each installed package by | ||
| 164 | ;; `package-load-all-descriptors', which ultimately populates the | ||
| 165 | ;; `package-alist' variable. | ||
| 166 | |||
| 167 | (defun package-process-define-package (exp) | ||
| 168 | "Process define-package expression EXP and push it to `package-alist'. | ||
| 169 | EXP should be a form read from a foo-pkg.el file. | ||
| 170 | Convert EXP into a `package-desc' object using the | ||
| 171 | `package-desc-from-define' constructor before pushing it to | ||
| 172 | `package-alist'. | ||
| 173 | |||
| 174 | If there already exists a package by the same name in | ||
| 175 | `package-alist', insert this object there such that the packages | ||
| 176 | are sorted with the highest version first." | ||
| 177 | (when (eq (car-safe exp) 'define-package) | ||
| 178 | (let* ((new-pkg-desc (apply #'package-desc-from-define (cdr exp))) | ||
| 179 | (name (package-desc-name new-pkg-desc)) | ||
| 180 | (version (package-desc-version new-pkg-desc)) | ||
| 181 | (old-pkgs (assq name package-alist))) | ||
| 182 | (if (null old-pkgs) | ||
| 183 | ;; If there's no old package, just add this to `package-alist'. | ||
| 184 | (push (list name new-pkg-desc) package-alist) | ||
| 185 | ;; If there is, insert the new package at the right place in the list. | ||
| 186 | (while | ||
| 187 | (if (and (cdr old-pkgs) | ||
| 188 | (version-list-< version | ||
| 189 | (package-desc-version (cadr old-pkgs)))) | ||
| 190 | (setq old-pkgs (cdr old-pkgs)) | ||
| 191 | (push new-pkg-desc (cdr old-pkgs)) | ||
| 192 | nil))) | ||
| 193 | new-pkg-desc))) | ||
| 194 | |||
| 195 | (defun package-load-descriptor (pkg-dir) | ||
| 196 | "Load the package description file in directory PKG-DIR. | ||
| 197 | Create a new `package-desc' object, add it to `package-alist' and | ||
| 198 | return it." | ||
| 199 | (let ((pkg-file (expand-file-name (package--description-file pkg-dir) | ||
| 200 | pkg-dir)) | ||
| 201 | (signed-file (concat pkg-dir ".signed"))) | ||
| 202 | (when (file-exists-p pkg-file) | ||
| 203 | (with-temp-buffer | ||
| 204 | (insert-file-contents pkg-file) | ||
| 205 | (goto-char (point-min)) | ||
| 206 | (let ((pkg-desc (or (package-process-define-package | ||
| 207 | (read (current-buffer))) | ||
| 208 | (error "Can't find define-package in %s" pkg-file)))) | ||
| 209 | (setf (package-desc-dir pkg-desc) pkg-dir) | ||
| 210 | (if (file-exists-p signed-file) | ||
| 211 | (setf (package-desc-signed pkg-desc) t)) | ||
| 212 | pkg-desc))))) | ||
| 213 | |||
| 214 | (defun package-load-all-descriptors () | ||
| 215 | "Load descriptors for installed Emacs Lisp packages. | ||
| 216 | This looks for package subdirectories in `package-user-dir' and | ||
| 217 | `package-directory-list'. The variable `package-load-list' | ||
| 218 | controls which package subdirectories may be loaded. | ||
| 219 | |||
| 220 | In each valid package subdirectory, this function loads the | ||
| 221 | description file containing a call to `define-package', which | ||
| 222 | updates `package-alist'." | ||
| 223 | (dolist (dir (cons package-user-dir package-directory-list)) | ||
| 224 | (when (file-directory-p dir) | ||
| 225 | (dolist (pkg-dir (directory-files dir t "\\`[^.]")) | ||
| 226 | (when (file-directory-p pkg-dir) | ||
| 227 | (package-load-descriptor pkg-dir)))))) | ||
| 228 | |||
| 229 | (defun package--alist () | ||
| 230 | "Return `package-alist', after computing it if needed." | ||
| 231 | (or package-alist | ||
| 232 | (progn (package-load-all-descriptors) | ||
| 233 | package-alist))) | ||
| 234 | |||
| 235 | |||
| 236 | ;;; Package activation | ||
| 237 | ;; Section for functions used by `package-activate', which see. | ||
| 238 | |||
| 239 | (defun package-disabled-p (pkg-name version) | ||
| 240 | "Return whether PKG-NAME at VERSION can be activated. | ||
| 241 | The decision is made according to `package-load-list'. | ||
| 242 | Return nil if the package can be activated. | ||
| 243 | Return t if the package is completely disabled. | ||
| 244 | Return the max version (as a string) if the package is held at a lower version." | ||
| 245 | (let ((force (assq pkg-name package-load-list))) | ||
| 246 | (cond ((null force) (not (memq 'all package-load-list))) | ||
| 247 | ((null (setq force (cadr force))) t) ; disabled | ||
| 248 | ((eq force t) nil) | ||
| 249 | ((stringp force) ; held | ||
| 250 | (unless (version-list-= version (version-to-list force)) | ||
| 251 | force)) | ||
| 252 | (t (error "Invalid element in `package-load-list'"))))) | ||
| 253 | |||
| 254 | (defun package-built-in-p (package &optional min-version) | ||
| 255 | "Return non-nil if PACKAGE is built-in to Emacs. | ||
| 256 | Optional arg MIN-VERSION, if non-nil, should be a version list | ||
| 257 | specifying the minimum acceptable version." | ||
| 258 | (if (package-desc-p package) ;; was built-in and then was converted | ||
| 259 | (eq 'builtin (package-desc-dir package)) | ||
| 260 | (let ((bi (assq package package--builtin-versions))) | ||
| 261 | (cond | ||
| 262 | (bi (version-list-<= min-version (cdr bi))) | ||
| 263 | ((remove 0 min-version) nil) | ||
| 264 | (t | ||
| 265 | (require 'finder-inf nil t) ; For `package--builtins'. | ||
| 266 | (assq package package--builtins)))))) | ||
| 267 | |||
| 268 | (defun package--autoloads-file-name (pkg-desc) | ||
| 269 | "Return the absolute name of the autoloads file, sans extension. | ||
| 270 | PKG-DESC is a `package-desc' object." | ||
| 271 | (expand-file-name | ||
| 272 | (format "%s-autoloads" (package-desc-name pkg-desc)) | ||
| 273 | (package-desc-dir pkg-desc))) | ||
| 274 | |||
| 275 | (defvar Info-directory-list) | ||
| 276 | (declare-function info-initialize "info" ()) | ||
| 277 | |||
| 278 | (defvar package--quickstart-pkgs t | ||
| 279 | "If set to a list, we're computing the set of pkgs to activate.") | ||
| 280 | |||
| 281 | (defun package--add-info-node (pkg-dir) | ||
| 282 | "Add info node located in PKG-DIR." | ||
| 283 | (when (file-exists-p (expand-file-name "dir" pkg-dir)) | ||
| 284 | ;; FIXME: not the friendliest, but simple. | ||
| 285 | (require 'info) | ||
| 286 | (info-initialize) | ||
| 287 | (add-to-list 'Info-directory-list pkg-dir))) | ||
| 288 | |||
| 289 | (defun package-activate-1 (pkg-desc &optional reload deps) | ||
| 290 | "Activate package given by PKG-DESC, even if it was already active. | ||
| 291 | If DEPS is non-nil, also activate its dependencies (unless they | ||
| 292 | are already activated). | ||
| 293 | If RELOAD is non-nil, also `load' any files inside the package which | ||
| 294 | correspond to previously loaded files." | ||
| 295 | (let* ((name (package-desc-name pkg-desc)) | ||
| 296 | (pkg-dir (package-desc-dir pkg-desc))) | ||
| 297 | (unless pkg-dir | ||
| 298 | (error "Internal error: unable to find directory for `%s'" | ||
| 299 | (package-desc-full-name pkg-desc))) | ||
| 300 | (catch 'exit | ||
| 301 | ;; Activate its dependencies recursively. | ||
| 302 | ;; FIXME: This doesn't check whether the activated version is the | ||
| 303 | ;; required version. | ||
| 304 | (when deps | ||
| 305 | (dolist (req (package-desc-reqs pkg-desc)) | ||
| 306 | (unless (package-activate (car req)) | ||
| 307 | (message "Unable to activate package `%s'.\nRequired package `%s-%s' is unavailable" | ||
| 308 | name (car req) (package-version-join (cadr req))) | ||
| 309 | (throw 'exit nil)))) | ||
| 310 | (if (listp package--quickstart-pkgs) | ||
| 311 | ;; We're only collecting the set of packages to activate! | ||
| 312 | (push pkg-desc package--quickstart-pkgs) | ||
| 313 | (when (or reload (assq name package--builtin-versions)) | ||
| 314 | (package--reload-previously-loaded | ||
| 315 | pkg-desc (unless reload | ||
| 316 | "Package %S is activated too late. | ||
| 317 | The following files have already been loaded: %S"))) | ||
| 318 | (with-demoted-errors "Error loading autoloads: %s" | ||
| 319 | (load (package--autoloads-file-name pkg-desc) nil t))) | ||
| 320 | (package--add-info-node pkg-dir) | ||
| 321 | (push name package-activated-list) | ||
| 322 | ;; Don't return nil. | ||
| 323 | t))) | ||
| 324 | |||
| 325 | ;;;; `package-activate' | ||
| 326 | |||
| 327 | (defun package--get-activatable-pkg (pkg-name) | ||
| 328 | ;; Is "activatable" a word? | ||
| 329 | (let ((pkg-descs (cdr (assq pkg-name package-alist)))) | ||
| 330 | ;; Check if PACKAGE is available in `package-alist'. | ||
| 331 | (while | ||
| 332 | (when pkg-descs | ||
| 333 | (let ((available-version (package-desc-version (car pkg-descs)))) | ||
| 334 | (or (package-disabled-p pkg-name available-version) | ||
| 335 | ;; Prefer a builtin package. | ||
| 336 | (package-built-in-p pkg-name available-version)))) | ||
| 337 | (setq pkg-descs (cdr pkg-descs))) | ||
| 338 | (car pkg-descs))) | ||
| 339 | |||
| 340 | ;; This function activates a newer version of a package if an older | ||
| 341 | ;; one was already activated. It also loads a features of this | ||
| 342 | ;; package which were already loaded. | ||
| 343 | (defun package-activate (package &optional force) | ||
| 344 | "Activate the package named PACKAGE. | ||
| 345 | If FORCE is true, (re-)activate it if it's already activated. | ||
| 346 | Newer versions are always activated, regardless of FORCE." | ||
| 347 | (let ((pkg-desc (package--get-activatable-pkg package))) | ||
| 348 | (cond | ||
| 349 | ;; If no such package is found, maybe it's built-in. | ||
| 350 | ((null pkg-desc) | ||
| 351 | (package-built-in-p package)) | ||
| 352 | ;; If the package is already activated, just return t. | ||
| 353 | ((and (memq package package-activated-list) (not force)) | ||
| 354 | t) | ||
| 355 | ;; Otherwise, proceed with activation. | ||
| 356 | (t (package-activate-1 pkg-desc nil 'deps))))) | ||
| 357 | |||
| 358 | |||
| 359 | ;;; Installation -- Local operations | ||
| 360 | ;; This section contains a variety of features regarding installing a | ||
| 361 | ;; package to/from disk. This includes autoload generation, | ||
| 362 | ;; unpacking, compiling, as well as defining a package from the | ||
| 363 | ;; current buffer. | ||
| 364 | |||
| 365 | ;;;; Unpacking | ||
| 366 | |||
| 367 | ;;;###autoload | ||
| 368 | (defvar package--activated nil | ||
| 369 | "Non-nil if `package-activate-all' has been run.") | ||
| 370 | |||
| 371 | ;;;###autoload | ||
| 372 | (progn ;; Make the function usable without loading `package.el'. | ||
| 373 | (defun package-activate-all () | ||
| 374 | "Activate all installed packages. | ||
| 375 | The variable `package-load-list' controls which packages to load." | ||
| 376 | (setq package--activated t) | ||
| 377 | (let* ((elc (concat package-quickstart-file "c")) | ||
| 378 | (qs (if (file-readable-p elc) elc | ||
| 379 | (if (file-readable-p package-quickstart-file) | ||
| 380 | package-quickstart-file)))) | ||
| 381 | ;; The quickstart file presumes that it has a blank slate, | ||
| 382 | ;; so don't use it if we already activated some packages. | ||
| 383 | (or (and qs (not (bound-and-true-p package-activated-list)) | ||
| 384 | ;; Skip `load-source-file-function' which would slow us down by | ||
| 385 | ;; a factor 2 when loading the .el file (this assumes we were | ||
| 386 | ;; careful to save this file so it doesn't need any decoding). | ||
| 387 | (with-demoted-errors "Error during quickstart: %S" | ||
| 388 | (let ((load-source-file-function nil)) | ||
| 389 | (unless (boundp 'package-activated-list) | ||
| 390 | (setq package-activated-list nil)) | ||
| 391 | (load qs nil 'nomessage) | ||
| 392 | t))) | ||
| 393 | (progn | ||
| 394 | (require 'package) | ||
| 395 | ;; Silence the "unknown function" warning when this is compiled | ||
| 396 | ;; inside `loaddefs.el'. | ||
| 397 | ;; FIXME: We use `with-no-warnings' because the effect of | ||
| 398 | ;; `declare-function' is currently not scoped, so if we use | ||
| 399 | ;; it here, we end up with a redefinition warning instead :-) | ||
| 400 | (with-no-warnings | ||
| 401 | (package--activate-all))))))) | ||
| 402 | |||
| 403 | (defun package--activate-all () | ||
| 404 | (dolist (elt (package--alist)) | ||
| 405 | (condition-case err | ||
| 406 | (package-activate (car elt)) | ||
| 407 | ;; Don't let failure of activation of a package arbitrarily stop | ||
| 408 | ;; activation of further packages. | ||
| 409 | (error (message "%s" (error-message-string err)))))) | ||
| 410 | |||
| 411 | ;;;; Inferring package from current buffer | ||
| 412 | |||
| 413 | (declare-function lm-package-version "lisp-mnt" (&optional file)) | ||
| 414 | |||
| 415 | ;;;###autoload | ||
| 416 | (defun package-installed-p (package &optional min-version) | ||
| 417 | "Return non-nil if PACKAGE, of MIN-VERSION or newer, is installed. | ||
| 418 | If PACKAGE is a symbol, it is the package name and MIN-VERSION | ||
| 419 | should be a version list. | ||
| 420 | |||
| 421 | If PACKAGE is a `package-desc' object, MIN-VERSION is ignored." | ||
| 422 | (cond | ||
| 423 | ((package-desc-p package) | ||
| 424 | (let ((dir (package-desc-dir package))) | ||
| 425 | (and (stringp dir) | ||
| 426 | (file-exists-p dir)))) | ||
| 427 | ((and (not (bound-and-true-p package--initialized)) | ||
| 428 | (null min-version) | ||
| 429 | package-activated-list) | ||
| 430 | ;; We used the quickstart: make it possible to use package-installed-p | ||
| 431 | ;; even before package is fully initialized. | ||
| 432 | (or | ||
| 433 | (memq package package-activated-list) | ||
| 434 | ;; Also check built-in packages. | ||
| 435 | (package-built-in-p package min-version))) | ||
| 436 | (t | ||
| 437 | (or | ||
| 438 | (let ((pkg-descs (cdr (assq package (package--alist))))) | ||
| 439 | (and pkg-descs | ||
| 440 | (version-list-<= min-version | ||
| 441 | (package-desc-version (car pkg-descs))))) | ||
| 442 | ;; Also check built-in packages. | ||
| 443 | (package-built-in-p package min-version))))) | ||
| 444 | |||
| 445 | ;;;###autoload | ||
| 446 | (defun package-get-version () | ||
| 447 | "Return the version number of the package in which this is used. | ||
| 448 | Assumes it is used from an Elisp file placed inside the top-level directory | ||
| 449 | of an installed ELPA package. | ||
| 450 | The return value is a string (or nil in case we can't find it). | ||
| 451 | It works in more cases if the call is in the file which contains | ||
| 452 | the `Version:' header." | ||
| 453 | ;; In a sense, this is a lie, but it does just what we want: precomputes | ||
| 454 | ;; the version at compile time and hardcodes it into the .elc file! | ||
| 455 | (declare (pure t)) | ||
| 456 | ;; Hack alert! | ||
| 457 | (let ((file (or (macroexp-file-name) buffer-file-name))) | ||
| 458 | (cond | ||
| 459 | ((null file) nil) | ||
| 460 | ;; Packages are normally installed into directories named "<pkg>-<vers>", | ||
| 461 | ;; so get the version number from there. | ||
| 462 | ((string-match "/[^/]+-\\([0-9]\\(?:[0-9.]\\|pre\\|beta\\|alpha\\|snapshot\\)+\\)/[^/]+\\'" file) | ||
| 463 | (match-string 1 file)) | ||
| 464 | ;; For packages run straight from the an elpa.git clone, there's no | ||
| 465 | ;; "-<vers>" in the directory name, so we have to fetch the version | ||
| 466 | ;; the hard way. | ||
| 467 | (t | ||
| 468 | (let* ((pkgdir (file-name-directory file)) | ||
| 469 | (pkgname (file-name-nondirectory (directory-file-name pkgdir))) | ||
| 470 | (mainfile (expand-file-name (concat pkgname ".el") pkgdir))) | ||
| 471 | (unless (file-readable-p mainfile) (setq mainfile file)) | ||
| 472 | (when (file-readable-p mainfile) | ||
| 473 | (require 'lisp-mnt) | ||
| 474 | (lm-package-version mainfile))))))) | ||
| 475 | |||
| 476 | (provide 'package-core) | ||
| 477 | ;;; package-core.el ends here | ||