Add functions to load and initialise modules

This commit is contained in:
Wojciech Kozlowski 2017-08-25 22:15:54 +01:00
parent 132d86205a
commit 320baafeb3

View File

@ -1,23 +1,25 @@
;;; init-packages.el --- Helpful automation functions for `package' ;;; init-packages.el --- Helpful automation functions for `package'
;;
;; Copyright (C) 2017 Wojciech Kozlowski ;; Copyright (C) 2017 Wojciech Kozlowski
;;
;; Author: Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net> ;; Author: Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
;; Created: 23 Aug 2017 ;; Created: 23 Aug 2017
;; Version: 0.0.1 ;; Version: 0.0.1
;; Keywords: tools ;; Keywords: tools
;; Package-Requires: ;; Package-Requires:
;;
;; This file is not part of GNU Emacs. ;; This file is not part of GNU Emacs.
;;
;;; Commentary: ;;; Commentary:
;;
;; This package serves to help further automate package management with ;; This package serves to help further automate package management with
;; `package'. The aim of this package is to expose a simple API which when ;; `package'. The aim of this package is to expose a simple API which when
;; provided with a list of desired packages will (i) install them and remove ;; provided with a list of desired packages will (i) install them and remove
;; any redundant packages, (ii) update them, (iii) rollback updates. ;; any redundant packages, (ii) update them, (iii) rollback updates.
;;
;; Currently only (i) is implemented. ;; Currently only (i) is implemented.
;;
;;; License: GPLv3
;;; Code: ;;; Code:
@ -30,23 +32,23 @@
;;; Customization options ;;; Customization options
(defcustom init-packages-print-logs t (defcustom init-packages/print-logs t
"This variable determines if `init-packages' prints any logs." "This variable determines if `init-packages' prints any logs."
:type 'boolean) :type 'boolean)
(defcustom init-packages-log "*Init-Packages-Log*" (defcustom init-packages/log "*Init-Packages-Log*"
"Buffer to which logs will be printed by `init-packages'. "Buffer to which logs will be printed by `init-packages'.
This buffer will be erased whenever \\[init-packages-init] is This buffer will be erased whenever
called." \\[init-packages/isntall-packages] is called."
:type 'string) :type 'string)
(defcustom init-packages-error-log "*Init-Packages-Error-Log*" (defcustom init-packages/error-log "*Init-Packages-Error-Log*"
"Buffer to which error logs will be printed by `init-packages'. "Buffer to which error logs will be printed by `init-packages'.
This buffer will be erased whenever \\[init-packages-init] is This buffer will be erased whenever
called." \\[init-packages/install-packages] is called."
:type 'string) :type 'string)
(defcustom init-packages-install-attempts 2 (defcustom init-packages/install-attempts 2
"How many times to attempt a package installation. "How many times to attempt a package installation.
This only matters if for some reason a package fails to install This only matters if for some reason a package fails to install
on the first attempt. Sometimes re-attempting the installation on the first attempt. Sometimes re-attempting the installation
@ -54,27 +56,31 @@ may fix the problem. Note that subsequent attempts are only made
after attempting to install all other packages first." after attempting to install all other packages first."
:type 'integer) :type 'integer)
(defcustom init-packages/modules-dir "~/.emacs.d/modules/"
"Directory in which module files are to be found."
:type 'string)
;;; Print functions ;;; Print functions
(defun init-packages-erase-logs () (defun init-packages/erase-logs ()
"Erase both log buffers." "Erase both log buffers."
(when init-packages-print-logs (when init-packages/print-logs
;; Erase `init-packages-log'. ;; Erase `init-packages/log'.
(save-excursion (save-excursion
(set-buffer (get-buffer-create init-packages-log)) (set-buffer (get-buffer-create init-packages/log))
(erase-buffer) (erase-buffer)
(goto-char (point-min))) (goto-char (point-min)))
;; Erase `init-packages-error-log'. ;; Erase `init-packages/error-log'.
(save-excursion (save-excursion
(set-buffer (get-buffer-create init-packages-error-log)) (set-buffer (get-buffer-create init-packages/error-log))
(erase-buffer) (erase-buffer)
(goto-char (point-min))))) (goto-char (point-min)))))
(defun init-packages-print (string buffer) (defun init-packages/print (string buffer)
"Print STRING to BUFFER." "Print STRING to BUFFER."
(when init-packages-print-logs (when init-packages/print-logs
(save-excursion (save-excursion
(set-buffer (get-buffer-create buffer)) (set-buffer (get-buffer-create buffer))
(goto-char (point-max)) (goto-char (point-max))
@ -82,74 +88,74 @@ after attempting to install all other packages first."
(newline)) (newline))
(insert string)))) (insert string))))
(defun init-packages-print-format (fmt pkg buffer) (defun init-packages/print-format (fmt pkg buffer)
"Print string of FMT about PKG to BUFFER" "Print string of FMT about PKG to BUFFER"
(or (stringp pkg) (or (stringp pkg)
(setq pkg (symbol-name pkg))) (setq pkg (symbol-name pkg)))
(init-packages-print (format fmt pkg) buffer)) (init-packages/print (format fmt pkg) buffer))
(defun init-packages-print-installing (pkg) (defun init-packages/print-installing (pkg)
"Print a log message about installing PKG." "Print a log message about installing PKG."
(init-packages-print-format "Installing: %s" pkg init-packages-log)) (init-packages/print-format "Installing: %s" pkg init-packages/log))
(defun init-packages-print-deleting (pkg) (defun init-packages/print-deleting (pkg)
"Print a log message about deleting PKG." "Print a log message about deleting PKG."
(init-packages-print-format "Deleting: %s" pkg init-packages-log)) (init-packages/print-format "Deleting: %s" pkg init-packages/log))
(defun init-packages-print-failed (logstrbase pkg) (defun init-packages/print-failed (logstrbase pkg)
"Print a log message about failed operation of PKG." "Print a log message about failed operation of PKG."
(let* ((logstr (concat logstrbase (let* ((logstr (concat logstrbase
(format " (see %s for details)" (format " (see %s for details)"
init-packages-error-log))) init-packages/error-log)))
(errstr (concat "*** " logstrbase " ***"))) (errstr (concat "*** " logstrbase " ***")))
(init-packages-print-format logstr pkg init-packages-log) (init-packages/print-format logstr pkg init-packages/log)
(init-packages-print-format errstr pkg init-packages-error-log))) (init-packages/print-format errstr pkg init-packages/error-log)))
(defun init-packages-print-failed-install (pkg) (defun init-packages/print-failed-install (pkg)
"Print a log message about failed installation of PKG." "Print a log message about failed installation of PKG."
(init-packages-print-failed "Failed to install: %s" pkg)) (init-packages/print-failed "Failed to install: %s" pkg))
(defun init-packages-print-failed-delete (pkg) (defun init-packages/print-failed-delete (pkg)
"Print a log message about failed deletion of PKG." "Print a log message about failed deletion of PKG."
(init-packages-print-failed "Failed to delete: %s" pkg)) (init-packages/print-failed "Failed to delete: %s" pkg))
(defun init-packages-print-log (string) (defun init-packages/print-log (string)
"Print STRING to `init-packages-log'." "Print STRING to `init-packages/log'."
(init-packages-print string init-packages-log)) (init-packages/print string init-packages/log))
(defun init-packages-print-error-log (string) (defun init-packages/print-error-log (string)
"Print STRING to `init-packages-error-log'." "Print STRING to `init-packages/error-log'."
(init-packages-print string init-packages-error-log)) (init-packages/print string init-packages/error-log))
;;; Helper functions ;;; Helper functions
(defun init-packages-install-pkgs (install-pkgs) (defun init-packages/install-pkgs (install-pkgs)
"Install all packages in INSTALL-PKGS. "Install all packages in INSTALL-PKGS.
Log errors to `init-packages-error-log'." Log errors to `init-packages/error-log'."
(dolist (p install-pkgs nil) (dolist (p install-pkgs nil)
(init-packages-print-installing p) (init-packages/print-installing p)
(condition-case err (condition-case err
(package-install p) (package-install p)
(error (progn (error (progn
(init-packages-print-failed-install p) (init-packages/print-failed-install p)
(init-packages-print-error-log (error-message-string err))))))) (init-packages/print-error-log (error-message-string err)))))))
(defun init-packages-delete-pkgs (delete-pkgs) (defun init-packages/delete-pkgs (delete-pkgs)
"Delete all packages in DELETE-PKGS. "Delete all packages in DELETE-PKGS.
This will attempt to delete all installed versions. Log errors This will attempt to delete all installed versions. Log errors
to `init-packages-error-log'. This assumes all DELETE-PKGS can to `init-packages/error-log'. This assumes all DELETE-PKGS can
be removed, including packages that are dependencies as it is be removed, including packages that are dependencies as it is
assumed they would not be dependencies once all packages in assumed they would not be dependencies once all packages in
DELETE-PKGS are removed." DELETE-PKGS are removed."
(dolist (p delete-pkgs nil) (dolist (p delete-pkgs nil)
(init-packages-print-deleting p) (init-packages/print-deleting p)
(condition-case err (condition-case err
(package-delete (cadr (assq p package-alist)) t) (package-delete (cadr (assq p package-alist)) t)
(error (progn (error (progn
(init-packages-print-failed-install p) (init-packages/print-failed-install p)
(init-packages-print-error-log (error-message-string err))))))) (init-packages/print-error-log (error-message-string err)))))))
(defun init-packages-removable-packages (pkgs) (defun init-packages/removable-packages (pkgs)
"Return a list of names of packages no longer needed. "Return a list of names of packages no longer needed.
These are packages which are neither contained in PKGS nor a These are packages which are neither contained in PKGS nor a
dependency of one that is." dependency of one that is."
@ -164,59 +170,103 @@ dependency of one that is."
;;; Operational functions: ;;; Operational functions:
;; These functions are expected to be called from the init file. ;; These functions are expected to be called from the init file.
(defun init-packages-init (desired-pkgs &optional no-set-selected) (defun init-packages/install-packages (desired-pkgs &optional no-set-selected)
"Install DESIRED-PKGS and remove redundant packages. "Install DESIRED-PKGS and remove redundant packages.
First, any missing packages will be installed followed by the First, any missing packages will be installed followed by the
deletion of all packages that are not dependencies of anything in deletion of all packages that are not dependencies of anything in
DESIRED-PKGS. Information logs will be printed to the DESIRED-PKGS. Information logs will be printed to the
`init-packages-log' buffer whilst error messages will be printed `init-packages/log' buffer whilst error messages will be printed
to the `init-packages-error-log' buffer. Finally the to the `init-packages/error-log' buffer. Finally the
`package-selected-packages' custom variable will be set to `package-selected-packages' custom variable will be set to
DESIRED-PKGS unless NO-SET-SELECTED is non-nil" DESIRED-PKGS unless NO-SET-SELECTED is non-nil"
;; Erase log buffers. ;; Erase log buffers.
(init-packages-erase-logs) (init-packages/erase-logs)
;; Check if `package' initialized. ;; Check if `package' initialized.
(when (not package--initialized) (when (not package--initialized)
(let ((err-str "`package' not initialized")) (let ((err-str "`package' not initialized"))
(init-packages-print-error-log err-str) (init-packages/print-error-log err-str)
(error err-str))) (error err-str)))
;; Install packages. If any packages fail to install, re-attempt up to ;; Install packages. If any packages fail to install, re-attempt up to
;; `init-packages-install-attempts' total attempts. ;; `init-packages/install-attempts' total attempts.
(let ((attempt 0)) (let ((attempt 0))
(while (and (< attempt init-packages-install-attempts) (while (and (< attempt init-packages/install-attempts)
(setq install-pkgs (setq install-pkgs
(remove-if #'package-installed-p desired-pkgs))) (remove-if #'package-installed-p desired-pkgs)))
(if (= attempt 0) (if (= attempt 0)
(progn (progn
(init-packages-print-log "*** Install packages ***") (init-packages/print-log "*** Install packages ***")
(init-packages-print-log "--- Refreshing package archives ---") (init-packages/print-log "--- Refreshing package archives ---")
(package-refresh-contents)) (package-refresh-contents))
(init-packages-print-log (init-packages/print-log
"--- Re-attempt installation of failed packages ---")) "--- Re-attempt installation of failed packages ---"))
(init-packages-install-pkgs install-pkgs) (init-packages/install-pkgs install-pkgs)
(setq attempt (1+ attempt)))) (setq attempt (1+ attempt))))
;; Print an error message if not all packages were installed. ;; Print an error message if not all packages were installed.
(let ((failed-pkgs (remove-if #'package-installed-p desired-pkgs))) (let ((failed-pkgs (remove-if #'package-installed-p desired-pkgs)))
(when failed-pkgs (when failed-pkgs
(init-packages-print-error-log (init-packages/print-error-log
"*** WARNING: NOT ALL PACKAGES WERE INSTALLED ***") "*** WARNING: NOT ALL PACKAGES WERE INSTALLED ***")
(init-packages-print-error-log "--- Packages not installed: ") (init-packages/print-error-log "--- Packages not installed: ")
(init-packages-print-error-log (init-packages/print-error-log
(mapconcat #'symbol-name failed-pkgs ", ")))) (mapconcat #'symbol-name failed-pkgs ", "))))
;; Remove packages. ;; Remove packages.
(let ((delete-pkgs (init-packages-removable-packages desired-pkgs))) (let ((delete-pkgs (init-packages/removable-packages desired-pkgs)))
(when delete-pkgs (when delete-pkgs
(init-packages-print-log "*** Delete packages ***") (init-packages/print-log "*** Delete packages ***")
(init-packages-delete-pkgs delete-pkgs))) (init-packages/delete-pkgs delete-pkgs)))
(unless no-set-selected (unless no-set-selected
(customize-save-variable 'package-selected-packages desired-pkgs))) (customize-save-variable 'package-selected-packages desired-pkgs)))
(defun init-packages/load-module (mod)
"Load all definitions for module MOD.
This function expects the module to be located in a file called
MOD.el in the `init-packages/modules-dir' directory."
(load (expand-file-name (format "%s/%s.el"
init-packages/modules-dir
(symbol-name mod)))))
(defun init-packages/load-module-list (modlist)
"Load all modules in MODLIST."
(dolist (mod modlist nil)
(init-packages/load-module mod)))
(defun init-packages/get-module-packages (mod)
"Get all packages required by module MOD to MODLIST.
A module's packages are expected to be found in a list called
`init-packages/MOD-packages'"
(eval (intern (format "init-packages/%s-packages" (symbol-name mod)))))
(defun init-packages/cons-package-list (modlist)
"Construct a list of all packages required by MODLIST."
(let (pkglist)
(dolist (mod modlist pkglist)
(setq pkglist (append (init-packages/get-module-packages mod)
pkglist)))))
(defun init-packages/init-module (mod)
"Call the initialisation function for module MOD.
It is expected that every module has a function called
`init-packages/init-MOD' which will be called by the expansion of
this macro."
(funcall (intern (format "init-packages/init-%s" (symbol-name mod)))))
(defun init-packages/init-module-list (modlist)
"Init all modules in MODLIST."
(dolist (mod modlist nil)
(init-packages/init-module mod)))
(defun init-packages/init (modlist)
"Initialise all modules in MODLIST."
(init-packages/load-module-list modlist)
(init-packages/install-packages (init-packages/cons-package-list modlist))
(init-packages/init-module-list modlist))
(provide 'init-packages) (provide 'init-packages)
;;; init-packages.el ends here ;;; init-packages.el ends here