💥 Replace package.el/quelpa with straight #374

There are a few kinks to iron out, but for the most part it's done. Doom
Emacs, powered by straight. Goodbye gnutls and elpa/quelpa issues.

This update doesn't come with rollback or lockfile support yet, but I
will eventually include one with Doom, and packages will be (by default,
anyway) updated in sync with Doom.

Relevant threads: #1577 #1566 #1473
This commit is contained in:
Henrik Lissner 2019-07-21 15:39:45 +02:00
parent 492f2dea1e
commit b90dede1ab
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
35 changed files with 1542 additions and 1771 deletions

View file

@ -1,7 +1,6 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(dispatcher! (autoloads a)
(doom-reload-autoloads nil 'force)
(def-command! (autoloads a) ()
"Regenerates Doom's autoloads files.
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
@ -10,7 +9,8 @@ byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file'
(created from the concatenated autoloads files of all installed packages).
It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
`package-activated-list' and `auto-mode-alist'.")
`package-activated-list' and `auto-mode-alist'."
(doom-reload-autoloads nil 'force))
;; external variables
(defvar autoload-timestamps)
@ -21,55 +21,45 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
;;
;;; Helpers
(defvar doom-autoload-excluded-packages '(marshal gh)
"Packages that have silly or destructive autoload files that try to load
everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.")
(defun doom-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 doom-autoload-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)))
(message "Deleted old %s" (file-name-nondirectory file))))
t))
(defun doom--warn-refresh-session ()
(print! (bold (green "\nFinished!")))
(message "If you have a running Emacs Session, you will need to restart it or")
(message "reload Doom for changes to take effect:\n")
(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--reload-files (&rest files)
(if (not noninteractive)
(dolist (file files)
(load-file (byte-compile-dest-file file)))
(add-hook 'kill-emacs-hook #'doom--warn-refresh-session)))
(if noninteractive
(add-hook 'doom-cli-post-execute-hook #'doom--warn-refresh-session)
(dolist (file files)
(load-file (byte-compile-dest-file file)))))
(defun doom--byte-compile-file (file)
(let ((short-name (file-name-nondirectory file))
(byte-compile-dynamic-docstrings t))
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)))
(condition-case e
(when (byte-compile-file file)
;; Give autoloads file a chance to report error
(load (if doom-debug-mode
file
(byte-compile-dest-file file))
nil t)
(unless noninteractive
(message "Finished compiling %s" short-name)))
nil t))
((debug error)
(let ((backup-file (concat file ".bk")))
(message "Copied backup to %s" backup-file)
(print! (warn "- Copied backup to %s") (relpath backup-file))
(copy-file file backup-file 'overwrite))
(doom-delete-autoloads-file file)
(signal 'doom-autoload-error (list short-name e))))))
(signal 'doom-autoload-error (list file e))))))
(defun doom-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading.
@ -82,71 +72,133 @@ even if it doesn't need reloading!"
(signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file)
(cond ((file-equal-p file doom-autoload-file)
(doom-reload-doom-autoloads force-p))
(doom-reload-core-autoloads force-p))
((file-equal-p file doom-package-autoload-file)
(doom-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file)))
(doom-reload-doom-autoloads force-p)
(doom-reload-core-autoloads force-p)
(doom-reload-package-autoloads force-p)))
;;
;;; Doom autoloads
(defun doom--file-cookie-p (file)
"Returns the return value of the ;;;###if predicate form in FILE."
(with-temp-buffer
(insert-file-contents-literally file nil 0 256)
(if (and (re-search-forward "^;;;###if " nil t)
(<= (line-number-at-pos) 3))
(let ((load-file-name file))
(eval (sexp-at-point)))
t)))
(defun doom--generate-header (func)
(goto-char (point-min))
(insert ";; -*- lexical-binding:t -*-\n"
(insert ";; -*- lexical-binding:t; byte-compile-dynamic-docstrings: t; -*-\n"
";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
(defun doom--generate-autoloads (targets)
(require 'autoload)
(dolist (file targets)
(let* ((file (file-truename file))
(generated-autoload-file doom-autoload-file)
(generated-autoload-load-name (file-name-sans-extension file))
(noninteractive (not doom-debug-mode))
autoload-timestamps)
(print!
(cond ((not (doom--file-cookie-p file))
"⚠ Ignoring %s")
((autoload-generate-file-autoloads file (current-buffer))
(yellow "✕ Nothing in %s"))
((green "✓ Scanned %s")))
(if (file-in-directory-p file default-directory)
(file-relative-name file)
(abbreviate-file-name file))))))
(let ((n 0))
(dolist (file targets)
(insert
(with-temp-buffer
(cond ((not (doom-file-cookie-p file))
(print! (debug "Ignoring %s") (relpath file)))
(defun doom--expand-autoloads ()
((let ((generated-autoload-load-name (file-name-sans-extension file)))
(require 'autoload)
(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--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-emacs-dir)
(append (list doom-private-dir)
doom-modules-dirs
load-path))
cache)
(while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(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 cache))
(when-let* ((libpath (locate-library path))
(libpath (file-name-sans-extension libpath)))
(push (cons path (abbreviate-file-name libpath)) cache)
(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--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 (cond ((doom-module-from-path path))
((file-in-directory-p path doom-private-dir)
`(:private . ,(intern (file-name-base path))))
((file-in-directory-p path doom-emacs-dir)
`(:core . ,(intern (file-name-base 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
(let ((docstring (if (stringp (car body))
(pop body)
"No documentation.")))
(appendq!
forms
(list (if member-p
(make-autoload sexp (abbreviate-file-name (file-name-sans-extension path)))
(setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin docstring))
(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--generate-autodefs (targets enabled-targets)
(goto-char (point-max))
(search-backward ";;;***" nil t)
@ -155,83 +207,17 @@ even if it doesn't need reloading!"
(insert
(with-temp-buffer
(insert-file-contents path)
(let ((member-p (or (member path enabled-targets)
(file-in-directory-p path doom-core-dir)))
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 (cond ((doom-module-from-path path))
((file-in-directory-p path doom-private-dir)
`(:private . ,(intern (file-name-base path))))
((file-in-directory-p path doom-emacs-dir)
`(:core . ,(intern (file-name-base path))))))
(doom-file-form
`(put ',name 'doom-file ,(abbreviate-file-name 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
(let ((docstring (if (stringp (car body))
(pop body)
"No documentation.")))
(push (if member-p
(make-autoload sexp (abbreviate-file-name (file-name-sans-extension path)))
(push doom-file-form forms)
(setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin docstring))
(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
(message "Ignoring autodef %s (%s)"
name e)
nil)))
forms)
(push `(put ',name 'doom-module ',origin) forms))))
((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 docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin docstring))
(setq target #'ignore))
(push doom-file-form forms)
(push `(put ',name 'doom-module ',origin) forms)
(push `(defalias ',name #',target ,docstring)
forms))))
(member-p
(push sexp forms)))))
(if forms
(concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
"\n")
""))))))
(if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets)))
(concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
"\n")
"")))))
(defun doom--cleanup-autoloads ()
(goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t t)))
(defun doom-reload-doom-autoloads (&optional force-p)
(defun doom-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,
@ -241,62 +227,78 @@ modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
Run this whenever your `doom!' block, or a module autoload file, is modified."
(let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))
(abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))
(targets
(file-expand-wildcards
(expand-file-name "autoload/*.el" doom-core-dir)))
(enabled-targets (copy-sequence targets))
case-fold-search)
(dolist (path (doom-module-load-path t))
(let* ((auto-dir (expand-file-name "autoload" path))
(auto-file (expand-file-name "autoload.el" path))
(module (doom-module-from-path auto-file))
(module-p (or (doom-module-p (car module) (cdr module))
(file-equal-p path doom-private-dir))))
(when (file-exists-p auto-file)
(push auto-file targets)
(if module-p (push auto-file enabled-targets)))
(dolist (file (doom-files-in auto-dir :match "\\.el$" :full t :sort nil))
(push file targets)
(if module-p (push file enabled-targets)))))
(if (and (not force-p)
(not doom-emacs-changed-p)
(file-exists-p doom-autoload-file)
(not (file-newer-than-file-p (expand-file-name "init.el" doom-private-dir)
doom-autoload-file))
(not (cl-loop for file in targets
if (file-newer-than-file-p file doom-autoload-file)
return t)))
(progn (print! (green "Doom core autoloads is up-to-date"))
(doom-initialize-autoloads doom-autoload-file)
nil)
(doom-delete-autoloads-file doom-autoload-file)
(message "Generating new autoloads.el")
(make-directory (file-name-directory doom-autoload-file) t)
(with-temp-file doom-autoload-file
(doom--generate-header 'doom-reload-doom-autoloads)
(prin1 `(setq doom--modules-cache ',doom-modules) (current-buffer))
(save-excursion
(doom--generate-autoloads (reverse enabled-targets)))
;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path'
(save-excursion
(doom--expand-autoloads)
(print! (green "✓ 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--generate-autodefs (reverse targets) enabled-targets)
(print! (green "✓ Generated autodefs")))
;; Remove byte-compile-inhibiting file variables so we can byte-compile
;; the file, and autoload comments.
(doom--cleanup-autoloads)
(print! (green "✓ Clean up autoloads")))
;; Byte compile it to give the file a chance to reveal errors.
(doom--byte-compile-file doom-autoload-file)
(doom--reload-files doom-autoload-file)
t)))
;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them
(noninteractive t)
(backup-inhibited t)
(version-control 'never)
(case-fold-search nil) ; reduce magit
(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-initialize-autoloads doom-autoload-file))
(print! (start "Regenerating core autoloads file"))
(if (doom-delete-autoloads-file doom-autoload-file)
(print! (success "Deleted old %s") (filename doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
(with-temp-file doom-autoload-file
(doom--generate-header 'doom-reload-core-autoloads)
(save-excursion
(doom--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--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--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--cleanup-autoloads)
(print! (success "Clean 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--byte-compile-file doom-autoload-file)
(print! (success "Finished compiling %s") (relpath doom-autoload-file)))
(doom--reload-files doom-autoload-file))
t)))
;;
@ -304,33 +306,49 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
(defun doom--generate-package-autoloads ()
"Concatenates package autoload files, let-binds `load-file-name' around
them,and remove unnecessary `provide' statements or blank links.
Skips over packages in `doom-autoload-excluded-packages'."
(dolist (spec (doom-get-package-alist))
(if-let* ((pkg (car spec))
(desc (cdr spec)))
(unless (memq pkg doom-autoload-excluded-packages)
(let ((file (concat (package--autoloads-file-name desc) ".el")))
(when (file-exists-p file)
(insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n")
(insert-file-contents file)
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
(unless (nth 8 (syntax-ppss))
(replace-match "" t t)))
(unless (bolp) (insert "\n"))
(insert ")\n"))))
(message "Couldn't find package desc for %s" (car spec)))))
them,and remove unnecessary `provide' statements or blank links."
(dolist (pkg (straight--directory-files (straight--build-dir)))
(let ((file (straight--autoloads-file pkg)))
(when (file-exists-p file)
(insert-file-contents file)
(when (save-excursion
(and (re-search-forward "\\_<load-file-name\\_>" nil t)
(not (nth 8 (syntax-ppss)))))
;; Set `load-file-name' so that the contents of autoloads
;; files can pretend they're in the file they're expected to
;; be in, rather than `doom-package-autoload-file'.
(insert (format "(setq load-file-name %S)\n" (abbreviate-file-name file))))
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
(unless (nth 8 (syntax-ppss))
(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
auto-mode-alist
',(let ((alist (copy-sequence auto-mode-alist))
newalist
last-group
last-mode
it)
(while (setq it (pop alist))
(cl-destructuring-bind (re . mode) it
(unless (eq mode last-mode)
(when last-mode
(push (cons (if (cdr last-group)
(concat "\\(?:" (string-join last-group "\\)\\|\\(?:") "\\)")
(car last-group))
last-mode)
newalist))
(setq last-mode mode
last-group nil))
(push re last-group)))
(nreverse newalist))
Info-directory-list ',Info-directory-list
doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t))
package-activated-list ',package-activated-list)
doom-disabled-packages ',doom-disabled-packages)
(current-buffer)))
(defun doom--cleanup-package-autoloads ()
@ -351,34 +369,63 @@ 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."
(let ((abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir)))
(if (and (not force-p)
(not doom-emacs-changed-p)
(file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p doom-packages-dir doom-package-autoload-file))
(not (ignore-errors
(cl-loop 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))))
(ignore (print! (green "Doom package autoloads is up-to-date"))
(doom-initialize-autoloads doom-package-autoload-file))
(let (case-fold-search)
(doom-delete-autoloads-file doom-package-autoload-file)
(with-temp-file doom-package-autoload-file
(doom--generate-header 'doom-reload-package-autoloads)
(save-excursion
;; Cache important and expensive-to-initialize state here.
(doom--generate-var-cache)
(print! (green "✓ Cached package state"))
;; Concatenate the autoloads of all installed packages.
(doom--generate-package-autoloads)
(print! (green "✓ Package autoloads included")))
;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
;; at least); they are cached later, so all those membership checks are
;; unnecessary overhead.
(doom--cleanup-package-autoloads)
(print! (green "✓ Removed load-path/auto-mode-alist entries"))))
(doom--byte-compile-file doom-package-autoload-file)
(doom--reload-files doom-package-autoload-file)
t)))
(print! (start "Checking package autoloads file"))
(print-group!
(if (and (not force-p)
(file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file))
(not (ignore-errors
(cl-loop for dir in (straight--directory-files (straight--repos-dir))
if (cl-find-if (lambda (dir) (file-newer-than-file-p dir doom-package-autoload-file))
(glob! (straight--repos-dir dir) "*.el"))
return t)))
(not (ignore-errors
(cl-loop 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))))
(ignore
(print! (success "Skipping package autoloads, they are up-to-date"))
(doom-initialize-autoloads doom-package-autoload-file))
(let (;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them
(noninteractive t)
(backup-inhibited t)
(version-control 'never)
(case-fold-search nil) ; reduce magit
(autoload-timestamps nil))
(print! (start "Regenerating package autoloads file"))
(if (doom-delete-autoloads-file doom-package-autoload-file)
(print! (success "Deleted old %s") (filename doom-package-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
(with-temp-file doom-package-autoload-file
(doom--generate-header 'doom-reload-package-autoloads)
(save-excursion
;; Cache important and expensive-to-initialize state here.
(doom--generate-var-cache)
(print! (success "Cached package state"))
;; Concatenate the autoloads of all installed packages.
(doom--generate-package-autoloads)
(print! (success "Package autoloads included")))
;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path'
(save-excursion
(doom--expand-autoload-paths)
(print! (success "Expanded module autoload paths")))
;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
;; at least); they are cached later, so all those membership checks are
;; unnecessary overhead.
(doom--cleanup-package-autoloads)
(print! (success "Removed load-path/auto-mode-alist entries")))
;; 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--byte-compile-file doom-package-autoload-file)
(print! (success "Finished compiling %s") (relpath doom-package-autoload-file)))
(doom--reload-files doom-package-autoload-file)))
t))

View file

@ -1,21 +1,24 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*-
(dispatcher! (compile c) (doom-byte-compile args)
(def-command! (compile c) (&rest targets)
"Byte-compiles your config or selected modules.
compile [TARGETS...]
compile :core :private lang/python
compile feature lang
Accepts :core, :private and :plugins as special arguments, indicating you want
to byte-compile Doom's core files, your private config or your ELPA plugins,
respectively.")
Accepts :core and :private as special arguments, which target Doom's core files
and your private config files, respectively. To recompile your packages, use
'doom rebuild' instead."
(doom-byte-compile targets))
(dispatcher! (recompile rc) (doom-byte-compile args 'recompile)
"Re-byte-compiles outdated *.elc files.")
(def-command! (recompile rc) (&rest targets)
"Re-byte-compiles outdated *.elc files."
(doom-byte-compile targets 'recompile))
(dispatcher! clean (doom-clean-byte-compiled-files)
"Delete all *.elc files.")
(def-command! clean ()
"Delete all *.elc files."
(doom-clean-byte-compiled-files))
;;
@ -25,9 +28,10 @@ respectively.")
(let ((filename (file-name-nondirectory path)))
(or (string-prefix-p "." filename)
(string-prefix-p "test-" filename)
(not (equal (file-name-extension path) "el")))))
(not (equal (file-name-extension path) "el"))
(member filename (list "packages.el" "doctor.el")))))
(defun doom-byte-compile (&optional modules recompile-p)
(cl-defun doom-byte-compile (&optional modules recompile-p)
"Byte compiles your emacs configuration.
init.el is always byte-compiled by this.
@ -45,35 +49,35 @@ byte-compilation.
If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir)
(total-ok 0)
(total-fail 0)
(total-noop 0)
compile-plugins-p
(doom-modules (doom-modules))
(byte-compile-verbose doom-debug-mode)
(byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
;; In case it is changed during compile-time
(auto-mode-alist auto-mode-alist)
(noninteractive t)
targets)
(dolist (module (delete-dups modules) (nreverse targets))
(pcase module
(":core" (push doom-core-dir targets))
(":private" (push doom-private-dir targets))
(":plugins"
(cl-loop for (_name . desc) in (doom-get-package-alist)
do (package--compile desc))
(setq compile-plugins-p t
modules (delete ":plugins" modules)))
((pred file-directory-p)
(push module targets))
((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
(push (doom-module-locate-path
(doom-keyword-intern (match-string 1 module))
(intern (match-string 2 module)))
targets))))
(cl-block 'byte-compile
;; If we're just here to byte-compile our plugins, we're done!
(and (not modules)
compile-plugins-p
(cl-return-from 'byte-compile t))
(unless (or (equal modules '(":core"))
recompile-p)
(unless (or doom-auto-accept
(let (target-dirs)
(dolist (module (delete-dups modules))
(pcase module
(":core"
(push (doom-glob doom-emacs-dir "init.el") targets)
(push doom-core-dir target-dirs))
(":private"
(push doom-private-dir target-dirs))
((pred file-directory-p)
(push module target-dirs))
((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
(push (doom-module-locate-path
(doom-keyword-intern (match-string 1 module))
(intern (match-string 2 module)))
target-dirs))))
(and (or (null modules) (member ":private" modules))
(not recompile-p)
(not (or doom-auto-accept
(y-or-n-p
(concat "Warning: byte compiling is for advanced users. It will interfere with your\n"
"efforts to debug issues. It is not recommended you do it if you frequently\n"
@ -82,109 +86,113 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
"Doom core files, as these don't change often.\n\n"
"If you have issues, please make sure byte-compilation isn't the cause by using\n"
"`bin/doom clean` to clear out your *.elc files.\n\n"
"Byte-compile anyway?")))
(message "Aborting.")
(cl-return-from 'byte-compile)))
(when (and (not recompile-p)
(or (null modules)
(equal modules '(":core"))))
(doom-clean-byte-compiled-files))
(let (doom-emacs-changed-p
noninteractive)
;; But first we must be sure that Doom and your private config have been
;; fully loaded. Which usually aren't so in an noninteractive session.
(unless (and (doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file))
(doom-reload-autoloads))
(doom-initialize)
(doom-initialize-modules 'force))
;; If no targets were supplied, then we use your module list.
(unless modules
(let ((doom-modules-dirs (delete (expand-file-name "modules/" doom-private-dir)
doom-modules-dirs)))
(setq targets
(append (list doom-core-dir)
(delete doom-private-dir (doom-module-load-path))))))
;; Assemble el files we want to compile; taking into account that
;; MODULES may be a list of MODULE/SUBMODULE strings from the command
;; line.
(let ((target-files (doom-files-in targets :filter #'doom--byte-compile-ignore-file-p :sort nil)))
(when (or (not modules)
(member ":core" modules))
(push (expand-file-name "init.el" doom-emacs-dir)
target-files))
(unless target-files
(if targets
(message "Couldn't find any valid targets")
(message "No targets to %scompile" (if recompile-p "re" "")))
(cl-return-from 'byte-compile))
(require 'use-package)
(condition-case e
(let ((use-package-defaults use-package-defaults)
(use-package-expand-minimally t)
(load-path load-path)
kill-emacs-hook kill-buffer-query-functions)
;; Prevent packages from being loaded at compile time if they
;; don't meet their own predicates.
(push (list :no-require t
(lambda (_name args)
(or (when-let (pred (or (plist-get args :if)
(plist-get args :when)))
(not (eval pred t)))
(when-let (pred (plist-get args :unless))
(eval pred t)))))
use-package-defaults)
(dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal))
(if (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target)))
(and (file-exists-p elc-file)
(file-newer-than-file-p target elc-file))))
(let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target)
(not (doom--file-cookie-p target)))
'no-byte-compile
(byte-compile-file target)))
(short-name (if (file-in-directory-p target doom-emacs-dir)
(file-relative-name target doom-emacs-dir)
(abbreviate-file-name target))))
(cl-incf
(cond ((eq result 'no-byte-compile)
(print! (dark (white "⚠ Ignored %s")) short-name)
total-noop)
((null result)
(print! (red "✕ Failed to compile %s") short-name)
total-fail)
(t
(print! (green "✓ Compiled %s") short-name)
(load target t t)
total-ok))))
(cl-incf total-noop)))
(print! (bold (color (if (= total-fail 0) 'green 'red)
"%s %d/%d file(s) (%d ignored)"))
(if recompile-p "Recompiled" "Compiled")
total-ok (- (length target-files) total-noop)
total-noop)
(or (= total-fail 0)
(error "Failed to compile some files")))
((debug error)
(print! (red "\nThere were breaking errors.\n\n%s")
"Reverting changes...")
(signal 'doom-error (list 'byte-compile e))))))))
"Byte-compile anyway?"))))
(user-error "Aborting"))
;; But first we must be sure that Doom and your private config have been
;; fully loaded. Which usually aren't so in an noninteractive session.
(doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file)
;;
(unless target-dirs
(push (doom-glob doom-emacs-dir "init.el") targets)
;; If no targets were supplied, then we use your module list.
(appendq! target-dirs
(list doom-core-dir)
;; Omit `doom-private-dir', which is always first
(cl-remove-if-not (lambda (path) (file-in-directory-p path doom-emacs-dir))
(cdr (doom-module-load-path)))))
;; Assemble el files we want to compile; taking into account that MODULES
;; may be a list of MODULE/SUBMODULE strings from the command line.
(appendq! targets
(doom-files-in target-dirs
:match "\\.el$"
:filter #'doom--byte-compile-ignore-file-p)))
(unless targets
(print!
(if targets
(warn "Couldn't find any valid targets")
(info "No targets to %scompile" (if recompile-p "re" ""))))
(cl-return nil))
(print!
(info (if recompile-p
"Recompiling stale elc files..."
"Byte-compiling your config (may take a while)...")))
(print-group!
(require 'use-package)
(condition-case e
(let ((total-ok 0)
(total-fail 0)
(total-noop 0)
(use-package-defaults use-package-defaults)
(use-package-expand-minimally t)
kill-emacs-hook kill-buffer-query-functions)
;; Prevent packages from being loaded at compile time if they
;; don't meet their own predicates.
(push (list :no-require t
(lambda (_name args)
(or (when-let (pred (or (plist-get args :if)
(plist-get args :when)))
(not (eval pred t)))
(when-let (pred (plist-get args :unless))
(eval pred t)))))
use-package-defaults)
(unless recompile-p
(doom-clean-byte-compiled-files))
(dolist (target (delete-dups targets))
(cl-incf
(if (not (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target)))
(and (file-exists-p elc-file)
(file-newer-than-file-p target elc-file)))))
total-noop
(pcase (if (doom-file-cookie-p target)
(byte-compile-file target)
'no-byte-compile)
(`no-byte-compile
(print! (info "Ignored %s") (relpath target))
total-noop)
(`nil
(print! (error "Failed to compile %s") (relpath target))
total-fail)
(_
(print! (success "Compiled %s") (relpath target))
(load target t t)
total-ok)))))
(print! (class (if (= total-fail 0) 'success 'error)
"%s %d/%d file(s) (%d ignored)")
(if recompile-p "Recompiled" "Compiled")
total-ok (- (length targets) total-noop)
total-noop)
t)
((debug error)
(print! (error "\nThere were breaking errors.\n\n%s")
"Reverting changes...")
(signal 'doom-error (list 'byte-compile e)))))))
(defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'"
(cl-loop with default-directory = doom-emacs-dir
for path
in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 0 :sort nil)
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil)
(doom-files-in doom-core-dir :match "\\.elc$" :sort nil)
(doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil))
for truepath = (file-truename path)
if (file-exists-p path)
do (delete-file path)
and do
(print! (green "✓ Deleted %s")
(if (file-in-directory-p truepath default-directory)
(file-relative-name truepath)
(abbreviate-file-name truepath)))
finally do (print! (bold (green "Everything is clean")))))
(print! (start "Cleaning .elc files"))
(print-group!
(cl-loop with default-directory = doom-emacs-dir
with success = nil
for path
in (append (doom-glob doom-emacs-dir "*.elc")
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil)
(doom-files-in doom-core-dir :match "\\.elc$" :sort nil)
(doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil))
if (file-exists-p path)
do (delete-file path)
and do (print! (success "Deleted %s") (relpath path))
and do (setq success t)
finally do
(print! (if success
(success "All elc files deleted")
(info "No elc files to clean"))))))

View file

@ -1,7 +1,26 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*-
(dispatcher! info (doom/info)
"Output system info in markdown for bug reports.")
(load! "autoload/debug" doom-core-dir)
(dispatcher! (version v) (doom/version)
"Reports the version of Doom and Emacs.")
;;
;;; Commands
(def-command! info (&optional format)
"Output system info in markdown for bug reports."
(pcase format
("json"
(require 'json)
(with-temp-buffer
(insert (json-encode (doom-info)))
(json-pretty-print-buffer)
(print! (buffer-string))))
((or "md" "markdown")
(doom/info))
(_ (doom/info 'raw)))
nil)
(def-command! (version v) ()
"Reports the version of Doom and Emacs."
(doom/version)
nil)

View file

@ -1,25 +1,6 @@
;;; core/cli/env.el -*- lexical-binding: t; -*-
(dispatcher! env
(let ((env-file (abbreviate-file-name doom-env-file)))
(pcase (car args)
((or "refresh" "re")
(doom-reload-env-file 'force))
((or "enable" "auto")
(setenv "DOOMENV" "1")
(print! (green "Enabling auto-reload of %S") env-file)
(doom-reload-env-file 'force)
(print! (green "Done! `doom refresh' will now refresh your envvar file.")))
("clear"
(setenv "DOOMENV" nil)
(unless (file-exists-p env-file)
(user-error "%S does not exist to be cleared" env-file))
(delete-file env-file)
(print! (green "Disabled envvar file by deleting %S") env-file))
(_
(print! "%s\n\n%s"
(bold (red "No valid subcommand provided."))
"See `doom help env` to see available commands."))))
(def-command! env (&optional command)
"Manages your envvars file.
env [SUBCOMMAND]
@ -38,7 +19,18 @@ on Linux).
To generate a file, run `doom env refresh`. If you'd like this file to be
auto-reloaded when running `doom refresh`, run `doom env enable` instead (only
needs to be run once).")
needs to be run once)."
(let ((default-directory doom-emacs-dir))
(pcase command
("clear"
(unless (file-exists-p doom-env-file)
(user-error! "%S does not exist to be cleared"
(relpath doom-env-file)))
(delete-file doom-env-file)
(print! (success "Successfully deleted %S")
(relpath doom-env-file)))
(_
(doom-reload-env-file 'force)))))
;;
@ -86,12 +78,12 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
`doom-env-ignored-vars' are removed."
(when (or force-p (not (file-exists-p doom-env-file)))
(with-temp-file doom-env-file
(message "%s envvars file at %S"
(print! (start "%s envvars file at %S")
(if (file-exists-p doom-env-file)
"Regenerating"
"Generating")
(abbreviate-file-name doom-env-file))
(let ((process-environment doom-site-process-environment))
(relpath doom-env-file doom-emacs-dir))
(let ((process-environment doom--initial-process-environment))
(insert
(concat
"# -*- mode: dotenv -*-\n"
@ -111,25 +103,31 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"# To auto-regenerate this file when `doom reload` is run, use `doom env auto' or\n"
"# set DOOMENV=1 in your shell environment/config.\n"
"# ---------------------------------------------------------------------------\n\n"))
(let ((shell-command-switch doom-env-switches))
(message "Scraping env from '%s %s %s'"
shell-file-name
shell-command-switch
doom-env-executable)
(let ((shell-command-switch doom-env-switches)
(error-buffer (get-buffer-create "*env errors*")))
(print! (info "Scraping shell environment with '%s %s %s'")
(filename shell-file-name)
shell-command-switch
(filename doom-env-executable))
(save-excursion
(insert (shell-command-to-string doom-env-executable)))
;; Remove undesireable variables
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
(save-excursion
(let* ((valend (or (save-match-data
(when (re-search-forward "^\\([^= ]+\\)=" nil t)
(line-beginning-position)))
(point-max)))
(var (match-string 1))
(value (buffer-substring-no-properties (point) (1- valend))))
(when (cl-loop for regexp in doom-env-ignored-vars
if (string-match-p regexp var)
return t)
(message "Ignoring %s" var)
(delete-region (match-beginning 0) (1- valend))))))
(print! (green "Envvar successfully generated")))))))
(shell-command doom-env-executable (current-buffer) error-buffer))
(print-group!
(let ((errors (with-current-buffer error-buffer (buffer-string))))
(unless (string-empty-p errors)
(print! (info "Error output:\n\n%s") (indent 4 errors))))
;; Remove undesireable variables
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
(save-excursion
(let* ((valend (or (save-match-data
(when (re-search-forward "^\\([^= ]+\\)=" nil t)
(line-beginning-position)))
(point-max)))
(var (match-string 1)))
(when (cl-loop for regexp in doom-env-ignored-vars
if (string-match-p regexp var)
return t)
(print! (info "Ignoring %s") var)
(delete-region (match-beginning 0) (1- valend)))))))
(print! (success "Successfully generated %S")
(relpath doom-env-file doom-emacs-dir))
t)))))

View file

@ -1,55 +1,47 @@
;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el
;;
;;; Helpers
(defmacro doom--condition-case! (&rest body)
`(condition-case-unless-debug e
(progn ,@body)
('user-error
(print! (bold (red " NOTICE: %s")) e))
('file-error
(print! " %s\n %s"
(bold (red "FILE ERROR: %s" (error-message-string e)))
"Trying again...")
(quiet! (doom-refresh-packages-maybe t))
,@body)
('error
(print! (bold (red " %s %s\n %s"))
"FATAL ERROR: " e
"Run again with the -d flag for details"))))
(defsubst doom--ensure-autoloads-while (fn)
(doom-reload-doom-autoloads)
(when (funcall fn doom-auto-accept)
(doom-reload-package-autoloads)))
(defmacro doom--ensure-autoloads-while (&rest body)
`(progn
(doom-reload-core-autoloads)
(when (progn ,@body)
(doom-reload-package-autoloads 'force-p))
t))
;;
;;; Dispatchers
(dispatcher! (install i)
(doom--ensure-autoloads-while #'doom-packages-install)
"Installs wanted packages that aren't installed.
Package management in Doom is declarative. A `package!' declaration in an
enabled module or your private packages.el marks a package as 'wanted'.")
(dispatcher! (update u)
(doom--ensure-autoloads-while #'doom-packages-update)
(def-command! (update u) ()
"Updates packages.
This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property.")
or :ignore property."
(doom--ensure-autoloads-while
(straight-check-all)
(when (doom-packages-update doom-auto-accept)
(doom-packages-rebuild doom-auto-accept)
t)))
(dispatcher! (autoremove r)
(doom--ensure-autoloads-while #'doom-packages-autoremove)
"Removes packages that are no longer needed.
(def-command! (rebuild b) ()
"Rebuilds all installed packages.
This includes packages installed with 'M-x package-install' without an
accompanying `package!' declaration in an enabled module's packages.el file or
your private one.")
This ensures that all needed files are symlinked from their package repo and
their elisp files are byte-compiled."
(doom--ensure-autoloads-while
(doom-packages-rebuild doom-auto-accept (member "all" args))))
(def-command! (purge p) ()
"Deletes any unused packages and package repos.
You should run this once in a while, as repos tend to build up over time."
(doom--ensure-autoloads-while
(straight-check-all)
(doom-packages-purge doom-auto-accept)))
;; (def-command! rollback () ; TODO rollback
;; "<Not implemented yet>"
;; (user-error "Not implemented yet, sorry!"))
;;
@ -63,153 +55,212 @@ declaration) or dependency thereof that hasn't already been.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be installed."
(print! "Looking for packages to install...")
(let ((packages (doom-get-missing-packages)))
(cond ((not packages)
(print! (green "No packages to install!"))
nil)
(print! "> Installing & building packages...")
(print-group!
(let ((n 0))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight--with-plist (gethash package straight--recipe-cache)
(local-repo)
(let ((existed-p (file-directory-p (straight--repos-dir package))))
(condition-case-unless-debug e
(and (straight-use-package (intern package) nil nil " ")
(not existed-p)
(file-directory-p (straight--repos-dir package))
(cl-incf n))
(error
(signal 'doom-package-error
(list e (straight--process-get-output))))))))
(if (= n 0)
(ignore (print! (success "No packages need to be installed")))
(print! (success "Installed & built %d packages") n)
t))))
((not (or auto-accept-p
(y-or-n-p
(format "%s packages will be installed:\n\n%s\n\nProceed?"
(length packages)
(mapconcat
(lambda (pkg)
(format "+ %s (%s)"
(car pkg)
(cond ((doom-package-different-recipe-p (car pkg))
"new recipe")
((doom-package-different-backend-p (car pkg))
(format "%s -> %s"
(doom-package-backend (car pkg) 'noerror)
(doom-package-recipe-backend (car pkg) 'noerror)))
((plist-get (cdr pkg) :recipe)
"quelpa")
("elpa"))))
(cl-sort (cl-copy-list packages) #'string-lessp
:key #'car)
"\n")))))
(user-error "Aborted!"))
((let (success)
(doom-refresh-packages-maybe doom-debug-mode)
(dolist (pkg packages)
(print! "Installing %s" (car pkg))
(doom--condition-case!
(let ((result
(or (and (doom-package-installed-p (car pkg))
(not (doom-package-different-backend-p (car pkg)))
(not (doom-package-different-recipe-p (car pkg)))
'already-installed)
(and (doom-install-package (car pkg) (cdr pkg))
(setq success t)
'success)
'failure))
(pin-label
(and (plist-member (cdr pkg) :pin)
(format " [pinned: %s]" (plist-get (cdr pkg) :pin)))))
(print! "%s%s"
(pcase result
(`already-installed (dark (white "⚠ ALREADY INSTALLED")))
(`success (green "✓ DONE"))
(`failure (red "✕ FAILED")))
(or pin-label "")))))
(print! (bold (green "Finished!")))
(when success
(set-file-times doom-packages-dir)
(doom-delete-autoloads-file doom-package-autoload-file))
success)))))
(defun doom-packages-rebuild (&optional auto-accept-p all)
"(Re)build all packages."
(print! (start "(Re)building %spackages...") (if all "all " ""))
(print-group!
(let ((n 0))
(if all
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight-use-package
(intern package) nil (lambda (_) (cl-incf n) nil) " ")))
(let ((straight-check-for-modifications '(find-when-checking)))
(straight-check-all)
(dolist (recipe (hash-table-values straight--recipe-cache))
(straight--with-plist recipe (package local-repo no-build)
(unless (or no-build (null local-repo))
;; REVIEW We do these modification checks manually because
;; Straight's checks seem to miss stale elc files. Need
;; more tests to confirm this.
(when (or (gethash package straight--cached-package-modifications)
(file-newer-than-file-p (straight--repos-dir local-repo)
(straight--build-dir package))
(cl-loop for file
in (doom-files-in (straight--build-dir package)
:match "\\.el$"
:full t)
for elc-file = (byte-compile-dest-file file)
if (and (file-exists-p elc-file)
(file-newer-than-file-p file elc-file))
return t))
(print! (info "Rebuilding %s") package)
;; REVIEW `straight-rebuild-package' alone wasn't enough. Why?
(delete-directory (straight--build-dir package) 'recursive)
(straight-rebuild-package package)
(cl-incf n)))))))
(if (= n 0)
(ignore (print! (success "No packages need rebuilding")))
(print! (success "Rebuilt %d package(s)" n))
t))))
(defun doom-packages-update (&optional auto-accept-p)
"Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be updated."
(print! "Looking for outdated packages...")
(let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp
:key #'car)))
(cond ((not packages)
(print! (green "Everything is up-to-date"))
nil)
(print! (start "Scanning for outdated packages (this may take a while)..."))
(print-group!
;; REVIEW Does this fail gracefully enough? Is it error tolerant?
;; TODO Add version-lock checks; don't want to spend all this effort on
;; packages that shouldn't be updated
(condition-case e
(let (futures)
(dolist (group (seq-partition (hash-table-values straight--repo-cache) 8))
(push (async-start
`(lambda ()
(setq load-path ',load-path
doom-modules ',doom-modules)
(load ,(concat doom-core-dir "core.el"))
(let (packages)
(when (require 'straight nil t)
(dolist (recipe ',group)
(straight--with-plist recipe (package local-repo)
(when (and local-repo (straight--repository-is-available-p recipe))
(straight-fetch-package package)
;; REVIEW Isn't there a better way to get this information? Maybe with `vc'?
(let* ((default-directory (straight--repos-dir local-repo))
(n (string-to-number
(shell-command-to-string "git rev-list --right-only --count HEAD..@'{u}'")))
(pretime
(string-to-number
(shell-command-to-string "git log -1 --format=%at HEAD")))
(time
(string-to-number
(shell-command-to-string "git log -1 --format=%at FETCH_HEAD"))))
(when (> n 0)
(push (list n pretime time recipe)
packages)))))))
(nreverse packages))))
futures))
(let ((total (length futures))
(futures (nreverse futures))
(specs '(t)))
(while futures
(while (not (async-ready (car futures)))
(sleep-for 2)
(print! "."))
(nconc specs (async-get (pop futures))))
(terpri)
(if-let (specs (delq nil (cdr specs)))
(if (or auto-accept-p
(y-or-n-p
(format! "%s\n\nThere %s %d package%s available to update. Update them?"
(mapconcat
(lambda (spec)
(cl-destructuring-bind (n pretime time recipe) spec
(straight--with-plist recipe (package)
(format! "+ %-33s %s commit(s) behind %s -> %s"
(yellow package) (yellow n)
(format-time-string "%Y%m%d" pretime)
(format-time-string "%Y%m%d" time)))))
specs
"\n")
(if (cdr specs) "are" "is")
(length specs)
(if (cdr specs) "s" ""))))
(terpri)
(dolist (spec specs t)
(cl-destructuring-bind (n pretime time recipe) spec
(straight--with-plist recipe (local-repo package)
(let ((default-directory (straight--repos-dir local-repo)))
(print! (start "Updating %S") package)
;; HACK `straight' doesn't assume it would ever be used
;; non-interactively, but here we are. If the repo is
;; dirty, the command will lock up, waiting for
;; interaction that will never come, so discard all local
;; changes. Doom doesn't want you modifying those anyway.
(and (straight--get-call "git" "reset" "--hard")
(straight--get-call "git" "clean" "-ffd"))
(straight-merge-package package)
;; HACK `straight-rebuild-package' doesn't pick up that
;; this package has changed, so we do it manually. Is
;; there a better way?
(run-hook-with-args 'straight-use-package-pre-build-functions package)
(straight--build-package recipe " "))
(with-current-buffer (straight--process-get-buffer)
(with-silent-modifications
(erase-buffer))))))
(print! (info "Aborted update"))
nil)
(print! (success "No packages to update"))
nil)))
(error
(message "Output:\n%s" (straight--process-get-output))
(signal (car e) (error-message-string e))))))
((not (or auto-accept-p
(y-or-n-p
(format "%s packages will be updated:\n\n%s\n\nProceed?"
(length packages)
(let ((max-len
(or (car (sort (mapcar (lambda (it) (length (symbol-name (car it)))) packages)
#'>))
10)))
(mapconcat
(lambda (pkg)
(format (format "+ %%-%ds (%%s) %%-%ds -> %%s"
(+ max-len 2) 14)
(symbol-name (car pkg))
(doom-package-backend (car pkg))
(package-version-join (cadr pkg))
(package-version-join (cl-caddr pkg))))
packages
"\n"))))))
(user-error "Aborted!"))
((let (success)
(dolist (pkg packages)
(print! "Updating %s" (car pkg))
(doom--condition-case!
(print!
(let ((result (doom-update-package (car pkg) t)))
(when result (setq success t))
(color (if result 'green 'red)
(if result "✓ DONE" "✕ FAILED"))))))
(print! (bold (green "Finished!")))
(when success
(set-file-times doom-packages-dir)
(doom-delete-autoloads-file doom-package-autoload-file))
success)))))
(defun doom--packages-to-purge ()
(let (builds repos)
(dolist (name (straight--directory-files (straight--repos-dir)))
(unless (straight--checkhash name straight--repo-cache)
(push name repos)))
(dolist (name (straight--directory-files (straight--build-dir)))
(unless (gethash name straight--profile-cache)
(push name builds)))
(straight-prune-build-cache)
(list builds repos)))
(defun doom-packages-autoremove (&optional auto-accept-p)
"Auto-removes orphaned packages.
(defun doom-packages-purge (&optional auto-accept-p)
"Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have
a `package!' declaration) or isn't depended on by another primary package.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed."
(print! "Looking for orphaned packages...")
(let ((packages (doom-get-orphaned-packages)))
(cond ((not packages)
(print! (green "No unused packages to remove"))
nil)
((not
(or auto-accept-p
(y-or-n-p
(format "%s packages will be deleted:\n\n%s\n\nProceed?"
(length packages)
(mapconcat
(lambda (sym)
(let ((old-backend (doom-package-backend sym 'noerror))
(new-backend (doom-package-recipe-backend sym 'noerror)))
(format "+ %s (%s)" sym
(cond ((null new-backend)
"removed")
((eq old-backend new-backend)
(symbol-name new-backend))
((format "%s -> %s" old-backend new-backend))))))
(sort (cl-copy-list packages) #'string-lessp)
"\n")))))
(user-error "Aborted!"))
((let (success)
(dolist (pkg packages)
(doom--condition-case!
(let ((result (doom-delete-package pkg t)))
(if result (setq success t))
(print! (color (if result 'green 'red) "%s %s")
(if result "✓ Removed" "✕ Failed to remove")
pkg))))
(print! (bold (green "Finished!")))
(when success
(set-file-times doom-packages-dir)
(doom-delete-autoloads-file doom-package-autoload-file))
success)))))
(print! (start "Searching for orphaned packages..."))
(cl-destructuring-bind (builds repos) (doom--packages-to-purge)
(unless (bound-and-true-p package--initialized)
(package-initialize))
(print-group!
(let ((packages (append builds (mapcar #'car package-alist) nil)))
(if (not packages)
(ignore (print! (success "No orphaned packages to purge")))
(or auto-accept-p
(y-or-n-p
(format! "\n%s\n\n%d packages are orphaned. Purge them (for the Emperor)?"
(mapconcat (lambda (pkgs)
(mapconcat (lambda (p) (format " + %-20.20s" p))
pkgs
""))
(seq-partition (cl-sort (copy-sequence packages) #'string-lessp)
3)
"\n")
(length packages)))
(user-error "Aborted"))
(let ((n 0))
(dolist (dir (append (mapcar #'straight--repos-dir repos)
(mapcar #'straight--build-dir builds)))
(print! (info "Deleting %S") (relpath dir (straight--dir)))
(delete-directory dir 'recursive)
(unless (file-directory-p dir)
(cl-incf n)))
(straight-prune-build-cache)
(when (file-directory-p package-user-dir)
(delete-directory package-user-dir t)
t)
(> n 0)))))))

View file

@ -1,9 +1,6 @@
;;; core/cli/patch-macos.el -*- lexical-binding: t; -*-
(dispatcher! (patch-macos)
(doom-patch-macos (or (member "--undo" args)
(member "-u" args))
(doom--find-emacsapp-path))
(def-command! patch-macos ()
"Patches Emacs.app to respect your shell environment.
WARNING: This command is deprecated. Use 'doom env' instead.
@ -30,7 +27,11 @@ It can be undone with the --undo or -u options.
Alternatively, you can install the exec-path-from-shell Emacs plugin, which will
scrape your shell environment remotely, at startup. However, this can be slow
depending on your shell configuration and isn't always reliable.")
depending on your shell configuration and isn't always reliable."
:hidden t
(doom-patch-macos (or (member "--undo" args)
(member "-u" args))
(doom--find-emacsapp-path)))
;;

View file

@ -1,7 +1,12 @@
;;; core/cli/quickstart.el -*- lexical-binding: t; -*-
(dispatcher! (quickstart qs) (apply #'doom-quickstart args)
"Guides you through setting up Doom for first time use.
(def-command! quickstart (&rest args) ; DEPRECATED
""
:hidden t
(apply #'doom-cli-install args))
(def-command! (install i) ()
"A wizard for installing Doom for the first time.
This command does the following:
@ -17,89 +22,80 @@ This command is idempotent and safe to reuse.
The location of DOOMDIR can be changed with the -p option, or by setting the
DOOMDIR environment variable. e.g.
doom -p ~/.config/doom quickstart
DOOMDIR=~/.config/doom doom quickstart
doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install
Quickstart understands the following switches:
install understands the following switches:
--no-config Don't create DOOMDIR or dummy files therein
--no-install Don't auto-install packages
--no-env Don't generate an envvars file (see `doom help env`)
--no-fonts Don't install (or prompt to install) all-the-icons fonts")
;;
;; Library
(defun doom-quickstart (&rest args)
"Quickly deploy a private module and setup Doom.
This deploys a barebones config to `doom-private-dir', installs all missing
packages, prompts to install all-the-icons fonts, generates an env file and
regenerates the autoloads file."
;; Create `doom-private-dir'
(let ((short-private-dir (abbreviate-file-name doom-private-dir)))
--no-fonts Don't install (or prompt to install) all-the-icons fonts"
(print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-dir "~")))
;; Create `doom-private-dir'
(if (member "--no-config" args)
(print! (yellow "Not copying private config template, as requested"))
(print! "Creating %s" short-private-dir)
(make-directory doom-private-dir t)
(print! (green "Done!"))
(print! (warn "Not copying private config template, as requested"))
(print! "> Creating %s" (relpath doom-private-dir))
(make-directory doom-private-dir 'parents)
(print! (success "Created %s") (relpath doom-private-dir))
;; Create init.el, config.el & packages.el
(dolist (file (list (cons "init.el"
(lambda ()
(insert-file-contents (expand-file-name "init.example.el" doom-emacs-dir))))
(cons "config.el"
(lambda ()
(insert (format ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
short-private-dir)
";; Place your private configuration here\n")))
(cons "packages.el"
(lambda ()
(insert (format ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n"
short-private-dir)
";;; Examples:\n"
";; (package! some-package)\n"
";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n"
";; (package! builtin-package :disable t)\n")))))
(cl-destructuring-bind (path . fn) file
(if (file-exists-p! path doom-private-dir)
(print! "%s already exists, skipping" path)
(print! "Creating %s%s" short-private-dir path)
(with-temp-file (expand-file-name path doom-private-dir)
(funcall fn))
(print! (green "Done!")))))))
(mapc (lambda (file)
(cl-destructuring-bind (filename . fn) file
(if (file-exists-p! filename doom-private-dir)
(print! (warn "%s already exists, skipping") filename)
(print! (info "Creating %s%s") (relpath doom-private-dir) filename)
(with-temp-file (doom-path doom-private-dir filename)
(funcall fn))
(print! (success "Done!")))))
'(("init.el" .
(lambda ()
(insert-file-contents (doom-dir "init.example.el"))))
("config.el" .
(lambda ()
(insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
";; Place your private configuration here\n"
((relpath doom-private-dir)))))
("packages.el" .
(lambda ()
(insert! ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n"
";;; Examples:\n"
";; (package! some-package)\n"
";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n"
";; (package! builtin-package :disable t)\n"
((relpath doom-private-dir))))))))
;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install)
(doom-initialize-packages 'force-p)
;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install)
(doom-initialize-packages 'force-p)
;; Ask if Emacs.app should be patched
(if (member "--no-env" args)
(print! (yellow "Not generating envvars file, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(doom-reload-env-file 'force-p)))
;; Ask if Emacs.app should be patched
(if (member "--no-env" args)
(print! (warn "- Not generating envvars file, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(doom-reload-env-file 'force-p)))
;; Install Doom packages
(if (member "--no-install" args)
(print! (yellow "Not installing plugins, as requested"))
(print! "Installing plugins")
(doom-packages-install doom-auto-accept))
;; Install Doom packages
(if (member "--no-install" args)
(print! (warn "- Not installing plugins, as requested"))
(print! "Installing plugins")
(doom-packages-install doom-auto-accept))
(print! "Regenerating autoloads files")
(doom-reload-autoloads nil 'force-p)
(print! "Regenerating autoloads files")
(doom-reload-autoloads nil 'force-p)
(if (member "--no-fonts" args)
(print! (yellow "Not installing fonts, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns)
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
(if (member "--no-fonts" args)
(print! (warn "- Not installing fonts, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns)
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
(print! (bold (green "\nFinished! Doom is ready to go!\n")))
(with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO")
(print! (buffer-string))))
(print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO")
(print! (buffer-string)))))

View file

@ -1,67 +1,73 @@
;;; core/cli/test.el -*- lexical-binding: t; -*-
(dispatcher! test (doom-run-tests args)
(def-command! test ()
"Run Doom unit tests.")
;;
;; Library
;;; Library
(defun doom-run-tests (&optional modules)
"Run all loaded tests, specified by MODULES (a list of module cons cells) or
command line args following a double dash (each arg should be in the
'module/submodule' format).
(defun doom-run-tests ()
"Discover and load test files, then run all defined suites.
If neither is available, run all tests in all enabled modules."
;; Core libraries aren't fully loaded in a noninteractive session, so we
;; reload it with `noninteractive' set to nil to force them to.
(let* ((noninteractive t)
(doom-modules (doom-modules)))
(quiet! (doom-reload-autoloads))
(let ((target-paths
;; Convert targets into a list of string paths, pointing to the root
;; directory of modules
(cond ((stringp (car modules)) ; command line
(save-match-data
(cl-loop for arg in modules
if (string= arg ":core") collect doom-core-dir
else if (string-match-p "/" arg)
nconc (mapcar (apply-partially #'expand-file-name arg)
doom-modules-dirs)
else
nconc (cl-loop for dir in doom-modules-dirs
for path = (expand-file-name arg dir)
if (file-directory-p path)
nconc (doom-files-in path :type 'dirs :depth 1 :full t :sort nil))
finally do (setq argv nil))))
(modules ; cons-cells given to MODULES
(cl-loop for (module . submodule) in modules
if (doom-module-locate-path module submodule)
collect it))
((append (list doom-core-dir)
(doom-module-load-path))))))
;; Load all the unit test files...
(require 'buttercup)
(mapc (lambda (file) (load file :noerror (not doom-debug-mode)))
(doom-files-in (mapcar (apply-partially #'expand-file-name "test/")
target-paths)
:match "\\.el$" :full t))
;; ... then run them
(when doom-debug-mode
(setq buttercup-stack-frame-style 'pretty))
(let ((split-width-threshold 0)
(split-height-threshold 0)
(window-min-width 0)
(window-min-height 0))
(buttercup-run)))))
Takes directories as command line arguments, defaulting to the
current directory."
(let ((dirs nil)
(patterns nil)
(args command-line-args-left)
(doom-modules (doom-modules)))
(doom-initialize-autoloads doom-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file)
(while args
(cond
;; ((member (car args) '("--traceback"))
;; (when (not (cdr args))
;; (error "Option requires argument: %s" (car args)))
;; ;; Make sure it's a valid style by trying to format a dummy
;; ;; frame with it
;; (buttercup--format-stack-frame '(t myfun 1 2) (intern (cadr args)))
;; (setq buttercup-stack-frame-style (intern (cadr args)))
;; (setq args (cddr args)))
;; ((member (car args) '("-p" "--pattern"))
;; (when (not (cdr args))
;; (error "Option requires argument: %s" (car args)))
;; (push (cadr args) patterns)
;; (setq args (cddr args)))
;; ((member (car args) '("-c" "--no-color"))
;; (setq buttercup-color nil)
;; (setq args (cdr args)))
(t
(push (car args) dirs)
(setq args (cdr args)))))
(setq command-line-args-left nil)
(dolist (dir (or dirs '(".")))
(setq dir (if (string= dir "core")
doom-core-dir
(expand-file-name dir doom-modules-dir)))
(let ((test-dir (expand-file-name "test" dir)))
(when (or (string= dir doom-core-dir)
(cl-destructuring-bind (category . module)
(or (doom-module-from-path dir)
(cons nil nil))
(and category module (doom-module-p category module))))
(dolist (file (nreverse (doom-glob test-dir "test-*.el")))
(when (doom-file-cookie-p file)
(load file nil t))))))
(when patterns
(dolist (spec (buttercup--specs buttercup-suites))
(let ((spec-full-name (buttercup-spec-full-name spec)))
(unless (cl-dolist (p patterns)
(when (string-match p spec-full-name)
(cl-return t)))
(setf (buttercup-spec-function spec)
(lambda () (signal 'buttercup-pending "SKIPPED")))))))
(buttercup-run)))
;;
;; Test library
(defmacro insert! (&rest text)
(defmacro insert!! (&rest text)
"Insert TEXT in buffer, then move cursor to last {0} marker."
`(progn
(insert ,@text)

View file

@ -1,7 +1,7 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(dispatcher! (upgrade up) (doom-upgrade)
"Checks out the latest Doom on this branch.
(def-command! (upgrade up) ()
"Updates Doom and packages.
Doing so is equivalent to:
@ -9,7 +9,8 @@ Doing so is equivalent to:
git pull
bin/doom clean
bin/doom refresh
bin/doom update")
bin/doom update"
(doom-upgrade))
;;
@ -75,7 +76,7 @@ Doing so is equivalent to:
(buffer-string)))
(unless (equal (vc-git-working-revision doom-emacs-dir) rev)
(error "Failed to checkout latest commit.\n\n%s" (buffer-string))))
(doom-refresh 'force-p)
(doom-cli-refresh 'force-p)
(when (doom-packages-update doom-auto-accept)
(doom-reload-package-autoloads))
(message "Done! Please restart Emacs for changes to take effect")))