Refactor autoload generator
- Halves LOC - Adopts functional paradigm where possible. - Reduces the filesize of autoloads files by ~10-20% - Speeds up autoloads generation by ~20%
This commit is contained in:
parent
4808d40736
commit
f8ff50565e
3 changed files with 264 additions and 396 deletions
|
@ -1,407 +1,274 @@
|
||||||
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
|
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
(defvar doom-autoload-excluded-packages '("gh")
|
(defvar doom-autoload-excluded-packages '("gh")
|
||||||
"Packages that have silly or destructive autoload files that try to load
|
"What packages whose autoloads file we won't index.
|
||||||
|
|
||||||
|
These packages have silly or destructive autoload files that try to load
|
||||||
everyone in the universe and their dog, causing errors that make babies cry. No
|
everyone in the universe and their dog, causing errors that make babies cry. No
|
||||||
one wants that.")
|
one wants that.")
|
||||||
|
|
||||||
|
(defvar doom-autoload-cached-vars
|
||||||
|
'(load-path
|
||||||
|
auto-mode-alist
|
||||||
|
Info-directory-list
|
||||||
|
doom-disabled-packages)
|
||||||
|
"A list of variables to be cached in `doom-package-autoload-file'.")
|
||||||
|
|
||||||
;; externs
|
;; externs
|
||||||
(defvar autoload-timestamps)
|
(defvar autoload-timestamps)
|
||||||
(defvar generated-autoload-load-name)
|
(defvar generated-autoload-load-name)
|
||||||
(defvar generated-autoload-file)
|
|
||||||
|
|
||||||
|
(defun doom-cli-reload-autoloads (&optional type force-p)
|
||||||
;;
|
|
||||||
;;; Helpers
|
|
||||||
|
|
||||||
(defun doom--cli-delete-autoloads-file (file)
|
|
||||||
"Delete FILE (an autoloads file) and accompanying *.elc file, if any."
|
|
||||||
(cl-check-type file string)
|
|
||||||
(when (file-exists-p file)
|
|
||||||
(when-let (buf (find-buffer-visiting file))
|
|
||||||
(with-current-buffer buf
|
|
||||||
(set-buffer-modified-p nil))
|
|
||||||
(kill-buffer buf))
|
|
||||||
(delete-file file)
|
|
||||||
(ignore-errors (delete-file (byte-compile-dest-file file)))
|
|
||||||
t))
|
|
||||||
|
|
||||||
(defun doom--cli-warn-refresh-session-h ()
|
|
||||||
(message "Restart or reload Doom Emacs for changes to take effect:\n")
|
|
||||||
(message " M-x doom/restart-and-restore")
|
|
||||||
(message " M-x doom/restart")
|
|
||||||
(message " M-x doom/reload"))
|
|
||||||
|
|
||||||
(defun doom--cli-byte-compile-file (file)
|
|
||||||
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings))
|
|
||||||
(byte-compile-dynamic t)
|
|
||||||
(byte-compile-dynamic-docstrings t))
|
|
||||||
(condition-case-unless-debug e
|
|
||||||
(when (byte-compile-file file)
|
|
||||||
(unless doom-interactive-mode
|
|
||||||
(add-hook 'doom-cli-post-success-execute-hook #'doom--cli-warn-refresh-session-h))
|
|
||||||
(let (noninteractive)
|
|
||||||
(load file 'noerror 'nomessage 'nosuffix)))
|
|
||||||
((debug error)
|
|
||||||
(let ((backup-file (concat file ".bk")))
|
|
||||||
(print! (warn "Copied backup to %s") (relpath backup-file))
|
|
||||||
(copy-file file backup-file 'overwrite))
|
|
||||||
(doom--cli-delete-autoloads-file file)
|
|
||||||
(signal 'doom-autoload-error (list file e))))))
|
|
||||||
|
|
||||||
(defun doom-cli-reload-autoloads (&optional file force-p)
|
|
||||||
"Reloads FILE (an autoload file), if it needs reloading.
|
"Reloads FILE (an autoload file), if it needs reloading.
|
||||||
|
|
||||||
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If
|
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If
|
||||||
it is nil, it will try to reload both. If FORCE-P (universal argument) do it
|
it is nil, it will try to reload both. If FORCE-P (universal argument) do it
|
||||||
even if it doesn't need reloading!"
|
even if it doesn't need reloading!"
|
||||||
(or (null file)
|
(if type
|
||||||
(stringp file)
|
(cond ((eq type 'core)
|
||||||
(signal 'wrong-type-argument (list 'stringp file)))
|
(doom-cli-reload-core-autoloads
|
||||||
(if (stringp file)
|
doom-autoload-file force-p))
|
||||||
(cond ((file-equal-p file doom-autoload-file)
|
((eq type 'package)
|
||||||
(doom-cli-reload-core-autoloads force-p))
|
(doom-cli-reload-package-autoloads
|
||||||
((file-equal-p file doom-package-autoload-file)
|
doom-package-autoload-file force-p))
|
||||||
(doom-cli-reload-package-autoloads force-p))
|
((error "Invalid autoloads file: %s" type)))
|
||||||
((error "Invalid autoloads file: %s" file)))
|
(doom-cli-reload-autoloads 'core force-p)
|
||||||
(doom-cli-reload-core-autoloads force-p)
|
(doom-cli-reload-autoloads 'package force-p)))
|
||||||
(doom-cli-reload-package-autoloads force-p)))
|
|
||||||
|
|
||||||
|
(defun doom-cli-reload-core-autoloads (file &optional force-p)
|
||||||
;;
|
(cl-check-type file string)
|
||||||
;;; Doom autoloads
|
(print! (start "(Re)generating core autoloads..."))
|
||||||
|
|
||||||
(defun doom--cli-generate-header (func)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(insert ";; -*- lexical-binding:t; -*-\n"
|
|
||||||
";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
|
|
||||||
|
|
||||||
(defun doom--cli-generate-autoloads (targets)
|
|
||||||
(let ((n 0))
|
|
||||||
(dolist (file targets)
|
|
||||||
(insert
|
|
||||||
(with-temp-buffer
|
|
||||||
(cond ((not (doom-file-cookie-p file "if" t))
|
|
||||||
(print! (debug "Ignoring %s") (relpath file)))
|
|
||||||
|
|
||||||
((let ((generated-autoload-load-name (file-name-sans-extension file))
|
|
||||||
;; Prevent `autoload-find-file' from firing file hooks,
|
|
||||||
;; e.g. adding to recentf.
|
|
||||||
find-file-hook
|
|
||||||
write-file-functions
|
|
||||||
;; Prevent a possible source of crashes when there's a
|
|
||||||
;; syntax error in the autoloads file
|
|
||||||
debug-on-error)
|
|
||||||
(quiet! (autoload-generate-file-autoloads file (current-buffer))))
|
|
||||||
(print! (debug "Nothing in %s") (relpath file)))
|
|
||||||
|
|
||||||
((cl-incf n)
|
|
||||||
(print! (debug "Scanning %s...") (relpath file))))
|
|
||||||
(buffer-string))))
|
|
||||||
(print! (class (if (> n 0) 'success 'info)
|
|
||||||
"Scanned %d file(s)")
|
|
||||||
n)))
|
|
||||||
|
|
||||||
(defun doom--cli-expand-autoload-paths (&optional allow-internal-paths)
|
|
||||||
(let ((load-path
|
|
||||||
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
|
|
||||||
;; will be unable to declare autoloads for the built-in autoload.el
|
|
||||||
;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why
|
|
||||||
;; they'd want to though, so it's an acceptable compromise.
|
|
||||||
(append (list doom-private-dir)
|
|
||||||
doom-modules-dirs
|
|
||||||
(straight--directory-files (straight--build-dir) nil t)
|
|
||||||
load-path)))
|
|
||||||
(defvar doom--autoloads-path-cache nil)
|
|
||||||
(while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
|
|
||||||
(let ((path (match-string 1)))
|
|
||||||
(replace-match
|
|
||||||
(or (cdr (assoc path doom--autoloads-path-cache))
|
|
||||||
(when-let* ((libpath (or (and allow-internal-paths
|
|
||||||
(locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
|
|
||||||
(locate-library path)))
|
|
||||||
(libpath (file-name-sans-extension libpath))
|
|
||||||
(libpath (abbreviate-file-name libpath)))
|
|
||||||
(push (cons path libpath) doom--autoloads-path-cache)
|
|
||||||
libpath)
|
|
||||||
path)
|
|
||||||
t t nil 1)))))
|
|
||||||
|
|
||||||
(defun doom--cli-generate-autodefs-1 (path &optional member-p)
|
|
||||||
(let (forms)
|
|
||||||
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
|
|
||||||
(let* ((sexp (sexp-at-point))
|
|
||||||
(alt-sexp (match-string 1))
|
|
||||||
(type (car sexp))
|
|
||||||
(name (doom-unquote (cadr sexp)))
|
|
||||||
(origin (doom-module-from-path path)))
|
|
||||||
(cond
|
|
||||||
((and (not member-p)
|
|
||||||
alt-sexp)
|
|
||||||
(push (read alt-sexp) forms))
|
|
||||||
|
|
||||||
((memq type '(defun defmacro cl-defun cl-defmacro))
|
|
||||||
(cl-destructuring-bind (_ _name arglist &rest body) sexp
|
|
||||||
(appendq!
|
|
||||||
forms
|
|
||||||
(list (if member-p
|
|
||||||
(make-autoload sexp path)
|
|
||||||
(let ((docstring
|
|
||||||
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
|
|
||||||
origin
|
|
||||||
(if (stringp (car body))
|
|
||||||
(pop body)
|
|
||||||
"No documentation."))))
|
|
||||||
(condition-case-unless-debug e
|
|
||||||
(if alt-sexp
|
|
||||||
(read alt-sexp)
|
|
||||||
(append
|
|
||||||
(list (pcase type
|
|
||||||
(`defun 'defmacro)
|
|
||||||
(`cl-defun `cl-defmacro)
|
|
||||||
(_ type))
|
|
||||||
name arglist docstring)
|
|
||||||
(cl-loop for arg in arglist
|
|
||||||
if (and (symbolp arg)
|
|
||||||
(not (keywordp arg))
|
|
||||||
(not (memq arg cl--lambda-list-keywords)))
|
|
||||||
collect arg into syms
|
|
||||||
else if (listp arg)
|
|
||||||
collect (car arg) into syms
|
|
||||||
finally return (if syms `((ignore ,@syms))))))
|
|
||||||
('error
|
|
||||||
(print! "- Ignoring autodef %s (%s)" name e)
|
|
||||||
nil))))
|
|
||||||
`(put ',name 'doom-module ',origin)))))
|
|
||||||
|
|
||||||
((eq type 'defalias)
|
|
||||||
(cl-destructuring-bind (_type name target &optional docstring) sexp
|
|
||||||
(let ((name (doom-unquote name))
|
|
||||||
(target (doom-unquote target)))
|
|
||||||
(unless member-p
|
|
||||||
(setq target #'ignore
|
|
||||||
docstring
|
|
||||||
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
|
|
||||||
origin docstring)))
|
|
||||||
(appendq! forms `((put ',name 'doom-module ',origin)
|
|
||||||
(defalias ',name #',target ,docstring))))))
|
|
||||||
|
|
||||||
(member-p (push sexp forms)))))
|
|
||||||
forms))
|
|
||||||
|
|
||||||
(defun doom--cli-generate-autodefs (targets enabled-targets)
|
|
||||||
(goto-char (point-max))
|
|
||||||
(search-backward ";;;***" nil t)
|
|
||||||
(save-excursion (insert "\n"))
|
|
||||||
(dolist (path targets)
|
|
||||||
(insert
|
|
||||||
(with-temp-buffer
|
|
||||||
(insert-file-contents path)
|
|
||||||
(if-let (forms (doom--cli-generate-autodefs-1 path (member path enabled-targets)))
|
|
||||||
(concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
|
|
||||||
"\n")
|
|
||||||
"")))))
|
|
||||||
|
|
||||||
(defun doom--cli-cleanup-autoloads ()
|
|
||||||
(goto-char (point-min))
|
|
||||||
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
|
|
||||||
(replace-match "" t t)))
|
|
||||||
|
|
||||||
(defun doom-cli-reload-core-autoloads (&optional force-p)
|
|
||||||
"Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
|
|
||||||
|
|
||||||
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
|
|
||||||
modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
|
|
||||||
`doom-autoload-file'.
|
|
||||||
|
|
||||||
Run this whenever your `doom!' block, or a module autoload file, is modified."
|
|
||||||
(require 'autoload)
|
|
||||||
(let* ((default-directory doom-emacs-dir)
|
|
||||||
(doom-modules (doom-modules))
|
|
||||||
|
|
||||||
;; The following bindings are in `package-generate-autoloads'.
|
|
||||||
;; Presumably for a good reason, so I just copied them
|
|
||||||
(backup-inhibited t)
|
|
||||||
(version-control 'never)
|
|
||||||
(case-fold-search nil) ; reduce magic
|
|
||||||
(autoload-timestamps nil)
|
|
||||||
|
|
||||||
;; Where we'll store the files we'll scan for autoloads. This should
|
|
||||||
;; contain *all* autoload files, even in disabled modules, so we can
|
|
||||||
;; scan those for autodefs. We start with the core libraries.
|
|
||||||
(targets (doom-glob doom-core-dir "autoload/*.el"))
|
|
||||||
;; A subset of `targets' in enabled modules
|
|
||||||
(active-targets (copy-sequence targets)))
|
|
||||||
|
|
||||||
(dolist (path (doom-module-load-path 'all-p))
|
|
||||||
(when-let* ((files (cons (doom-glob path "autoload.el")
|
|
||||||
(doom-files-in (doom-path path "autoload")
|
|
||||||
:match "\\.el$")))
|
|
||||||
(files (delq nil files)))
|
|
||||||
(appendq! targets files)
|
|
||||||
(when (or (doom-module-from-path path 'enabled-only)
|
|
||||||
(file-equal-p path doom-private-dir))
|
|
||||||
(appendq! active-targets files))))
|
|
||||||
|
|
||||||
(print! (start "Checking core autoloads file"))
|
|
||||||
(print-group!
|
|
||||||
(if (and (not force-p)
|
|
||||||
(file-exists-p doom-autoload-file)
|
|
||||||
(not (file-newer-than-file-p doom-emacs-dir doom-autoload-file))
|
|
||||||
(not (cl-loop for dir
|
|
||||||
in (append (doom-glob doom-private-dir "init.el*")
|
|
||||||
targets)
|
|
||||||
if (file-newer-than-file-p dir doom-autoload-file)
|
|
||||||
return t)))
|
|
||||||
(ignore
|
|
||||||
(print! (success "Skipping core autoloads, they are up-to-date"))
|
|
||||||
(doom-load-autoloads-file doom-autoload-file))
|
|
||||||
(if (doom--cli-delete-autoloads-file doom-autoload-file)
|
|
||||||
(print! (success "Deleted old %s") (filename doom-autoload-file))
|
|
||||||
(make-directory (file-name-directory doom-autoload-file) t))
|
|
||||||
|
|
||||||
(print! (start "Regenerating core autoloads file"))
|
|
||||||
(print-group!
|
|
||||||
(with-temp-file doom-autoload-file
|
|
||||||
(doom--cli-generate-header 'doom-cli-reload-core-autoloads)
|
|
||||||
(save-excursion
|
|
||||||
(doom--cli-generate-autoloads active-targets)
|
|
||||||
(print! (success "Generated new autoloads.el")))
|
|
||||||
;; Replace autoload paths (only for module autoloads) with absolute
|
|
||||||
;; paths for faster resolution during load and simpler `load-path'
|
|
||||||
(save-excursion
|
|
||||||
(doom--cli-expand-autoload-paths 'allow-internal-paths)
|
|
||||||
(print! (success "Expanded module autoload paths")))
|
|
||||||
;; Generates stub definitions for functions/macros defined in disabled
|
|
||||||
;; modules, so that you will never get a void-function when you use
|
|
||||||
;; them.
|
|
||||||
(save-excursion
|
|
||||||
(doom--cli-generate-autodefs targets (reverse active-targets))
|
|
||||||
(print! (success "Generated autodefs")))
|
|
||||||
;; Remove byte-compile-inhibiting file variables so we can byte-compile
|
|
||||||
;; the file, and autoload comments.
|
|
||||||
(doom--cli-cleanup-autoloads)
|
|
||||||
(print! (success "Cleaned up autoloads"))))
|
|
||||||
;; Byte compile it to give the file a chance to reveal errors (and buy us a
|
|
||||||
;; few marginal performance boosts)
|
|
||||||
(print! "> Byte-compiling %s..." (relpath doom-autoload-file))
|
|
||||||
(when (doom--cli-byte-compile-file doom-autoload-file)
|
|
||||||
(print-group!
|
|
||||||
(print! (success "Compiled %s") (relpath doom-autoload-file)))))
|
|
||||||
t)))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;;; Package autoloads
|
|
||||||
|
|
||||||
(defun doom--generate-package-autoloads ()
|
|
||||||
"Concatenates package autoload files, let-binds `load-file-name' around
|
|
||||||
them,and remove unnecessary `provide' statements or blank links."
|
|
||||||
(dolist (pkg (hash-table-keys straight--build-cache))
|
|
||||||
(unless (member pkg doom-autoload-excluded-packages)
|
|
||||||
(let ((file (straight--autoloads-file pkg)))
|
|
||||||
(when (file-exists-p file)
|
|
||||||
(insert-file-contents file)
|
|
||||||
(save-excursion
|
|
||||||
(while (re-search-forward "\\(?:\\_<load-file-name\\|#\\$\\)\\_>" nil t)
|
|
||||||
;; `load-file-name' is meaningless in a concatenated
|
|
||||||
;; mega-autoloads file, so we replace references to it and #$ with
|
|
||||||
;; the file they came from.
|
|
||||||
(unless (doom-point-in-string-or-comment-p)
|
|
||||||
(replace-match (prin1-to-string (abbreviate-file-name file))
|
|
||||||
t t))))
|
|
||||||
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
|
|
||||||
(unless (doom-point-in-string-p)
|
|
||||||
(replace-match "" t t)))
|
|
||||||
(unless (bolp) (insert "\n")))))))
|
|
||||||
|
|
||||||
(defun doom--generate-var-cache ()
|
|
||||||
"Print a `setq' form for expensive-to-initialize variables, so we can cache
|
|
||||||
them in Doom's autoloads file."
|
|
||||||
(doom-initialize-packages)
|
|
||||||
(prin1 `(setq load-path ',load-path
|
|
||||||
auto-mode-alist ',auto-mode-alist
|
|
||||||
Info-directory-list ',Info-directory-list
|
|
||||||
doom-disabled-packages ',doom-disabled-packages)
|
|
||||||
(current-buffer)))
|
|
||||||
|
|
||||||
(defun doom--cleanup-package-autoloads ()
|
|
||||||
"Remove (some) forms that modify `load-path' or `auto-mode-alist'.
|
|
||||||
|
|
||||||
These variables are cached all at once and at later, so these removed statements
|
|
||||||
served no purpose but to waste cycles."
|
|
||||||
(while (re-search-forward "^\\s-*\\((\\(?:add-to-list\\|\\(?:when\\|if\\) (boundp\\)\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\)" nil t)
|
|
||||||
(goto-char (match-beginning 1))
|
|
||||||
(kill-sexp)))
|
|
||||||
|
|
||||||
(defun doom-cli-reload-package-autoloads (&optional force-p)
|
|
||||||
"Compiles `doom-package-autoload-file' from the autoloads files of all
|
|
||||||
installed packages. It also caches `load-path', `Info-directory-list',
|
|
||||||
`doom-disabled-packages', `package-activated-list' and `auto-mode-alist'.
|
|
||||||
|
|
||||||
Will do nothing if none of your installed packages have been modified. If
|
|
||||||
FORCE-P (universal argument) is non-nil, regenerate it anyway.
|
|
||||||
|
|
||||||
This should be run whenever your `doom!' block or update your packages."
|
|
||||||
(require 'autoload)
|
|
||||||
(print! (start "Checking package autoloads file"))
|
|
||||||
(print-group!
|
(print-group!
|
||||||
(if (and (not force-p)
|
(let ((autoload-files
|
||||||
(file-exists-p doom-package-autoload-file)
|
(cl-loop for dir in (append (list doom-core-dir)
|
||||||
(not (file-newer-than-file-p package-user-dir doom-package-autoload-file))
|
(cdr (doom-module-load-path 'all-p))
|
||||||
(not (cl-loop for dir in (straight--directory-files (straight--build-dir))
|
(list doom-private-dir))
|
||||||
if (cl-find-if
|
if (doom-glob dir "autoload.el") collect it
|
||||||
(lambda (dir)
|
if (doom-glob dir "autoload/*.el") append it)))
|
||||||
(file-newer-than-file-p dir doom-package-autoload-file))
|
(if (or force-p
|
||||||
(doom-glob (straight--build-dir dir) "*.el"))
|
(not (file-exists-p file))
|
||||||
return t))
|
(file-newer-than-file-p doom-emacs-dir file)
|
||||||
(not (cl-loop with doom-modules = (doom-modules)
|
(cl-loop for dir
|
||||||
for key being the hash-keys of doom-modules
|
in (append (doom-glob doom-private-dir "init.el*")
|
||||||
for path = (doom-module-path (car key) (cdr key) "packages.el")
|
autoload-files)
|
||||||
if (file-newer-than-file-p path doom-package-autoload-file)
|
if (file-newer-than-file-p dir doom-autoload-file)
|
||||||
return t)))
|
return t))
|
||||||
(ignore
|
(and (print! (start "Generating core autoloads..."))
|
||||||
(print! (success "Skipping package autoloads, they are up-to-date"))
|
(doom-cli--write-autoloads
|
||||||
(doom-load-autoloads-file doom-package-autoload-file))
|
file (doom-cli--generate-autoloads autoload-files 'scan))
|
||||||
(let (;; The following bindings are in `package-generate-autoloads'.
|
(print! (start "Byte-compiling core autoloads file..."))
|
||||||
|
(doom-cli--byte-compile-file file)
|
||||||
|
(print! (success "Generated %s")
|
||||||
|
(relpath (byte-compile-dest-file file)
|
||||||
|
doom-emacs-dir)))
|
||||||
|
(print! (success "Core autoloads are up-to-date"))
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
(defun doom-cli-reload-package-autoloads (file &optional force-p)
|
||||||
|
(cl-check-type file string)
|
||||||
|
(print! (start "(Re)generating package autoloads..."))
|
||||||
|
(print-group!
|
||||||
|
(doom-initialize-packages)
|
||||||
|
(if (or force-p
|
||||||
|
(not (file-exists-p file))
|
||||||
|
(file-newer-than-file-p package-user-dir file)
|
||||||
|
(cl-loop for dir in (straight--directory-files (straight--build-dir))
|
||||||
|
if (cl-find-if
|
||||||
|
(doom-rpartial #'file-newer-than-file-p doom-package-autoload-file)
|
||||||
|
(doom-glob (straight--build-dir dir) "*.el"))
|
||||||
|
return t)
|
||||||
|
(not (cl-loop with doom-modules = (doom-modules)
|
||||||
|
for key being the hash-keys of doom-modules
|
||||||
|
for path = (doom-module-path (car key) (cdr key) "packages.el")
|
||||||
|
if (file-newer-than-file-p path doom-package-autoload-file)
|
||||||
|
return t)))
|
||||||
|
(and (print! (start "Generating package autoloads..."))
|
||||||
|
(doom-cli--write-autoloads
|
||||||
|
file
|
||||||
|
(doom-cli--generate-var-cache doom-autoload-cached-vars)
|
||||||
|
(doom-cli--generate-autoloads
|
||||||
|
(mapcar #'straight--autoloads-file
|
||||||
|
(cl-set-difference (hash-table-keys straight--build-cache)
|
||||||
|
doom-autoload-excluded-packages
|
||||||
|
:test #'string=))))
|
||||||
|
(print! (start "Byte-compiling package autoloads file..."))
|
||||||
|
(doom-cli--byte-compile-file file)
|
||||||
|
(print! (success "Generated %s")
|
||||||
|
(relpath (byte-compile-dest-file file)
|
||||||
|
doom-emacs-dir)))
|
||||||
|
(print! (success "Package autoloads are up-to-date"))
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;;; Helpers
|
||||||
|
|
||||||
|
(defun doom-cli--write-autoloads (file &rest forms)
|
||||||
|
(make-directory (file-name-directory file) 'parents)
|
||||||
|
(condition-case-unless-debug e
|
||||||
|
(with-temp-file file
|
||||||
|
(let ((standard-output (current-buffer))
|
||||||
|
(print-quoted t)
|
||||||
|
(print-level nil)
|
||||||
|
(print-length nil))
|
||||||
|
(insert ";; -*- lexical-binding: t; -*-\n"
|
||||||
|
";; This file is autogenerated by Doom, DO NOT EDIT IT!!\n")
|
||||||
|
(dolist (form (delq nil forms))
|
||||||
|
(mapc #'print form))
|
||||||
|
t))
|
||||||
|
(error (delete-file file)
|
||||||
|
(signal 'doom-autoload-error (list file e)))))
|
||||||
|
|
||||||
|
(defun doom-cli--byte-compile-file (file)
|
||||||
|
(condition-case-unless-debug e
|
||||||
|
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings))
|
||||||
|
(byte-compile-dynamic t)
|
||||||
|
(byte-compile-dynamic-docstrings t))
|
||||||
|
(when (byte-compile-file file)
|
||||||
|
(unless doom-interactive-mode
|
||||||
|
(add-hook 'doom-cli-post-success-execute-hook #'doom-cli--warn-refresh-session-h))
|
||||||
|
(load (byte-compile-dest-file file) nil t)))
|
||||||
|
(error
|
||||||
|
(delete-file (byte-compile-dest-file file))
|
||||||
|
(signal 'doom-autoload-error (list file e)))))
|
||||||
|
|
||||||
|
(defun doom-cli--warn-refresh-session-h ()
|
||||||
|
(print! "Restart or reload Doom Emacs for changes to take effect:")
|
||||||
|
(print-group! (print! "M-x doom/restart-and-restore")
|
||||||
|
(print! "M-x doom/restart")
|
||||||
|
(print! "M-x doom/reload")))
|
||||||
|
|
||||||
|
(defun doom-cli--generate-var-cache (vars)
|
||||||
|
`((setq ,@(cl-loop for var in vars
|
||||||
|
append `(,var ',(symbol-value var))))))
|
||||||
|
|
||||||
|
(defun doom-cli--filter-form (form &optional expand)
|
||||||
|
(let ((func (car-safe form)))
|
||||||
|
(cond ((memq func '(provide custom-autoload))
|
||||||
|
nil)
|
||||||
|
((and (eq func 'add-to-list)
|
||||||
|
(memq (doom-unquote (cadr form))
|
||||||
|
doom-autoload-cached-vars))
|
||||||
|
nil)
|
||||||
|
((not (eq func 'autoload))
|
||||||
|
form)
|
||||||
|
((and expand (not (file-name-absolute-p (nth 2 form))))
|
||||||
|
(defvar doom--autoloads-path-cache nil)
|
||||||
|
(setf (nth 2 form)
|
||||||
|
(let ((path (nth 2 form)))
|
||||||
|
(or (cdr (assoc path doom--autoloads-path-cache))
|
||||||
|
(when-let* ((libpath (locate-library path))
|
||||||
|
(libpath (file-name-sans-extension libpath))
|
||||||
|
(libpath (abbreviate-file-name libpath)))
|
||||||
|
(push (cons path libpath) doom--autoloads-path-cache)
|
||||||
|
libpath)
|
||||||
|
path)))
|
||||||
|
form)
|
||||||
|
(form))))
|
||||||
|
|
||||||
|
(defun doom-cli--generate-autoloads-autodefs (file buffer module &optional module-enabled-p)
|
||||||
|
(with-current-buffer
|
||||||
|
(or (get-file-buffer file)
|
||||||
|
(autoload-find-file file))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
|
||||||
|
(let* ((standard-output buffer)
|
||||||
|
(form (read (current-buffer)))
|
||||||
|
(altform (match-string 1))
|
||||||
|
(definer (car-safe form))
|
||||||
|
(symbol (doom-unquote (cadr form))))
|
||||||
|
(cond ((and (not module-enabled-p) altform)
|
||||||
|
(print (read altform)))
|
||||||
|
((memq definer '(defun defmacro cl-defun cl-defmacro))
|
||||||
|
(if module-enabled-p
|
||||||
|
(print (make-autoload form file))
|
||||||
|
(cl-destructuring-bind (_ _ arglist &rest body) form
|
||||||
|
(print
|
||||||
|
(if altform
|
||||||
|
(read altform)
|
||||||
|
(append
|
||||||
|
(list (pcase definer
|
||||||
|
(`defun 'defmacro)
|
||||||
|
(`cl-defun `cl-defmacro)
|
||||||
|
(_ type))
|
||||||
|
symbol arglist
|
||||||
|
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
|
||||||
|
module
|
||||||
|
(if (stringp (car body))
|
||||||
|
(pop body)
|
||||||
|
"No documentation.")))
|
||||||
|
(cl-loop for arg in arglist
|
||||||
|
if (and (symbolp arg)
|
||||||
|
(not (keywordp arg))
|
||||||
|
(not (memq arg cl--lambda-list-keywords)))
|
||||||
|
collect arg into syms
|
||||||
|
else if (listp arg)
|
||||||
|
collect (car arg) into syms
|
||||||
|
finally return (if syms `((ignore ,@syms)))))))))
|
||||||
|
(print `(put ',symbol 'doom-module ',module)))
|
||||||
|
((eq definer 'defalias)
|
||||||
|
(cl-destructuring-bind (_ _ target &optional docstring) form
|
||||||
|
(unless module-enabled-p
|
||||||
|
(setq target #'ignore
|
||||||
|
docstring
|
||||||
|
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
|
||||||
|
module docstring)))
|
||||||
|
(print `(put ',symbol 'doom-module ',module))
|
||||||
|
(print `(defalias ',symbol #',(doom-unquote target) ,docstring))))
|
||||||
|
(module-enabled-p (print form)))))))
|
||||||
|
|
||||||
|
(defun doom-cli--generate-autoloads-buffer (file)
|
||||||
|
(when (doom-file-cookie-p file "if" t)
|
||||||
|
(let* (;; Prevent `autoload-find-file' from firing file hooks, e.g. adding
|
||||||
|
;; to recentf.
|
||||||
|
find-file-hook
|
||||||
|
write-file-functions
|
||||||
|
;; Prevent a possible source of crashes when there's a syntax error
|
||||||
|
;; in the autoloads file
|
||||||
|
debug-on-error
|
||||||
|
;; The following bindings are in `package-generate-autoloads'.
|
||||||
;; Presumably for a good reason, so I just copied them
|
;; Presumably for a good reason, so I just copied them
|
||||||
(backup-inhibited t)
|
(backup-inhibited t)
|
||||||
(version-control 'never)
|
(version-control 'never)
|
||||||
(case-fold-search nil) ; reduce magic
|
case-fold-search ; reduce magic
|
||||||
(autoload-timestamps nil))
|
autoload-timestamps ; reduce noise in generated files
|
||||||
|
;; Needed for `autoload-generate-file-autoloads'
|
||||||
|
(generated-autoload-load-name (file-name-sans-extension file))
|
||||||
|
(target-buffer (current-buffer))
|
||||||
|
(module (doom-module-from-path file))
|
||||||
|
(module-enabled-p (or (memq (car module) '(:core :private))
|
||||||
|
(doom-module-p (car module) (cdr module)))))
|
||||||
|
(save-excursion
|
||||||
|
(when module-enabled-p
|
||||||
|
(quiet! (autoload-generate-file-autoloads file target-buffer)))
|
||||||
|
(doom-cli--generate-autoloads-autodefs
|
||||||
|
file target-buffer module module-enabled-p)))))
|
||||||
|
|
||||||
(if (doom--cli-delete-autoloads-file doom-package-autoload-file)
|
(defun doom-cli--generate-autoloads (files &optional scan)
|
||||||
(print! (success "Deleted old %s") (filename doom-package-autoload-file))
|
(require 'autoload)
|
||||||
(make-directory (file-name-directory doom-autoload-file) t))
|
(let (autoloads)
|
||||||
|
(dolist (file files (delq nil (nreverse autoloads)))
|
||||||
(print! (start "Regenerating package autoloads file"))
|
(and (file-readable-p file)
|
||||||
(print-group!
|
(with-temp-buffer
|
||||||
(with-temp-file doom-package-autoload-file
|
(save-excursion
|
||||||
(doom--cli-generate-header 'doom-cli-reload-package-autoloads)
|
(if scan
|
||||||
|
(doom-cli--generate-autoloads-buffer file)
|
||||||
(save-excursion
|
(insert-file-contents-literally file)))
|
||||||
;; Cache important and expensive-to-initialize state here.
|
(save-excursion
|
||||||
(doom--generate-var-cache)
|
(let ((filestr (prin1-to-string file)))
|
||||||
(print! (success "Cached package state"))
|
(while (re-search-forward "\\_<load-file-name\\_>" nil t)
|
||||||
;; Concatenate the autoloads of all installed packages.
|
;; `load-file-name' is meaningless in a concatenated
|
||||||
(doom--generate-package-autoloads)
|
;; mega-autoloads file, so we replace references to it with the
|
||||||
(print! (success "Package autoloads included")))
|
;; file they came from.
|
||||||
|
(replace-match filestr t t))))
|
||||||
;; Replace autoload paths (only for module autoloads) with absolute
|
(let ((load-file-name file)
|
||||||
;; paths for faster resolution during load and simpler `load-path'
|
(load-path
|
||||||
(save-excursion
|
(append (list doom-private-dir)
|
||||||
(doom--cli-expand-autoload-paths)
|
doom-modules-dirs
|
||||||
(print! (success "Expanded module autoload paths")))
|
load-path)))
|
||||||
|
(condition-case _
|
||||||
;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
|
(while t
|
||||||
;; at least); they are cached later, so all those membership checks are
|
(push (doom-cli--filter-form (read (current-buffer))
|
||||||
;; unnecessary overhead.
|
scan)
|
||||||
(doom--cleanup-package-autoloads)
|
autoloads))
|
||||||
(print! (success "Removed load-path/auto-mode-alist entries"))))
|
(end-of-file))))))))
|
||||||
;; Byte compile it to give the file a chance to reveal errors (and buy us a
|
|
||||||
;; few marginal performance boosts)
|
|
||||||
(print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file))
|
|
||||||
(when (doom--cli-byte-compile-file doom-package-autoload-file)
|
|
||||||
(print-group!
|
|
||||||
(print! (success "Compiled %s") (relpath doom-package-autoload-file))))))
|
|
||||||
t))
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ between HEAD and FETCH_HEAD. This can take a while.
|
||||||
This excludes packages whose `package!' declaration contains a non-nil :freeze
|
This excludes packages whose `package!' declaration contains a non-nil :freeze
|
||||||
or :ignore property."
|
or :ignore property."
|
||||||
(straight-check-all)
|
(straight-check-all)
|
||||||
(doom-cli-reload-core-autoloads)
|
(doom-cli-reload-autoloads 'core)
|
||||||
(when (doom-cli-packages-update)
|
(when (doom-cli-packages-update)
|
||||||
(doom-cli-reload-package-autoloads 'force-p))
|
(doom-cli-reload-autoloads 'package 'force))
|
||||||
t)
|
t)
|
||||||
|
|
||||||
(defcli! (build b)
|
(defcli! (build b)
|
||||||
|
@ -23,7 +23,7 @@ This ensures that all needed files are symlinked from their package repo and
|
||||||
their elisp files are byte-compiled. This is especially necessary if you upgrade
|
their elisp files are byte-compiled. This is especially necessary if you upgrade
|
||||||
Emacs (as byte-code is generally not forward-compatible)."
|
Emacs (as byte-code is generally not forward-compatible)."
|
||||||
(when (doom-cli-packages-build (not rebuild-p))
|
(when (doom-cli-packages-build (not rebuild-p))
|
||||||
(doom-cli-reload-package-autoloads 'force-p))
|
(doom-cli-reload-autoloads 'package 'force))
|
||||||
t)
|
t)
|
||||||
|
|
||||||
(defcli! (purge p)
|
(defcli! (purge p)
|
||||||
|
@ -46,7 +46,7 @@ list remains lean."
|
||||||
(not norepos-p)
|
(not norepos-p)
|
||||||
(not nobuilds-p)
|
(not nobuilds-p)
|
||||||
regraft-p)
|
regraft-p)
|
||||||
(doom-cli-reload-package-autoloads 'force-p))
|
(doom-cli-reload-autoloads 'package 'force))
|
||||||
t)
|
t)
|
||||||
|
|
||||||
;; (defcli! rollback () ; TODO doom rollback
|
;; (defcli! rollback () ; TODO doom rollback
|
||||||
|
@ -310,7 +310,7 @@ a `package!' declaration) or isn't depended on by another primary package.
|
||||||
If BUILDS-P, include straight package builds.
|
If BUILDS-P, include straight package builds.
|
||||||
If REPOS-P, include straight repos.
|
If REPOS-P, include straight repos.
|
||||||
If ELPA-P, include packages installed with package.el (M-x package-install)."
|
If ELPA-P, include packages installed with package.el (M-x package-install)."
|
||||||
(print! (start "Searching for orphaned packages to purge (for the emperor)..."))
|
(print! (start "Purging orphaned packages (for the emperor)..."))
|
||||||
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
|
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
|
||||||
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
|
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
|
||||||
(bdirs (straight--directory-files (straight--build-dir) nil nil 'sort)))
|
(bdirs (straight--directory-files (straight--build-dir) nil nil 'sort)))
|
||||||
|
|
|
@ -248,13 +248,14 @@ stale."
|
||||||
|
|
||||||
;; Ensures that no pre-existing state pollutes the generation of the new
|
;; Ensures that no pre-existing state pollutes the generation of the new
|
||||||
;; autoloads files.
|
;; autoloads files.
|
||||||
(mapc #'doom--cli-delete-autoloads-file
|
(dolist (file (list doom-autoload-file doom-package-autoload-file))
|
||||||
(list doom-autoload-file
|
(delete-file file)
|
||||||
doom-package-autoload-file))
|
(delete-file (byte-compile-dest-file file)))
|
||||||
|
|
||||||
(doom-initialize 'force 'noerror)
|
(doom-initialize 'force 'noerror)
|
||||||
(doom-initialize-modules)
|
(doom-initialize-modules)
|
||||||
|
|
||||||
(doom-cli-reload-core-autoloads (not if-necessary-p))
|
(doom-cli-reload-autoloads 'core (not if-necessary-p))
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
(and (doom-cli-packages-install)
|
(and (doom-cli-packages-install)
|
||||||
|
@ -263,7 +264,7 @@ stale."
|
||||||
(setq success t))
|
(setq success t))
|
||||||
(and (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p)
|
(and (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p)
|
||||||
(setq success t)))
|
(setq success t)))
|
||||||
(doom-cli-reload-package-autoloads (or success (not if-necessary-p)))
|
(doom-cli-reload-autoloads 'package (or success (not if-necessary-p)))
|
||||||
(doom-cli-byte-compile nil 'recompile))
|
(doom-cli-byte-compile nil 'recompile))
|
||||||
t)))
|
t)))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue