Skip to content

Commit

Permalink
Use :exordium-force-elpa where appropriate and add doc and statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
pkryger committed Nov 15, 2024
1 parent e9e071c commit 089aed4
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 55 deletions.
50 changes: 25 additions & 25 deletions init.el
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -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 ()
Expand All @@ -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)))
Expand Down
180 changes: 150 additions & 30 deletions modules/init-require.el
Original file line number Diff line number Diff line change
@@ -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.")

Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)

Expand Down

0 comments on commit 089aed4

Please sign in to comment.