Guix Shell, Direnv, and Environment Variables

Many programs completely rely on environment variables (sometimes shortened to env vars) to look up and find information. Some examples of these programs are info, man, guile, even sh! These environment variables can be populated in a variety of ways, and Guix can usually make most of them work nicely.

There are a few environment variables that Guix does not always automatically populate. These include $INFOPATH, $MANPATH, $PYTHON_PATH, and others. Some of these environment variables are useful for development environments you get with guix shell.

I sometimes use direnv to automatically change my environment variables, to make moving between projects easier and faster. I also use envrc-mode to allow Emacs to automatically pick up these changes. Direnv is a stand-alone program reads a .envrc file from your project's repository and updates your shell's environment variables for you, restoring your old ones when you leave the project. envrc-mode is an Emacs minor-mode which will use direnv to update Emacs buffers for you as you move between buffers in different projects. But there is no good way to get direnv to play with Guix's modified $INFOPATH environment variables when working in these projects. This is not a problem with Direnv, but a fact of the way Guix works.

Guix Shell and Environment Variables

When you enter a guix shell environment for development, the $GUIX_ENVIRONMENT variable is set with the path to the profile in the store you built for this project. If you want other environment variables (like $INFOPATH) to be automatically updated upon entering the shell, you must include a package that forces those environment variables to be updated.

I will use accessing Texinfo documentation manuals for the packages in the project's dependencies as the example for this post. This post is applicable to any environment variables that are updated by including specific programs in a project's Guix environment. My example is: you want to access GCC's info manual inside of your development shell.

To start, you must have gcc-toolchain in your manifest or package native-inputs/inputs lists to use gcc in your development shell.

;; manifest.scm
;; To enter a development shell: guix shell -m manifest.scm
(specifications->manifest
 (list
  "gcc-toolchain"))

But if you enter the shell and do info gcc, you will not find the manual!

info gcc
info: No menu item 'gcc' in node '(dir)Top'

Setting Environment Variables

To get Info manuals added by updating $INFOPATH, you need to add a program that can read and display Info manuals (an Info reader) to the profile. There are two packages that provide an Info reader, which come from the same underlying source, but with different outputs.

  1. texinfo - Use this package if you are writing a Texinfo manual with your project. This will provide both the sources to compile Texinfo files and produce the final output and a reader to view the built output.
  2. info-reader - Use this package if you are only going to read Texinfo manuals during your project's development, but you will not be writing any manuals. This package uses the same sources as the texinfo package, but only produces the reader, removing the tools required to build manuals. By excluding the pieces needed to build Texinfo manuals, the final size of info-reader is significantly smaller. It is a comparatively tiny package, making it ideal for inclusion in per-project manifests.

As of Guix commit 425cf1fbe2fff25bda1b5eb948ce01170d23ab6a, Guix's total size for these packages are:

  • guix size texinfo - 172.3 MiB total, 10.1 MiB for just texinfo.
  • guix size info-reader - 86.2 MiB total, 2.9 MiB for just info-reader.

Both of these packages will update $INFOPATH for you. This happens because the texinfo package also specifies the native-search-paths field in Guix's package record. info-reader inherits texinfo's package definition, so info-reader will also set these environment variables for you. Below is a snippet of texinfo's Guix package specification to show what gives texinfo and info-reader the ability to update $INFOPATH.

(define-public texinfo
  (package
    (name "texinfo")
    ...
    (native-search-paths
     ;; This is the variable used by the stand-alone Info reader.
     (list (search-path-specification
            (variable "INFOPATH")
            (files '("share/info")))))
    ...))

(define-public info-reader
  (package/inherit texinfo
    (name "info-reader")
    ...))

The native-search-paths field is what Guix uses to determine what a environment variables a package honors and can be used for finding information through those honored environment variables. The search-path-specification function comes from Guix and is responsible for scanning the built environment and prepends new entries for this profile to $INFOPATH.

So, let's add info-reader to our manifest:

;; manifest.scm
;; To enter a development shell: guix shell -m manifest.scm
(specifications->manifest
 (list
  "gcc-toolchain"
  "info-reader"))

After adding info-reader to your environment, and re-entering your development environment, you should see something like the following:

guix shell -m manifest.scm
hint: Consider passing the `--check' option once to make sure your shell does not clobber environment variables.

The following derivation will be built:
  /gnu/store/5ngaqadbwzfav2gcihkf364dvlv2h7k3-profile.drv

building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 2 packages...

The "building directory of Info manuals..." is your signal that $INFOPATH has been updated. Now let's try reading GCC's Texinfo manual!

info gcc
# GCC's manual pops up in an ncurses window now!

It works!

Now that Guix is also updating $INFOPATH for us, direnv will pick up Guix's change. Now you can run info gcc without needing to explicitly enter a guix shell and everything will work. In addition, because direnv is working, Emacs's envrc-mode will pick it up, so I can now leverage Emacs's info-mode to read documentation for my projects' dependencies.

emacs
# Inside of Emacs, C-h R gcc RET
# GCC's manual should open.

Conclusion

I always recommend that you write a Guix shell environment for every project. Guix shell will set many of the necessary environment variables for you, but not all of them. If you want to read the manuals for the packages your project depends on, you need to include, at least, the info-reader package. This is true for other environment variables too, like $PYTHONTZPATH, $GUIX_PYTHONPATH, and others.

If you are struggling to make a program or environment work because of environment variables, you may just need to add another program to your environment.