From 089aed46faa602dd802badb0b71f140e0bae53d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Kryger?= Date: Wed, 13 Nov 2024 17:33:28 +0000 Subject: [PATCH] Use :exordium-force-elpa where appropriate and add doc and statistics --- init.el | 50 +++++------ modules/init-require.el | 180 +++++++++++++++++++++++++++++++++------- 2 files changed, 175 insertions(+), 55 deletions(-) diff --git a/init.el b/init.el index 92e86edb..4ef800ba 100644 --- a/init.el +++ b/init.el @@ -194,12 +194,12 @@ Warn if DIR is not a directory and IGNORE-IF-ABSENT is nil." (setq package-native-compile (native-comp-available-p))) (package-initialize) - + ;; Load the packages we need if they are not installed already (let ((package-pinned-packages (append '((use-package . "gnu") (diminish . "gnu") - (bind-key . "melpa-pinned")) + (bind-key . "gnu")) exordium-extra-pinned)) has-refreshed) (mapc (lambda (pkg) @@ -212,6 +212,27 @@ Warn if DIR is not a directory and IGNORE-IF-ABSENT is nil." (package-install pkg))) (append (mapcar #'car package-pinned-packages) exordium-extra-packages))) +;; This is only needed once, near the top of the file +(require 'use-package) + +(eval-and-compile + (load (file-name-concat (locate-user-emacs-file "modules") "init-require"))) +(exordium-require 'init-prefs) ; defines variables that prefs.el can override +(exordium-require 'init-lib) ; utility functions - load this first + +(use-package use-package + :exordium-force-elpa gnu + :custom + (use-package-always-ensure t) + (use-package-compute-statistics t)) +(use-package diminish + :exordium-force-elpa gnu) +(use-package bind-key + :exordium-force-elpa gnu) + +(dolist (pkg-pin exordium-extra-pinned) + (use-package-pin-package (car pkg-pin) (cdr pkg-pin))) + ;; - Some packages (i.e., magit, forge) require seq-2.24. ;; - Emacs-29.1 is delivered with seq-2.23. @@ -223,24 +244,10 @@ Warn if DIR is not a directory and IGNORE-IF-ABSENT is nil." (when (version< emacs-version "29.2") (let (debug-on-error) ;; this assumes `package-refresh-contents has been called' - (package-install (car (alist-get 'seq package-archive-contents))))) + (use-package seq + :exordium-force-elpa gnu))) -;; This is only needed once, near the top of the file -(eval-when-compile - ;; Following line is not needed if use-package.el is in ~/.emacs.d - (require 'use-package)) -(require 'diminish) ;; if you use :diminish -(require 'bind-key) ;; if you use any :bind variant - -;; Ensure use-package and diminish are from gnu -(use-package-pin-package 'use-package 'gnu) -(use-package-pin-package 'diminish 'gnu) - -(require 'use-package-ensure) -(setq use-package-always-ensure t) -(setq use-package-compute-statistics t) - ;;; Load Modules (require 'bytecomp) (defun exordium-recompile-modules () @@ -267,16 +274,9 @@ Also discard .elc without corresponding .el." (delete-file elc))))))) (exordium-recompile-modules) -(eval-and-compile - (load (file-name-concat (locate-user-emacs-file "modules") "init-require"))) - -(exordium-require 'init-lib) ; utility functions - load this first (exordium-require 'init-environment) ; environment variables -;;; Local preferences (fonts, frame size etc.) -(exordium-require 'init-prefs) ; defines variables that prefs.el can override - (dolist (tapped-file exordium-tapped-prefs-files) (message "Loadding tapped prefs file: %s" tapped-file) (load (file-name-sans-extension tapped-file))) diff --git a/modules/init-require.el b/modules/init-require.el index 2d5d1b59..0fb2180c 100644 --- a/modules/init-require.el +++ b/modules/init-require.el @@ -1,11 +1,110 @@ ;;; init-require.el --- Exordium require/loading library -*- lexical-binding: t -*- ;;; Commentary: -;; The `exoridum-require' is a substitute to the built-in require, to be used -;; in the Exordium. -;; @todo: how to use +;; +;; The `exoridum-require' has been designed to be used in Exordium as a +;; replacement to built-in `load', `require', and `use-package'. It is heavily +;; influenced by implementations of `require' and `use-package'. +;; +;; In order use the `exordium-require' you need to manually load it in the +;; module. For example: +;; +;; (eval-when-compile +;; (unless (featurep 'init-require) +;; (load (file-name-concat (locate-user-emacs-file "modules") "init-require")))) +;; +;; Macro arguments were inspired by `require' and `use-package', although they +;; were adjusted to support needs of Exordium. In particular, the LOCATION +;; argument is used as a relative directory inside Exordium. For example +;; "modules", "themes", or "taps/my-tap". The default LOCATION is "modules". +;; +;; The DECLARATION is a plist that can be used to generate `declare-function' +;; and `defvars' forms should the compiler not infer them from the loaded +;; module. For example, a code in a file: +;; +;; (exordium-require 'init-foo "foo-dir" +;; :functions (foo-simple (foo-args . (arg1 arg2))) +;; :defines (foo-var)) +;; +;; will: +;; - when the file either is evaluated or is compiled, it will `load' a +;; feature 'init-foo a file "foo-dir/init-foo" (following `load-suffixes' +;; and `load-prefer-newer', which see), +;; - when the file containing the `exordium-require' is compiled the following +;; forms will be generated: +;; +;; (declare-function foo-simle nil) +;; (declare-function foo-args nil (arg1 arg2)) +;; (defvar foo-var) +;; +;; Note, the forms will not be generated in files that contain the +;; `exordium-require' but are merely loaded by the file as part of the +;; compilation or evaluation. +;; +;; When developing Exordium, please follow these rules when "including" +;; functionality from other modules, packages, and libraries. +;; +;; For a given package X only one Exordium module should contain form +;; +;; (use-package X ...) +;; +;; to install, load, and configure the package X. This is dictated by +;; difficulty syncing different calls of `use-package' when these are spread +;; across different Exordium modules. One example is the +;; `:diminish'/`:delight' which may be clobbered by later `use-package' that +;; doesn't specify it. +;; +;; For a built-in package add `:ensure' nil to the `use-package' form. +;; +;; For a built-in package, when it needs to be shadowed (installation from ELPA +;; should be enforced) add `:exordium-force-elpa' to the `use-package' form. +;; +;; When the package X is required by other Exordium module, use +;; `exordium-require' to load it in when it is an ELPA package, it is a +;; shadowed package, or it is a built-in package that has been configured by +;; the or module . Use `require' when the package is built-in and there's no +;; configuration for it in Exordium, for example `cl-lib'. +;; +;; Usage of `exordium-require' for shadowed built-in packages is to ensure that +;; during compilation of a single Exordium module the ELPA version is used +;; consistently. +;; +;; Like the `use-packate', `exordium-require' supports statistics gathering +;; when `use-package-compute-statistics' is non-nil. Statistics are gathered +;; into internal `use-package' buffers, and can be reviewed with +;; `use-package-report' (which see). Note that Exordium modules call +;; `use-package' when they are evaluated. This causes their entries in the +;; report to have time for packages loaded by `use-package' loading/configuring +;; included. +;; +;;; Implementation details: +;; +;; When a FEATURE is loaded the `load' function is used (which see) with PATH +;; set to equivalent of: +;; +;; (expand-file-name (locate-user-emacs-file LOCATION) +;; FEATURE) +;; +;; Note that the filename extension is omitted to allow `load' to follow rules +;; for `load-suffixes' and `load-prefer-newer'. +;; +;; When macro is expanded for a compiled file errors are demoted while `load' +;; is executing. This is to suppress errors from the loaded file propagating +;; to the caller. Also the `package-archives' are set to nil (technically: a +;; value of the `exordium--require-package-archives' which is nil by default). +;; These measures are to allow for suppressing modules download for example +;; when running byte compilation as part of a lint process, for example like +;; `flycheck'. +;; +;; However, when a module is byte-compiled the +;; `exordium--require-package-archives' should be set to the desired value for +;; `package-archives', such that required packages can be installed from ELPAs. + ;;; Code: +(eval-when-compile + (require 'use-package nil t)) + (defvar exordium--require-package-archives nil "Package archives to be used when compiling.") @@ -31,25 +130,36 @@ for `exordium-require', which see." "If FEATURE is not already loaded, load it from Exordium LOCATION. Like `require', but concatenate LOCATION, that is relative in Exordium project, with printname of FEATURE as FILE passed to -`load', which see. +`load' (which see). Default LOCATION is \"modules\". To suppress compiler warnings use DECLARATIONS which is a plist -with two properties: :functions and :variables. The :functions +with two properties: `:functions' and `:defines'. The `:functions' is a list where each element either is a function FN or is a cons (FN ARGLIST) that are passed to `declare-function' (which -see). The :variables is a list where each element is a SYMBOL -that is passed to `defvar' (which see)." - (if (bound-and-true-p byte-compile-current-file) - ;; Like in `use-package-normalize-keywords', when byte-compiling, - ;; pre-load the package so all its symbols are in scope. With a few - ;; extensions: - ;; - set `package-archives' to nil to prevent `use-package' - ;; :ensure from downloading from ELPAs, - ;; - use `eval-and-compile' such that function names are autoloaded, - ;; - handle `declare-functions', should the above not work. - (let ((declare-forms - (append - (when-let* ((fns (plist-get declarations :functions))) +see). Note that `declare-function' forms has FILE argument set +to nil. The `:defines' is a list where each element is a SYMBOL +that is passed to `defvar' (which see). + +Please see Commentary in init-require.el for more details." + (append + `(progn + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :use-package ,feature nil)))) + (when (bound-and-true-p byte-compile-current-file) + ;; Like in `use-package-normalize-keywords' for byte-compiling, + ;; pre-load the package so all its symbols are in scope. With a few + ;; extensions: + ;; - set `package-archives' to nil to prevent `use-package' + ;; `:ensure' in loaded files from downloading from ELPAs when + ;; compilation is run as a lint, + ;; - this is overridable by `exordium--require-package-archives' when + ;; - compilation is actually producing results, + ;; - use `eval-and-compile' such that functions and variables are brought + ;; into scope, + ;; - handle `declare-functions' and `defvar' should the above not work. + (let ((declare-forms + (append + (when-let* ((fns (plist-get declarations :functions))) (cons '(require 'loadhist) (mapcar (lambda (fn) @@ -63,25 +173,31 @@ that is passed to `defvar' (which see)." "Wrong type argument: symbolp or (and consp (symbolp car)), %S" fn)))) fns))) - (mapcar (lambda (var) - (if (symbolp var) - `(defvar ,var) - (error "Wrong type argument: symbolp, %S" var))) - (plist-get declarations :variables))))) - `(eval-and-compile + (mapcar (lambda (var) + (if (symbolp var) + `(defvar ,var) + (error "Wrong type argument: symbolp, %S" var))) + (plist-get declarations :defines))))) + `((eval-and-compile + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :preface ,feature nil))) (let ((package-archives exordium--require-package-archives)) (with-demoted-errors ,(format "(exordium-require) Cannot load %s: %%S" feature) (unless (featurep ,feature) (exordium--require-load ,feature ,location t)))) - ,@declare-forms)) - ;; Like in `require' handle errors and ensure everything is loaded when - ;; in runtime. - `(if (and (symbolp ,feature) + ,@declare-forms + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :preface ,feature t))))))) + ;; Like in `require' handle errors and ensure everything is loaded while + ;; eval. + `((if (and (symbolp ,feature) (or (not ,location) (stringp ,location))) (if (featurep ,feature) ,feature + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :config ,feature nil))) (unwind-protect (if (< 3 (cl-count ,feature exordium--require-nesting-list)) (error @@ -97,10 +213,14 @@ that is passed to `defvar' (which see)." (error "Required feature `%s' was not provided" ,feature)))) (when (eq ,feature (car exordium--require-nesting-list)) - (pop exordium--require-nesting-list)))) + (pop exordium--require-nesting-list))) + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :config ,feature t)))) (if (symbolp ,feature) (error "Wrong type argument: symbolp, %S" ,feature) - (error "Wrong type argument: stringp, %S" ,location))))) + (error "Wrong type argument: stringp, %S" ,location))) + ,@(when (bound-and-true-p use-package-compute-statistics) + `((use-package-statistics-gather :use-package ,feature t)))))) (provide 'init-require)