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