Monday, 21 October 2019
GNU Guix (just like Nix) can easily spawn development environments with all the necessary dependencies one may need to work on a project.
For example: In a shell session, all you would have to do is to run
$ guix shell gcc
and a new shell is launched with adjusted environmental variables, so that GCC can now be used. Likewise, you can instruct Guix to fetch the developmental dependencies, i.e. exactly what you need to build a package
$ guix shell --development gcc
As a matter of convenience, one can also specify what an environment
should consist of and store that in a file. An easy way to generate such
a file is using the --export-manifest
option:
$ guix shell --export-manifest --development gcc > manifest.scm
On my system this generates this file:
;; What follows is a "manifest" equivalent to the command line you gave.
;; You can store it in a file that you may then pass to any 'guix' command
;; that accepts a '--manifest' (or '-m') option.
(package->development-manifest
(specification->package "gcc"))
More details on other options can be found in the manual.
This is fine, but if you use Emacs, then the shell and the editor are
“inverted”, or rather Emacs performs the function of a shell (generic
user interface, that wraps the kernel). To use something like
guix shell
, you’d have to either
guix shell
session so that it inherits
the environmental variables set by Guix.M-x shell
,
using shell-command
, compile
, etc. with a
guix shell ... --
prefix.`Neither of these two options are that convenient, so I was unusually delighted when I found a simple solution in the recently updated buffer-env by Augusto Stoffel (author of many under-appreciated packages). Reading through the source for the first time was a real joy, and I kept thinking about it like one would after hearing a good, catchy song.
The package has as simple interface, and for the most part it can be configured in a single line:
(add-hook 'hack-local-variables-hook 'buffer-env-update)
The package was added to GNU ELPA earlier this year, and was initially just described as a pure-elisp direnv implementation. If this is all you need, you don’t need to bother yourself with anything else.
For those unfamiliar with hack-local-variables-hook
,
here is the docstring:
Normal hook run after processing a file’s local variables specs. Major modes can use this to examine user-specified local variables in order to initialize other data structure based on them.
So what buffer-env-update
does, in the case of Guix, is
check the buffer-env-commands
variable and find an entry
that says “if you find a manifest.scm
command”, run
guix shell -D -f \"$0\" -- env -0
(where $0
is replaced with the absolute file path),
parse the output and update variables such as
process-environment
and exec-path
, that
influence how processes are spawned.
The Guix documentation mentions something similar to this idea, but I find it much more complicated than what buffer-env allows me to do.
My configuration goes a bit further than just modifying
hack-local-variables-hook
: Using Setup I have the
following in my init.el
:
;;;;; Dynamic Environments
(setup (:if-package buffer-env)"manifest.scm")
(:option buffer-env-script-name
(add-hook 'comint-mode-hook #'hack-dir-local-variables-non-file-buffer) (add-hook 'hack-local-variables-hook #'buffer-env-update))
In other words, this changes two notable things:
buffer-env-script-name
to looks for
manifest.scm
files, so that buffer-env automatically picks
up on manifest.scm
files. Could be a list as well, if I
also wanted to use guix.scm
files.comint
buffers
(REPLs, M-x shell
, …) so that buffer-env also takes
effect in these kinds of buffers that have no files associated with
them.What I found particularly clever is that there is no need for an
ongoing guix shell
session, but that by calling
env -0
we extract all the environmental variables and apply
them manually. This might run into issues if you use
guix gc
concurrently though. And after all, all there is to
Guix or Nix are just a few clever symbolic links and environmental
variables.