How to try out somebody's Guix package

The GNU Guix package ecosystem is growing rapidly, and given all of the amazing features Guix delivers, I always check Guix first to see if the software I want is available.

When it's not, there's still a possibility that a search will reveal somebody else has packaged what I want. For example, I share packages that I'm testing before I submit them upstream. Downloading a package definition to test it out on your machine is easy, but there are some tricks. It took me a while to figure out what to do, and I see other people get confused regularly due to unfortunate decisions in Guix's design. So here's a guide to get you on the right path.

First, a warning

Guix packages look a lot like data, since they mostly consist of URLs and descriptions and so on. But don't be fooled! Packages are code, and building a package runs arbitrary code on your computer. A malicious package can do dastardly things. So don't run packages from people you don't trust unless you've inspected them yourself and gain some understanding of what they're doing.

The Guile load path

Guix discovers what packages exist by loading all the Guile code on its guile-load-path and collecting package definitions. That means you can add new packages just by adding files that define them to your Guix load path.

Here's an example using my repository:

  git clone https://github.com/ryanprior/guix-packages.git
  cd guix-packages
  guix environment -L. --ad-hoc countdown -- countdown 10s

To break it down:

Cloning a repository

People often put Guix packages in a git repository. Cloning the whole repository is often the most convenient way to get access to those packages. Sometimes one file will refer to things defined in another, and by getting the whole repository you have a better chance to make sure you have everything that's needed, with the right directory structure.

If you don't have Git installed yet, run guix install git before you start.

Using guix environment with the -L flag

The guix environment command creates a subshell with new Guix packages added to its environment, without making any changes to your profile. This makes it great for trying things out.

With each use of the -L flag, Guix will add an additional path to its Guile load path, allowing it to find more packages. So invoking environment with -L. instructs Guix to look in the current directory (called .) for packages.

Everything after the -- in the command is run inside the subshell. So when we put together the pieces, the command looks in all of Guix plus the current directory for a package called "countdown," adds it to the environment of a subshell, runs "countdown 10s" inside that subshell, and exits. If you want to interact with the subshell directly, you can omit the -- countdown 10s portion entirely:

  ~/guix-packages$ countdown
  countdown: command not found
  ~/guix-packages$ guix environment -L. --ad-hoc countdown
  ~/guix-packages$ type countdown
  countdown is /gnu/store/w0gy4d14b2byir64jakpsrw3j73n916a-profile/bin/countdown
  ~/guix-packages$ exit
  ~/guix-packages$ type countdown
  bash: type: countdown: not found

Troubleshooting

This method is very sensitive to the current working directory, which is the directory you run the guix command from. For example, if I run the same guix environment command above inside the guix-packages/testing directory, I get a ton of warnings and then an error message:

# […lots of warnings]
guix environment: warning: failed to load '(vlang)':
no code for module (vlang)
./vlang.scm:4:0: warning: module name (testing vlang) does not match file name 'vlang.scm'
hint: File `./vlang.scm' should probably start with:

     (define-module (vlang))

guix environment: error: countdown: unknown package

If you run guix environment in the parent directory of guix-packages, it will first of all take ages as it tries to search every subdirectory for Scheme files, and then it will also print an error and not give you what you want.

Ugh! There's no way to make this work other than to ensure and triple-check you're in the exact right directory. If you're troubleshooting, you can try a few different directories and see if you hit on the right one. You can also look at the module definition for clues. For example, at the top of testing/countdown.scm we see:

  (define-module (testing countdown)
   ;; snip
  )

The clue this gives you is that this file expects to be found at ./testing/countdown.scm, and thus it expects you to run Guix with a Guile load path that has not its directory, but that directory's parent.

The -f flag

If you have a single file that defines a package of interest, you can use the -f flag to refer to the package it evaluates to. But you have to take care that the file actually evaluates to the desired package and doesn't merely define it.

For example, in my guix-packages repository, the countdown.scm file has a structure like this:

  (define-module (testing countdown) )


  (define-public countdown
    (package
      (name "countdown")
      ))

What's important to note is that the last form of the file is a (define-public) form, which does not have any return value. A file evaluates to the return value of its last form, so while this file defines the countdown package, it evaluates to nothing. So if you run guix build -f countdown.scm it will give you an error message like so:

~/guix-packages/testing$ guix build -f countdown.scm 
guix build: error: #: not something we can build

hint: If you build from a file, make sure the last Scheme expression returns a package value.  `define-public' defines a variable, but returns `#'.  To fix this, add a Scheme
expression at the end of the file that consists only of the package's variable name you defined, as in this example:

     (define-public my-package
       (package
         ...))
     
     my-package

If you edit countdown.scm to put a new line at the end that just says countdown, then Guix will build the package:

  ~/dev/guix-packages/testing$ cat >>countdown.scm

  countdown
  ~/dev/guix-packages/testing$ guix build -f countdown.scm 
  /gnu/store/6x2lmakp59vzdjbjqxha2560g2jrqdyv-countdown-1.0.0

Now the countdown executable is available in the listed directory, and we can start a 10 second countdown like so:

~/dev/guix-packages/testing$ /gnu/store/6x2lmakp59vzdjbjqxha2560g2jrqdyv-countdown-1.0.0/bin/countdown 10s

What if I want guix environment?

We love guix environment! But Guix does not provide us any easy way to use a file with environment. The -f flag that is available on many other commands is not defined for environment. However, with another silly workaround we can make this work.

Instead of putting a line with countdown at the end of countdown.scm, we can make it evaluate to a trivial wrapper around countdown:

  ;; …rest of countdown.scm

  (package
    (inherit countdown)
    (inputs `(("countdown" ,countdown))))

Now we can use guix environment again:

  ~/guix-packages/testing$ guix environment -l countdown.scm 
  ~/guix-packages/testing$ type countdown
  countdown is /gnu/store/4gc33v065h1hm0lq42j1fir7gs151j9s-profile/bin/countdown

Note that we are using -l (lower case L) and not the -L flag we were using before. This does not modify the Guile load path. Instead of creating an environment containing the package the file evaluates to, it creates an environment with the dependencies of that package. This is why we have to create that trivial wrapper which adds countdown as a dependency.

Note that while this method is fiddly in many ways, it is not so dependent as the last one on having everything in exactly the right directory structure and being in the exact right directory when you run your Guix command.

Epilogue: any easy, worry-free way to try people's Guix packages?

The currently available methods for testing a Guix package you find on the Internet are fraught with thorns and pitfalls for the unwary. This can give an overall unfriendly feeling to newcomers.

Suppose we were to come upon a clearing in the thicket where some hackers were having a merry potluck, where everybody brought their packages to try and there was a friendly uniform mechanism to try and share these packages?

Such a tool was proposed: Guix Potluck was submitted in 2017 to address this kind of use case. It never got a public release, but there may be lessons there for a hypothetical future tool that could make guides like this one obsolete.