ENH clean up system dep code
This commit is contained in:
parent
efe3eac7c3
commit
9c0a153872
201
etc/conf.org
201
etc/conf.org
|
@ -7,9 +7,9 @@ This is my personal emacs config. It is quite massive. Please use the table of c
|
||||||
- [[#config-structure][config structure]]
|
- [[#config-structure][config structure]]
|
||||||
- [[#continuous-integration][continuous integration]]
|
- [[#continuous-integration][continuous integration]]
|
||||||
- [[#library][library]]
|
- [[#library][library]]
|
||||||
- [[#system-dependencies][system dependencies]]
|
|
||||||
- [[#external][external]]
|
- [[#external][external]]
|
||||||
- [[#internal][internal]]
|
- [[#internal][internal]]
|
||||||
|
- [[#system-dependencies][system dependencies]]
|
||||||
- [[#macros][macros]]
|
- [[#macros][macros]]
|
||||||
- [[#functions][functions]]
|
- [[#functions][functions]]
|
||||||
- [[#interactive][interactive]]
|
- [[#interactive][interactive]]
|
||||||
|
@ -102,60 +102,6 @@ In the root of this directory is a =.github= folder with some simple tests to en
|
||||||
The danger with only having emacs on my daily driver is that I could silently introduce a dependency on some system library, and this may or may not be present when I unpack this config on a different machine. For now, the CI pipeline simply tests whether or not this config will initialize and build correctly on a "bare" system, and also tests if I can pull a list of dependencies using my somewhat hacky [[#system-dependencies][API]] so they can be installed via the package manager.
|
The danger with only having emacs on my daily driver is that I could silently introduce a dependency on some system library, and this may or may not be present when I unpack this config on a different machine. For now, the CI pipeline simply tests whether or not this config will initialize and build correctly on a "bare" system, and also tests if I can pull a list of dependencies using my somewhat hacky [[#system-dependencies][API]] so they can be installed via the package manager.
|
||||||
* library
|
* library
|
||||||
This is code that is used generally throughout the emacs config
|
This is code that is used generally throughout the emacs config
|
||||||
** system dependencies
|
|
||||||
:PROPERTIES:
|
|
||||||
:ID: 2dc12a82-cb0c-40f1-ab5a-46d2800e9e53
|
|
||||||
:END:
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(defvar nd/required-exes '()
|
|
||||||
"Running list of executables required to run various configuations.")
|
|
||||||
|
|
||||||
(defun nd/require-bin (bin-or-path &optional pkg-type pkg-name)
|
|
||||||
"Indicate that a binary executable is required.
|
|
||||||
BIN-OR-PATH is a string indicating the executable name or path to
|
|
||||||
the executable. PKG-TYPE indicates how BIN-ON-PATH must be
|
|
||||||
installed (see `nd/required-exes' for available types). PKG-NAME
|
|
||||||
indicates the package name to install which provides BIN-OR-PATH,
|
|
||||||
which defaults to BIN-OR-PATH if not given."
|
|
||||||
(let* ((pt (or pkg-type :pacman))
|
|
||||||
(name (f-base bin-or-path))
|
|
||||||
(pn (or pkg-name name))
|
|
||||||
(new (list name
|
|
||||||
:full-path (executable-find bin-or-path)
|
|
||||||
:pkg-type pt
|
|
||||||
:pkg-name pn)))
|
|
||||||
(setq nd/required-exes (cons new nd/required-exes))))
|
|
||||||
|
|
||||||
(defmacro nd/if-bin (bin then &rest else)
|
|
||||||
"Execute THEN if BIN exists, otherwise do ELSE."
|
|
||||||
(declare (indent 1))
|
|
||||||
(if-let (x (alist-get bin nd/required-exes nil nil #'equal))
|
|
||||||
`(if ,(plist-get x :full-path) ,then ,@else)
|
|
||||||
(message "WARNING: executable '%s' must be required" bin)))
|
|
||||||
|
|
||||||
(defmacro nd/when-bin (bin &rest body)
|
|
||||||
"Execute BODY if the program BIN exists."
|
|
||||||
(declare (indent 1))
|
|
||||||
`(nd/if-bin ,bin (progn ,@body)
|
|
||||||
(message "Executable %s not found. Skipping." ,bin)))
|
|
||||||
|
|
||||||
(defun nd/verify-required-packages ()
|
|
||||||
"Verify `nd/required-exes'.
|
|
||||||
All binaries should be specified once."
|
|
||||||
(->> (-map #'car nd/required-exes)
|
|
||||||
(-uniq)
|
|
||||||
(length)
|
|
||||||
(equal (length nd/required-exes))))
|
|
||||||
|
|
||||||
(defun nd/get-dependencies (keys)
|
|
||||||
"Return list of all dependencies.
|
|
||||||
KEYS is a list of keywords that indicate the :pkg-type of
|
|
||||||
dependencies to return."
|
|
||||||
(->> nd/required-exes
|
|
||||||
(--filter (memq (plist-get (cdr it) :pkg-type) keys))
|
|
||||||
(--map (plist-get it :pkg-name))
|
|
||||||
(-uniq)))
|
|
||||||
#+end_src
|
|
||||||
** external
|
** external
|
||||||
Some useful external libraries that I use all over the place
|
Some useful external libraries that I use all over the place
|
||||||
*** string manipulation
|
*** string manipulation
|
||||||
|
@ -208,6 +154,147 @@ Define a path to internal libraries (either things I am developing or external =
|
||||||
(defun nd/expand-lib-directory (path)
|
(defun nd/expand-lib-directory (path)
|
||||||
(f-join user-emacs-directory nd/lib-directory path))
|
(f-join user-emacs-directory nd/lib-directory path))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
** system dependencies
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 2dc12a82-cb0c-40f1-ab5a-46d2800e9e53
|
||||||
|
:END:
|
||||||
|
*** Definitions
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
;; dependency declaration
|
||||||
|
|
||||||
|
(defvar nd/required-exes nil
|
||||||
|
"Running list of executables required to run various configuations.")
|
||||||
|
|
||||||
|
(defconst nd/valid-package-types '(:pacman :aur :gem :local))
|
||||||
|
|
||||||
|
(defun nd/require-bin (bin-or-path &optional pkg-type pkg-name)
|
||||||
|
"Indicate that a binary executable is required.
|
||||||
|
BIN-OR-PATH is a string indicating the executable name or path to
|
||||||
|
the executable. PKG-TYPE indicates how BIN-ON-PATH must be
|
||||||
|
installed (see `nd/required-exes' for available types). PKG-NAME
|
||||||
|
indicates the package name to install which provides BIN-OR-PATH,
|
||||||
|
which defaults to BIN-OR-PATH if not given."
|
||||||
|
(let* ((pt (or pkg-type :pacman))
|
||||||
|
(name (f-base bin-or-path))
|
||||||
|
(pn (or pkg-name name))
|
||||||
|
(new (list :full-path (executable-find bin-or-path)
|
||||||
|
:pkg-type pt
|
||||||
|
:pkg-name pn)))
|
||||||
|
(cond
|
||||||
|
((not (memq pt nd/valid-package-types))
|
||||||
|
(warn "Invalid dependency type: %s" pt))
|
||||||
|
((ht-get nd/required-exes name)
|
||||||
|
(warn "Dependency already required: %s" name))
|
||||||
|
(t
|
||||||
|
(ht-set nd/required-exes name new)))))
|
||||||
|
|
||||||
|
(defmacro nd/if-bin (bin then &rest else)
|
||||||
|
"Execute THEN if BIN exists, otherwise do ELSE."
|
||||||
|
(declare (indent 1))
|
||||||
|
(if-let (x (ht-get nd/required-exes bin))
|
||||||
|
`(if ,(plist-get x :full-path) ,then ,@else)
|
||||||
|
(warn "executable '%s' must be required" bin)))
|
||||||
|
|
||||||
|
(defmacro nd/when-bin (bin &rest body)
|
||||||
|
"Execute BODY if the program BIN exists."
|
||||||
|
(declare (indent 1))
|
||||||
|
`(nd/if-bin ,bin (progn ,@body)
|
||||||
|
(message "Executable %s not found. Skipping." ,bin)))
|
||||||
|
|
||||||
|
(defun nd/get-dependencies (keys)
|
||||||
|
"Return list of all dependencies.
|
||||||
|
KEYS is a list of keywords that indicate the :pkg-type of
|
||||||
|
dependencies to return."
|
||||||
|
(->> (ht-values nd/required-exes)
|
||||||
|
(--filter (memq (plist-get it :pkg-type) keys))
|
||||||
|
(--map (plist-get it :pkg-name))
|
||||||
|
(-uniq)))
|
||||||
|
|
||||||
|
;; pacman-specific introspection
|
||||||
|
|
||||||
|
(defvar nd/aur-helper nil
|
||||||
|
"The aur helper command installed on this system.")
|
||||||
|
|
||||||
|
(defun nd/find-aur-helper ()
|
||||||
|
"Return the current aur helper installed (or nil if none)."
|
||||||
|
;; add more as needed
|
||||||
|
(let ((helpers (list "yay")))
|
||||||
|
(-first #'executable-find helpers)))
|
||||||
|
|
||||||
|
(defun nd/pacman-dependencies (uninstalled?)
|
||||||
|
"Return pacman and aur dependencies.
|
||||||
|
If UNINSTALLED? is non-nil, return only the packages that are not
|
||||||
|
installed but required by this config."
|
||||||
|
(let ((ps (nd/get-dependencies '(:pacman)))
|
||||||
|
(as (nd/get-dependencies '(:aur))))
|
||||||
|
(if (not uninstalled?) `(,ps ,as)
|
||||||
|
(let ((is (->> (shell-command-to-string "pacman -Qq")
|
||||||
|
(s-split "\n"))))
|
||||||
|
(list (--remove (member it is) ps)
|
||||||
|
(--remove (member it is) as))))))
|
||||||
|
|
||||||
|
(defun nd/shell-test (cmd &rest args)
|
||||||
|
"Return t if CMD with ARGS succeeds."
|
||||||
|
(= 0 (apply #'call-process cmd nil nil nil args)))
|
||||||
|
|
||||||
|
(defun nd/pacman-pkg-exists (pkg)
|
||||||
|
"Return t if PKG exists in the pacman repositories."
|
||||||
|
(nd/shell-test "pacman" "-Ss" (format "^%s$" pkg)))
|
||||||
|
|
||||||
|
(defun nd/aur-pkg-exists (pkg)
|
||||||
|
"Return t if PKG exists in the aur repositories."
|
||||||
|
;; add more commands as needed
|
||||||
|
(cond
|
||||||
|
((equal nd/aur-helper "yay")
|
||||||
|
(let ((out (->> (format "yay -Ssaq %s" pkg)
|
||||||
|
(shell-command-to-string)
|
||||||
|
(s-split "\n"))))
|
||||||
|
(and (member pkg out) t)))
|
||||||
|
(t
|
||||||
|
(warn "No aur helper found"))))
|
||||||
|
|
||||||
|
(defun nd/invalid-pacman-pkgs ()
|
||||||
|
"Return pacman and aur packages that don't exist."
|
||||||
|
(-let* (((pacman aur) (nd/pacman-dependencies t))
|
||||||
|
(ps (-remove #'nd/pacman-pkg-exists pacman))
|
||||||
|
(as (-remove #'nd/aur-pkg-exists aur)))
|
||||||
|
(list ps as)))
|
||||||
|
|
||||||
|
(defun nd/warn-invalid-pacman-deps ()
|
||||||
|
"Warn user of any invalid pacman/aur packages in this config."
|
||||||
|
(-let (((pacman aur) (nd/invalid-pacman-pkgs)))
|
||||||
|
(--each pacman
|
||||||
|
(message "Pacman package does not exist in any configured repo: %s" it))
|
||||||
|
(--each aur
|
||||||
|
(message "Pacman package does not exist in AUR repo: %s" it))))
|
||||||
|
|
||||||
|
(defun nd/install-arch-dependencies ()
|
||||||
|
"Install all missing pacman/aur dependencies."
|
||||||
|
(cl-flet
|
||||||
|
((try-install
|
||||||
|
(what cmd args pkgs)
|
||||||
|
(if (not pkgs) (message "No %s packages to install" what)
|
||||||
|
(let ((res (if (apply #'nd/shell-test cmd (append args pkgs))
|
||||||
|
"Installed"
|
||||||
|
"Failed to install")))
|
||||||
|
(message "%s %s packages: %s" res what (s-join ", " pkgs))))))
|
||||||
|
(-let (((pacman aur) (nd/pacman-dependencies t)))
|
||||||
|
(try-install "official" "pacman" '("-S") pacman)
|
||||||
|
(if nd/aur-helper
|
||||||
|
(let ((aur-args (list "--needed" "--noconfirm" "--norebuild"
|
||||||
|
"--removemake" "-S")))
|
||||||
|
(try-install "unofficial" nd/aur-helper aur-args aur))
|
||||||
|
(message "No aur helper found")))
|
||||||
|
nil))
|
||||||
|
#+end_src
|
||||||
|
*** Setup
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
;; zero this out so reload won't complain about things already present
|
||||||
|
(setq nd/required-exes (ht-create #'equal)
|
||||||
|
nd/aur-helper (nd/find-aur-helper))
|
||||||
|
|
||||||
|
(nd/warn-invalid-pacman-deps)
|
||||||
|
#+end_src
|
||||||
** macros
|
** macros
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:ID: c83dc04a-754a-4ae4-b7da-cad984a7cb18
|
:ID: c83dc04a-754a-4ae4-b7da-cad984a7cb18
|
||||||
|
@ -936,7 +1023,7 @@ Also, this seems to have no relation to the =anaconda.el= package for python.
|
||||||
(f-canonical))
|
(f-canonical))
|
||||||
"Path to conda (which really means mamba) installation.")
|
"Path to conda (which really means mamba) installation.")
|
||||||
|
|
||||||
(nd/require-bin (f-join nd/conda-home "bin" "conda"))
|
(nd/require-bin (f-join nd/conda-home "bin" "conda") :local)
|
||||||
|
|
||||||
(nd/when-bin "conda"
|
(nd/when-bin "conda"
|
||||||
(use-package conda
|
(use-package conda
|
||||||
|
@ -1074,7 +1161,7 @@ Anaconda is much lighter and easier than elpy. Also use ipython instead of the b
|
||||||
|
|
||||||
(nd/require-bin "ipython")
|
(nd/require-bin "ipython")
|
||||||
(nd/require-bin "flake8")
|
(nd/require-bin "flake8")
|
||||||
(nd/require-bin "pylint")
|
(nd/require-bin "python-pylint")
|
||||||
|
|
||||||
(use-package python
|
(use-package python
|
||||||
:after flycheck
|
:after flycheck
|
||||||
|
|
Loading…
Reference in New Issue