core-packages: restore package/module state; fix package counter; refactor

This commit is contained in:
Henrik Lissner 2017-02-02 04:37:59 -05:00
parent 8a9a67c346
commit 9910b2c88e

View file

@ -17,16 +17,17 @@
;; more stable. ;; more stable.
;; 4. No external dependencies (e.g. Cask) for plugin management. ;; 4. No external dependencies (e.g. Cask) for plugin management.
(defvar doom-packages '((quelpa-use-package)) (defvar doom-enabled-modules nil
"List of packages that have been explicitly installed (not dependencies) with
`package!'. Each element is a list whose car is the package symbol, and cdr is
its quelpa recipe, if available.")
(defvar doom-modules nil
"List of enabled modules; each element is a cons cell (MODULE . SUBMODULE), "List of enabled modules; each element is a cons cell (MODULE . SUBMODULE),
where MODULE is the module's property symbol, e.g. :lang, and SUBMODULE is the where MODULE is the module's property symbol, e.g. :lang, and SUBMODULE is the
submodule symbol, e.g. 'evil.") submodule symbol, e.g. 'evil.")
(defvar doom-packages nil
"A list of enabled packages.")
(defvar doom-protected-packages '(quelpa-use-package f s dash)
"A list of packages that shouldn't be deleted.")
(defvar doom--init nil (defvar doom--init nil
"Non-nil if doom's package system has been initialized or not. It may not be "Non-nil if doom's package system has been initialized or not. It may not be
if you have byte-compiled your configuration (as intended).") if you have byte-compiled your configuration (as intended).")
@ -35,8 +36,8 @@ if you have byte-compiled your configuration (as intended).")
"If non-nil, install missing packages. Otherwise, strip :ensure and :quelpa "If non-nil, install missing packages. Otherwise, strip :ensure and :quelpa
from `package!' calls.") from `package!' calls.")
(defvar doom--dont-load-p nil (defvar doom--prefer-el-p (or noninteractive doom--auto-install-p)
"If non-nil, don't actually load modules, only keep track of them.") "If non-nil, load uncompiled .el config files.")
(defvar doom--load-path (append (list doom-core-dir (defvar doom--load-path (append (list doom-core-dir
doom-modules-dir) doom-modules-dir)
@ -55,6 +56,7 @@ from `package!' calls.")
use-package-always-defer t use-package-always-defer t
use-package-always-ensure nil use-package-always-ensure nil
use-package-expand-minimally t
use-package-debug doom-debug-mode use-package-debug doom-debug-mode
use-package-verbose doom-debug-mode use-package-verbose doom-debug-mode
quelpa-checkout-melpa-p nil quelpa-checkout-melpa-p nil
@ -80,61 +82,94 @@ byte-compilation."
((not mode) ((not mode)
(error "No namespace specified on `doom!' for %s" p)) (error "No namespace specified on `doom!' for %s" p))
(t (t
(setq doom-modules (append doom-modules (list (cons mode p)))))))) (setq doom-enabled-modules (append doom-enabled-modules (list (cons mode p))))))))
(unless doom--dont-load-p `(unless noninteractive
`(let (file-name-handler-alist) (let (file-name-handler-alist)
,@(mapcar (lambda (pkg) (macroexpand `(load! ,(car pkg) ,(cdr pkg)))) ,@(mapcar (lambda (pkg) (macroexpand `(load! ,(car pkg) ,(cdr pkg))))
doom-modules) doom-enabled-modules)
(unless noninteractive (when (display-graphic-p)
(when (display-graphic-p) (require 'server)
(require 'server) (unless (server-running-p)
(unless (server-running-p) (server-start)))
(server-start)))
;; Prevent any auto-displayed text + benchmarking ;; Prevent any auto-displayed text + benchmarking
(advice-add 'display-startup-echo-area-message :override 'ignore) (advice-add 'display-startup-echo-area-message :override 'ignore)
(message "Loaded %s packages in %s" (message "Loaded %s packages in %s"
(length doom-packages) (- (length load-path) (length doom--load-path))
(emacs-init-time)))))) (emacs-init-time)))))
(defun doom-initialize (&optional force-p) (defun doom-initialize (&optional force-p)
"Initialize installed packages (using package.el). On first run it will "Initialize installed packages (using package.el) and ensure the core packages
prepare Emacs to auto-install all missing packages. If you byte compile are installed. If you byte compile core/core.el, calls to `package.el' are
core/core.el, calls to `package.el' are avoided to speed up startup." avoided to speed up startup."
(unless (or doom--init force-p) (unless (or doom--init force-p)
(setq load-path doom--load-path (setq load-path doom--load-path
package-activated-list nil) package-activated-list nil)
(package-initialize) (package-initialize)
(unless (package-installed-p 'quelpa-use-package) (unless (and (file-exists-p doom-packages-dir)
(require 'quelpa-use-package nil t))
(package-refresh-contents) (package-refresh-contents)
(package-install 'quelpa-use-package t) ;; Ensure core packages are installed
(setq doom--auto-install-p (not noninteractive))) (mapc 'package-install '(quelpa-use-package dash f s)))
(unless (featurep 'quelpa-use-package) (unless (require 'quelpa-use-package nil t)
(require 'quelpa-use-package) (delete-directory doom-packages-dir t)
(quelpa-use-package-activate-advice) (error "There was an error initializing DOOM. Try running it again"))
(setq use-package-expand-minimally t) (quelpa-use-package-activate-advice)
;; Move :ensure to after conditional properties ;; Move :ensure to after conditional properties
(delq :ensure use-package-keywords) (delq :ensure use-package-keywords)
(push :ensure (cdr (memq :unless use-package-keywords)))) (push :ensure (cdr (memq :unless use-package-keywords)))
(setq doom--init t))) (setq doom--init t)))
(defun doom-reload-modules ()
"Reload `doom-modules'."
(setq doom-modules nil)
(let ((doom--prefer-el-p t)
(noninteractive t))
(load (concat doom-emacs-dir "init.el") nil :nomessage :nosuffix)))
(defun doom-reload-packages (&optional install-p)
"Reload `doom-packages'."
(doom-initialize)
(doom-reload-modules)
(let ((before-packages-n (length package-alist))
(doom--auto-install-p (and install-p t))
(doom--prefer-el-p t)
(after-packages-n 0)
noninteractive)
(when doom--auto-install-p
(package-refresh-contents))
(load (f-expand "core.el" doom-core-dir) nil (not doom-debug-mode) :nosuffix)
(mapc (lambda (pkg)
(let ((path (f-expand "packages.el" (doom-module-path (car pkg) (cdr pkg)))))
(when (f-exists-p path)
(load path nil (not doom-debug-mode) :nosuffix))))
doom-enabled-modules)
(- (length package-alist) before-packages-n)))
;; ;;
;; Macros ;; Macros
;; ;;
(defvar doom--packages nil (defvar __DIR__ nil "The directory of the currently loaded file (set by `load!')")
"List of packages explicitly installed during this session.") (defvar __FILE__ nil "The full path of the currently loaded file (set by `load!')")
(defalias 'use-package! 'use-package (defmacro use-package! (name &rest plist)
"To adhere to the naming conventions of DOOM emacs.") "A `use-package' wrapper, to adhere to the naming conventions of DOOM emacs
and let-bind `package-name' for the containing forms. Note that packages are
deferred by default."
(macroexpand
`(let ((package-name ',name))
(use-package ,name ,@plist))))
(defmacro package! (name &rest plist) (defmacro package! (name &rest plist)
"Wraps around `use-package' (with `quelpa-use-package') and takes the same "Wraps around `use-package' (with `quelpa-use-package') and takes the same
arguments. Ensures the package named NAME is installed and available. If arguments. Ensures the package named NAME is installed and available. If
`doom--auto-install-p' is nil, then strip out :ensure and :quelpa properties, `doom--auto-install-p' is nil, then strip out :ensure and :quelpa properties,
which is the case if you've byte-compiled DOOM Emacs." which is the case if you've byte-compiled DOOM Emacs.
Note that packages are deferred by default."
(declare (indent defun)) (declare (indent defun))
(let ((use-package-always-ensure doom--auto-install-p) (let ((use-package-always-ensure doom--auto-install-p)
(recipe (plist-get plist :quelpa))) (recipe (plist-get plist :quelpa)))
@ -142,199 +177,61 @@ which is the case if you've byte-compiled DOOM Emacs."
;; MELPA lookups by quelpa. ;; MELPA lookups by quelpa.
(when (and recipe (= 0 (mod (length recipe) 2))) (when (and recipe (= 0 (mod (length recipe) 2)))
(push name recipe) (push name recipe)
(plist-put plist :quelpa (append (list name) recipe))) (setq plist (plist-put plist :quelpa recipe)))
(if doom--auto-install-p (unless doom--auto-install-p
(unless (package-installed-p name)
(add-to-list 'doom--packages name))
(setq plist (use-package-plist-delete plist :ensure)) (setq plist (use-package-plist-delete plist :ensure))
(setq plist (use-package-plist-delete plist :quelpa))) (setq plist (use-package-plist-delete plist :quelpa)))
`(progn ;; (package--save-selected-packages (cons name package-selected-packages))
(add-to-list 'doom-packages '(,name ,@recipe)) (pushnew name doom-packages)
,(macroexpand-all `(use-package ,name ,@plist))))) (macroexpand `(use-package ,name ,@plist))))
(defmacro load! (file-or-module-sym &optional submodule) (defmacro load! (file-or-module-sym &optional submodule file)
"Load a module from `doom-modules-dir'. Plays the same role as "Load a module from `doom-modules-dir'. Plays the same role as
`load-relative', but is specific to DOOM emacs modules and submodules. `load-relative', but is specific to DOOM emacs modules and submodules. If
`doom--prefer-el-p' is non-nil, prefer the un-compiled elisp file.
Examples: Examples:
(load! :lang emacs-lisp) (load! :lang emacs-lisp)
Loads modules/lang/emacs-lisp/(packages|config).el; package.el if Loads modules/lang/emacs-lisp/FILE.el (defaults to config.el).
`doom--auto-install-p' is non-nil, config.el otherwise.
(load! +local-module) (load! +local-module)
NOTE: Requires that the calling module be loaded with `load!'. Loads +local-module.el relative to `__DIR__' or `doom-core-dir'."
If called from ./config.el, loads ./+local-module.el"
(let (path file) (let (path file)
(cond ((null submodule) (cond ((null submodule)
(setq path (f-dirname load-file-name) (setq path (or __DIR__ doom-core-dir)
file (list (if (symbolp file-or-module-sym) file (concat (if (symbolp file-or-module-sym)
(symbol-name file-or-module-sym) (symbol-name file-or-module-sym)
file-or-module-sym)))) file-or-module-sym)
".el")))
(t (t
(setq path (f-slash (doom-module-path file-or-module-sym submodule)) (setq path (doom-module-path file-or-module-sym submodule)
file (if doom--auto-install-p "packages.el" "config.el")))) file (or file "config.el"))))
(setq path (f-slash path) (setq path (f-slash path)
file (concat path file)) file (concat path file))
(when (f-exists-p file) `(let ((__FILE__ ,file)
`(let ((__FILE__ ,file) (__DIR__ ,path))
(__DIR__ ,path)) (load (if doom--prefer-el-p ,file ,(f-no-ext file))
(load ,(if noninteractive file (f-no-ext file)) nil (not doom-debug-mode) doom--prefer-el-p))))
nil ,(not doom-debug-mode) noninteractive)))))
(defvar __DIR__ nil "The directory of the currently loaded file (with `load!')")
(defvar __FILE__ nil "The full path of the currently loaded file (with `load!')")
(defun doom-module-path (module submodule &optional file) (defun doom-module-path (module submodule &optional file)
(f-expand (concat (substring (symbol-name module) 1) "/" (symbol-name submodule) "/" file) "Get the full path to a module: e.g. :lang emacs-lisp maps to
~/.emacs.d/modules/lang/emacs-lisp/. Will append FILE if non-nil."
(when (symbolp module)
(setq module (symbol-name module)))
(when (string-prefix-p ":" module)
(setq module (substring module 1)))
(when (symbolp submodule)
(setq submodule (symbol-name submodule)))
(f-expand (concat module "/" submodule "/" file)
doom-modules-dir)) doom-modules-dir))
;; ;;
;; Commands ;; Defuns
;; ;;
(defun doom-package-outdated-p (package)
"Determine whether PACKAGE (a symbol) is outdated or not. If outdated, returns
a cons cell, whose car is the current version string of PACKAGE (a symbol), and
whose cdr is the latest version of the package. Be sure to run
`package-refresh-contents' beforehand, or the return value could be out of
date."
(let ((pkg (assq package doom-packages)))
(when (and pkg (package-installed-p package))
(let* ((pkg-recipe (cdr pkg))
(cur-desc (cadr (or (assq package package-alist)
(assq package package--builtins))))
(cur-version (package-desc-version cur-desc))
(inhibit-message t)
new-version)
(setq new-version
(if pkg-recipe
(let ((ver (quelpa-checkout
pkg-recipe
(f-expand (symbol-name package) quelpa-build-dir))))
(or (and ver (version-to-list ver)) cur-version))
(package-desc-version (cadr (assq package package-archive-contents)))))
(unless (version-list-<= new-version cur-version)
(cons cur-version new-version))))))
(defun doom/packages-reload ()
"Reload `load-path' by scanning all packages. Run this if you ran make update
or make clean outside of Emacs."
(interactive)
(doom-initialize t)
(when (called-interactively-p 'interactive)
(message "Reloaded %s packages" (length package-alist))))
(defun doom/packages-install ()
"Install missing packages."
(interactive)
(declare (interactive-only t))
(doom-initialize t)
(let ((doom--auto-install-p t))
(load (concat doom-emacs-dir "init.el") nil nil t)))
(defun doom/packages-update ()
"Update outdated packages. This includes quelpa-installed packages and ELPA
packages. This will delete old versions of packages as well."
(interactive)
(message "Refreshing packages...")
(package-initialize)
(package-refresh-contents)
(if (not package-alist)
(message "No packages are installed")
(require 'quelpa)
(when (quelpa-setup-p)
(setq quelpa-cache (--filter (package-installed-p (car it)) quelpa-cache)))
(let* ((err 0)
(quelpa-packages (-map 'car quelpa-cache))
(elpa-packages (-difference (package--find-non-dependencies) quelpa-packages))
outdated-packages)
(dolist (pkg (append quelpa-packages elpa-packages))
(-when-let (ver (doom-package-outdated-p pkg))
(push (list pkg ver) outdated-packages)))
(cond ((not outdated-packages)
(message "Everything is up-to-date"))
((not (y-or-n-p
(format "%s packages will be updated:\n%s\n\nProceed?"
(length outdated-packages)
(mapconcat (lambda (pkg) (format "%s: %s -> %s"
(car pkg)
(car (cdr pkg))
(cdr (cdr pkg))))
(--sort (string-lessp (symbol-name (car it))
(symbol-name (car other)))
outdated-packages) ", "))))
(message "Aborted"))
(t
(dolist (pkg outdated-packages)
(condition-case ex
(cond ((assq pkg quelpa-outdated-packages)
(let ((inhibit-message t))
(quelpa package)))
((memq pkg elpa-outdated-packages)
(let ((desc (cadr (assq pkg package-alist)))
(archive (cadr (assoc pkg package-archive-contents))))
(package-install-from-archive archive)
(delete-directory (package-desc-dir desc) t)))
(t (error "Not a valid package")))
('error
(setq err (1+ err))
(message "ERROR (%s): %s" pkg ex))))))
(if (> err 0)
(message "Done, but with %s errors" err)
(message "Done")))))
(defun doom/packages-clean ()
"Delete packages that are no longer used or depended on."
(interactive)
(package-initialize)
(let* ((package-selected-packages (-intersection (package--find-non-dependencies)
(mapcar 'car doom-packages)))
(packages-to-delete (package--removable-packages))
quelpa-modified-p)
(cond ((not package-selected-packages)
(message "No packages installed!"))
((not packages-to-delete)
(message "No unused packages to remove."))
((not (y-or-n-p
(format "%s packages will be deleted:\n%s\n\nProceed?"
(length packages-to-delete)
(mapconcat 'symbol-name (reverse packages-to-delete) ", "))))
(message "Aborted."))
(t
(require 'quelpa)
(quelpa-setup-p)
(dolist (p packages-to-delete)
(package-delete (cadr (assq p package-alist)) t)
(when (and quelpa-cache (assq p quelpa-cache))
(setq quelpa-cache (assq-delete-all p quelpa-cache)
quelpa-modified-p t)))
(when quelpa-modified-p
(quelpa-save-cache))))))
(defun doom/byte-compile ()
"Byte (re)compile the important files in your emacs configuration (i.e.
init.el, core/*.el and modules/*/*/config.el) DOOM Emacs was designed to benefit
a lot from this."
(interactive)
(doom-initialize)
(let ((targets (append (list (f-expand "init.el" doom-emacs-dir)
(f-expand "core.el" doom-core-dir))
(reverse (f-glob "core-*.el" doom-core-dir))
(-filter 'f-exists-p
(--map (doom-module-path (car it) (cdr it) "config.el")
doom-modules))))
use-package-always-ensure
file-name-handler-alist)
(mapc 'byte-compile-file targets)
(when noninteractive
(when targets (message "\n"))
(message "Compiled %s files:\n%s" (length targets)
(mapconcat (lambda (file) (concat "+ " (f-relative file doom-emacs-dir)))
targets "\n")))))
(defun doom/refresh-autoloads () (defun doom/refresh-autoloads ()
"Refreshes the autoloads.el file, which tells Emacs where to find all the "Refreshes the autoloads.el file, which tells Emacs where to find all the
autoloaded functions in the modules you use or among the core libraries. autoloaded functions in the modules you use or among the core libraries.
@ -342,37 +239,59 @@ autoloaded functions in the modules you use or among the core libraries.
Rerun this whenever you modify your init.el (or use `make autoloads` from the Rerun this whenever you modify your init.el (or use `make autoloads` from the
command line)." command line)."
(interactive) (interactive)
;; Reload modules (don't load anything) (unless doom--init
(setq doom-modules nil) (doom-reload-modules))
(let ((doom--dont-load-p t)
(noninteractive t))
(load (concat doom-emacs-dir "init.el") nil :nomessage t))
(let ((generated-autoload-file (concat doom-local-dir "autoloads.el")) (let ((generated-autoload-file (concat doom-local-dir "autoloads.el"))
(autoload-files (autoload-files
(append (append (-flatten (mapcar (lambda (dir)
(-flatten (mapcar (lambda (m) (let ((auto-dir (f-expand "autoload" dir))
(let* ((dir (doom-module-path (car m) (cdr m))) (auto-file (f-expand "autoload.el" dir)))
(auto-dir (f-expand "autoload" dir)) (cond ((f-directory-p auto-dir)
(auto-file (f-expand "autoload.el" dir))) (f-glob "*.el" auto-dir))
(cond ((f-directory-p auto-dir) ((f-exists-p auto-file)
(f-glob "*.el" auto-dir)) auto-file))))
((f-exists-p auto-file) (--map (doom-module-path (car it) (cdr it)) doom-enabled-modules)))
auto-file)))) (f-glob "autoload/*.el" doom-core-dir))))
doom-modules))
(f-glob "autoload/*.el" doom-core-dir))))
(when (f-exists-p generated-autoload-file) (when (f-exists-p generated-autoload-file)
(delete-file generated-autoload-file) (f-delete generated-autoload-file)
(when noninteractive (message "Deleted old autoloads.el"))) (message "Deleted old autoloads.el"))
(dolist (file autoload-files) (dolist (file autoload-files)
(update-file-autoloads file) (update-file-autoloads file)
(when noninteractive (message "Detected: %s" (f-relative file doom-emacs-dir)))
(message "Detected: %s" (f-relative file doom-emacs-dir))))
(with-current-buffer (get-file-buffer generated-autoload-file) (with-current-buffer (get-file-buffer generated-autoload-file)
(save-buffer)) (save-buffer))
(when noninteractive (message "Done!")) (load generated-autoload-file nil t t)
(with-demoted-errors "WARNING: %s" (message "Done!")))
(load generated-autoload-file nil t))))
(defun doom/byte-compile ()
"Byte (re)compile the important files in your emacs configuration (i.e.
init.el, core/*.el and modules/*/*/config.el) DOOM Emacs was designed to benefit
a lot from this."
(interactive)
(doom-initialize)
(doom-reload-modules)
(let ((targets (append (list (f-expand "init.el" doom-emacs-dir)
(f-expand "core.el" doom-core-dir))
(reverse (f-glob "core-*.el" doom-core-dir))
(-filter 'f-exists-p
(--map (doom-module-path (car it) (cdr it) "config.el")
doom-enabled-modules))))
(n 0)
results
use-package-always-ensure
file-name-handler-alist)
(mapc (lambda (file)
(push (cons (f-relative file doom-emacs-dir)
(when (byte-compile-file file)
(setq n (1+ n))
t))
results))
targets)
(when noninteractive
(when targets (message "\n"))
(message "Compiling %s files:\n%s" n
(mapconcat (lambda (file) (concat "+ " (if (cdr file) "SUCCESS" "FAIL") ": " (car file)))
(reverse results) "\n")))))
(provide 'core-packages) (provide 'core-packages)
;;; core-packages.el ends here ;;; core-packages.el ends here