;;; package-quickstart.el --- Accelerating Package Startup -*- lexical-binding: t; -*- ;; Copyright (C) 2007-2025 Free Software Foundation, Inc. ;; Author: Tom Tromey ;; Daniel Hackney ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Activating packages via `package-initialize' is costly: for N installed ;; packages, it needs to read all N -pkg.el files first to decide ;; which packages to activate, and then again N -autoloads.el files. ;; To speed this up, we precompute a mega-autoloads file which is the ;; concatenation of all those -autoloads.el, so we can activate ;; all packages by loading this one file (and hence without initializing ;; package.el). ;; Other than speeding things up, this also offers a bootstrap feature: ;; it lets us activate packages according to `package-load-list' and ;; `package-user-dir' even before those vars are set. ;;; Code: (require 'package-core) (defcustom package-quickstart nil "Precompute activation actions to speed up startup. This requires the use of `package-quickstart-refresh' every time the activations need to be changed, such as when `package-load-list' is modified." :type 'boolean :version "27.1" :group 'package) ;;;###autoload (defcustom package-quickstart-file (locate-user-emacs-file "package-quickstart.el") "Location of the file used to speed up activation of packages at startup." :type 'file :group 'package :initialize #'custom-initialize-delay :version "27.1") (defun package--quickstart-maybe-refresh () (if package-quickstart ;; FIXME: Delay refresh in case we're installing/deleting ;; several packages! (package-quickstart-refresh) (delete-file (concat package-quickstart-file "c")) (delete-file package-quickstart-file))) (defvar package--quickstart-dir nil "Set by `package-quickstart-file' to the directory containing it.") (defun package--quickstart-rel (file) "Return an expr depending on `package--quickstart-dir' which evaluates to FILE. If FILE is in `package--quickstart-dir', returns an expression that is relative to that directory, so if that directory is moved we can still find FILE." (if (file-in-directory-p file package--quickstart-dir) `(file-name-concat package--quickstart-dir ,(file-relative-name file package--quickstart-dir)) file)) (defun package-quickstart-refresh () "(Re)Generate the `package-quickstart-file'." (interactive) (package-initialize 'no-activate) (require 'info) (let ((package--quickstart-pkgs ()) ;; Pretend we haven't activated anything yet! (package-activated-list ()) ;; Make sure we can load this file without load-source-file-function. (coding-system-for-write 'emacs-internal) ;; Ensure that `pp' and `prin1-to-string' calls further down ;; aren't truncated. (print-length nil) (print-level nil) (Info-directory-list '("")) (package--quickstart-dir nil)) (dolist (elt package-alist) (condition-case err (package-activate (car elt)) ;; Don't let failure of activation of a package arbitrarily stop ;; activation of further packages. (error (message "%s" (error-message-string err))))) (setq package--quickstart-pkgs (nreverse package--quickstart-pkgs)) (with-temp-file package-quickstart-file (emacs-lisp-mode) ;For `syntax-ppss'. (insert ";;; Quickstart file to activate all packages at startup -*- lexical-binding:t -*-\n") (insert ";; ¡¡ This file is autogenerated by `package-quickstart-refresh', DO NOT EDIT !!\n\n") (setq package--quickstart-dir (file-name-directory (expand-file-name package-quickstart-file))) (pp '(setq package--quickstart-dir (file-name-directory (expand-file-name load-file-name))) (current-buffer)) (dolist (pkg package--quickstart-pkgs) (let* ((file ;; Prefer uncompiled files (and don't accept .so files). (let ((load-suffixes '(".el" ".elc"))) (locate-library (package--autoloads-file-name pkg)))) (pfile (prin1-to-string (package--quickstart-rel file)))) (insert "(let* ((load-file-name " pfile ")\ \(load-true-file-name load-file-name))\n") (insert-file-contents file) ;; Fixup the special #$ reader form and throw away comments. (while (re-search-forward "#\\$\\|^;\\(.*\n\\)" nil 'move) (unless (ppss-string-terminator (save-match-data (syntax-ppss))) (replace-match (if (match-end 1) "" pfile) t t))) (unless (bolp) (insert "\n")) (insert ")\n"))) (pp `(defvar package-activated-list) (current-buffer)) (pp `(setq package-activated-list (delete-dups (append ',(mapcar #'package-desc-name package--quickstart-pkgs) package-activated-list))) (current-buffer)) (let ((info-dirs (mapcar #'package--quickstart-rel (butlast Info-directory-list)))) (when info-dirs (pp `(progn (require 'info) (info-initialize) (setq Info-directory-list (append (list . ,info-dirs) Info-directory-list))) (current-buffer)))) ;; Use `\s' instead of a space character, so this code chunk is not ;; mistaken for an actual file-local section of package.el. (insert " ;; Local\sVariables: ;; version-control: never ;; no-update-autoloads: t ;; byte-compile-warnings: (not make-local) ;; End: ")) ;; FIXME: Do it asynchronously in an Emacs subprocess, and ;; don't show the byte-compiler warnings. (byte-compile-file package-quickstart-file))) (provide 'package-quickstart) ;;; package-quickstart.el ends here