aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/emacs-lisp/package-core.el
diff options
context:
space:
mode:
authorPhilip Kaludercic2026-01-02 14:29:46 +0100
committerPhilip Kaludercic2026-01-02 14:29:46 +0100
commit938866b3352fb8a9bf54c1ab94d454a650837a03 (patch)
tree36e7ed55ca59fba2b8a9aeccef9f451910e6a8b6 /lisp/emacs-lisp/package-core.el
parent83d989c033b893f6eb2fbcc367a4942604df8ad1 (diff)
parent7b4dc0eb8401ec43b336c8a501d60f6f320af185 (diff)
downloademacs-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.el477
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.
75Slots:
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.
121The actual value is initialized by loading the library
122`finder-inf'; this is not done until it is needed, e.g. by the
123function `package-built-in-p'.
124
125Each element has the form (PKG . PACKAGE-BI-DESC), where PKG is a package
126name (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.
131Each element has the form (PKG . DESCS), where PKG is a package
132name (a symbol) and DESCS is a non-empty list of `package-desc'
133structures, sorted by decreasing versions.
134
135This variable is set automatically by `package-load-descriptor',
136called via `package-activate-all'. To change which packages are
137loaded 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.
144The return value is a list of names of built-in packages represented as
145symbols."
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.
150The return value is a list of integers representing the version of
151PACKAGE, in the format returned by `version-to-list', or nil if the
152package 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'.
169EXP should be a form read from a foo-pkg.el file.
170Convert EXP into a `package-desc' object using the
171`package-desc-from-define' constructor before pushing it to
172`package-alist'.
173
174If there already exists a package by the same name in
175`package-alist', insert this object there such that the packages
176are 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.
197Create a new `package-desc' object, add it to `package-alist' and
198return 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.
216This looks for package subdirectories in `package-user-dir' and
217`package-directory-list'. The variable `package-load-list'
218controls which package subdirectories may be loaded.
219
220In each valid package subdirectory, this function loads the
221description file containing a call to `define-package', which
222updates `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.
241The decision is made according to `package-load-list'.
242Return nil if the package can be activated.
243Return t if the package is completely disabled.
244Return 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.
256Optional arg MIN-VERSION, if non-nil, should be a version list
257specifying 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.
270PKG-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.
291If DEPS is non-nil, also activate its dependencies (unless they
292are already activated).
293If RELOAD is non-nil, also `load' any files inside the package which
294correspond 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.
317The 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.
345If FORCE is true, (re-)activate it if it's already activated.
346Newer 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.
375The 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.
418If PACKAGE is a symbol, it is the package name and MIN-VERSION
419should be a version list.
420
421If 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.
448Assumes it is used from an Elisp file placed inside the top-level directory
449of an installed ELPA package.
450The return value is a string (or nil in case we can't find it).
451It works in more cases if the call is in the file which contains
452the `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