From ce126f34eed538f61b874d8ba60fee67d3750967 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 23 Oct 2019 20:55:52 +0200 Subject: [PATCH] Emacs/Guix: Add manifest manipulation commands. --- .emacs.d/lisp/init-guix.el | 130 +++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/.emacs.d/lisp/init-guix.el b/.emacs.d/lisp/init-guix.el index e69b29ab..e4753ebc 100644 --- a/.emacs.d/lisp/init-guix.el +++ b/.emacs.d/lisp/init-guix.el @@ -44,4 +44,134 @@ with previous." (with-eval-after-load 'guix-ui-generation (define-key guix-generation-list-mode-map "=" #'ambrevar/guix-generations-list-diff-this)) +(defvar ambrevar/guix-extra-channels "~/.guix-extra-channels") +(defvar ambrevar/guix-extra-profiles "~/.guix-extra-profiles") +(defvar ambrevar/guix-manifest-directory "~/.package-lists") +(defvar ambrevar/guix-channel-spec-directory "~/.package-lists") +(defvar ambrevar/guix-always-use-channel-specs nil + "If non-nil, automatically use a channel specification matching the chosen manifest. +The channel specification is looked up in +`ambrevar/guix-channel-spec-directory'.") + +(cl-defun ambrevar/guix-query-file (&key file directory + (filter ".") + (prompt "File: ") + (name-function #'identity)) + "Query a file matching FILTER in DIRECTORY. +Return (NAME FILE). +If FILE is non-nil, then this function is useful to derive the name of the manifest. +NAME-FUNCTION take the file base name as argument and returns NAME." + (cl-flet ((name (file) + (replace-regexp-in-string + "-?guix-?" "" + (funcall name-function + (file-name-base file))))) + (if file + (list (name file) file) + (let ((files (mapcar (lambda (file) + (list (name file) file)) + (directory-files directory 'full filter)))) + (assoc (completing-read prompt (mapcar #'first files)) + files))))) + +(defun ambrevar/guix-query-manifest (&optional manifest) + "Query a manifest as found in `ambrevar/guix-manifest-directory'. +Return (NAME FILE). +If MANIFEST is non-nil, then this function is useful to derive the name of the manifest." + (ambrevar/guix-query-file + :file manifest + :directory ambrevar/guix-manifest-directory + :filter "manifest" + :prompt "Manifest: " + :name-function (lambda (name) + (replace-regexp-in-string "-?manifest-?" "" name)))) + +(defun ambrevar/guix-query-channel-spec (&optional channel-spec) + "Query a channel specification as found in `ambrevar/guix-channel-spec-directory'. +Return (NAME FILE). +If CHANNEL-SPEC is non-nil, then this function is useful to derive the name of +the channel specification." + (ambrevar/guix-query-file + :file channel-spec + :directory ambrevar/guix-channel-spec-directory + :filter "channels" + :prompt "Channel specification: " + :name-function (lambda (name) + (replace-regexp-in-string "-?channels?-?" "" name)))) + +(defun ambrevar/guix-edit-manifest (&optional manifest) + "Edit MANIFEST. +If MANIFEST is nil, it is queried from the manifests found in `ambrevar/guix-manifest-directory'." + (interactive) + (setq manifest (second (ambrevar/guix-query-manifest manifest))) + (find-file manifest)) +(global-set-key (kbd "C-x c g") #'ambrevar/guix-edit-manifest) + +(defun ambrevar/guix-save-channel-specs (dest) + "Save current Guix channel specification to DEST." + (call-process "guix" + nil `(:file ,dest) nil + "describe" "--format=channels")) + +(defun ambrevar/guix-find-channel-from-manifest (pattern) + "Return the channel specification file matching PATTERN in +`ambrevar/guix-channel-spec-directory'." + (first (directory-files ambrevar/guix-channel-spec-directory 'full + (concat pattern "-channel")))) + +(defun ambrevar/guix-install-manifest (&optional manifest channel) + "Install Guix manifest to `ambrevar/guix-extra-profiles'. + +Manifest is queried from those found in `ambrevar/guix-manifest-directory'. +Guix channel specification is stored in `ambrevar/guix-channel-spec-directory'. + +With a prefix argument, query for a channel specification file. + +If CHANNEL is nil and `ambrevar/guix-always-use-channel-specs' is +non-nil, then try to use a channel specification file from +`ambrevar/guix-channel-spec-directory' if any." + (interactive) + (let* ((manifest-pair (ambrevar/guix-query-manifest manifest)) + (manifest-name (first manifest-pair)) + (manifest (second manifest-pair)) + (channel (or channel + (and current-prefix-arg + (second (ambrevar/guix-query-channel-spec))) + (and ambrevar/guix-always-use-channel-specs + (ambrevar/guix-find-channel-from-manifest manifest-name)))) + (guix (if channel + (let ((dest (expand-file-name + manifest-name + ambrevar/guix-extra-channels))) + (make-directory dest 'parents) + (format "guix pull --channels=%s --profile=%s/guix && %s/guix/bin/guix" + (shell-quote-argument channel) + (shell-quote-argument dest) + (shell-quote-argument dest))) + "guix"))) + (make-directory (expand-file-name manifest-name + ambrevar/guix-extra-profiles) + 'parents) + (let ((eshell-buffer-name "*guix*")) + (eshell) + (when (eshell-interactive-process) + (eshell t)) + (eshell-interrupt-process) + (insert guix " package" (concat " --manifest=" manifest) + (if (string= "default" manifest-name) + "" + (concat " --profile=" (expand-file-name ambrevar/guix-extra-profiles) + "/" manifest-name + "/" manifest-name))) + (eshell-send-input)) + (unless channel + ;; TODO: Only do this when manifest install has succeeded. + (ambrevar/guix-save-channel-specs + (format "%s/guix-%s-channels.scm" + ambrevar/guix-channel-spec-directory + manifest-name))))) +(global-set-key (kbd "C-x c G") #'ambrevar/guix-install-manifest) + +;; TODO: See `guix-apply-manifest' and expand on it. + (provide 'init-guix)