Contributing my first Guix package
I've been using the Guix package manager for the past few months and I really like it. I enjoy specifying the packages I need on my system declaratively and I have utilized Guix to deploy my system configuration to other machines on at least 2 different occasions now. In at least 95% of cases the packages I need are already in the main GNU Guix channels or the nonguix channel. However sometimes I need need to create my own package declarations. Often, this is to use a fork of a package which is already part of Guix or to install a newer/older version of a package than what is in the official repositories. However lately I have added a few package declarations to my dotfiles which I think might be useful to contribute to the main project. The process of contributing to Guix is quite different than the fork and pull-request flow I'm used to, so hopefully this guide can help others contribute to the project.
Setting up the build environment
The first step is to set up an environment where we can edit and test out our packages. The following assumes you already have a Guix installation.
Guix setup
I mostly relied on this documentation to get started with hacking on Guix. First we need to clone Guix:
git clone https://git.savannah.gnu.org/git/guix.git
Optionally, authenticate to make sure you got an authentic copy of the source:
git fetch origin keyring:keyring guix git authenticate 9edb3f66fd807b096b48283debdcddccfea34bad \ "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA"
Now we need to install guile, make, and all the other dependencies for working on Guix. Luckily, since we already are using Guix installing these requirements can easily be done:
guix shell -D guix --pure
Now building Guix is as easy as:
./bootstrap ./configure make -j4
That is it, Guix (hopefully) built correctly. Test building a package like so:
./pre-inst-env guix build hello
pre-inst-env Is a shell script generated after running ./configure and it is useful when developing on Guix. It is used to build and test packages, without actually installing them. This offers assurances so if you make a mistake in your package declarations it won't cause any issues with the underlying system.
Emacs setup
Emacs has a great scheme-mode built in, so there isn't too much to set up, and all of it is optional. But for an easier experience, we can use geiser, which is a scheme REPL akin to sly for Common Lisp. We can also add tempel for useful code-snippets provide by Guix. All of these packages are easily installed via Guix:
(specifications->packages "emacs-geiser" "emacs-geiser-guile" "emacs-tempel")
Now we just need to configure Emacs to use these packages:
(use-package geiser) (use-package geiser-guile :init ;; put the path where you cloned guix here (add-to-list 'geiser-guile-load-path "~/sfs/proj/guix")) ;; YASnippet can be used instead of tempel, if preferred (use-package tempel :defer t :commands (tempel-complete tempel-insert tempel-expand) :config ;; Ensure tempel-path is a list -- it may also be a string. (unless (listp 'tempel-path) (setq tempel-path (list tempel-path))) ;; again use the path where you cloned guix (add-to-list 'tempel-path "~/sfs/proj/guix/etc/snippets/tempel/*"))
Creating and contributing an Emacs package
Creating the package declaration
This is likely the most difficult step in the process and this blog post isn't intended to be a tutorial for creating packages. Declaring Guix packages can be a bit daunting at first, especially for the first time. The documentation on defining packages is an excellent place to refer to in case of questions, however I find that I often learn best by looking at examples. Guix makes this super easy, you can examine the definition of any package using, for example guix edit emacs-apheleia. Try to find a package already in Guix which is similar to the package you want to add and take a look at it. This also helps you know what file you should be putting your package definition in. In my case I wanted to add the emacs eglot-booster package in gnu/packages/emacs-xyz.scm, which can be defined as follows:
(define-public emacs-eglot-booster ;; Not tagged (let ((commit "3f9159a8b7fe87e2f01280a2c4c98ca6dab49d13")) (package (name "emacs-eglot-booster") (version "0.0.2") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/jdtsmith/eglot-booster") (commit commit))) (file-name (git-file-name name version)) (sha256 (base32 "1aq5fpfasgm5lz8hk476c1zqyj33m024nx8w9qv4qrg88y5mq5n9")))) (build-system emacs-build-system) (home-page "https://github.com/jdtsmith/eglot-booster") (synopsis "TODO") (description "TODO") (license license:gpl3+))))
This looks like a lot, but there's not too much going on once we break it down. The main things we need to tell Guix are:
- The repository to get the package from
- The commit (or tag) to checkout
- The hash of the repository at that commit, so that Guix knows the package cloned is the same as what's expected
- This hash can be obtained by running
guix hash -x --serializer=nar .in the root of the package repository
- This hash can be obtained by running
- The version number of the package which was taken from the header of the eglot-booster.el file
Now we want to test and make sure the package builds like so:
./pre-inst-env guix build emacs-eglot-booster
I then added this package declaration to my Guix home configuration so I could test actually using the package.
Making sure the package follows Guix standards
Now that we have a working package we should add a synopsis and description which helps other Guix users find this package using commands like guix search. It is also a good idea to run ./pre-inst-dev guix lint emacs-eglot-booster to check for common problems and ./pre-inst-dev guix style emacs-eglot-booster to make sure the package declaration follows Guix style guidelines. See package guidelines for the full documentation.
Committing and sending the patch to Guix developers
- Run the checklist for the package being added. This makes sure it is following good standards. Some items in this checklist are optional and may not apply in the case of a small package like the one I added.
- Configure git for Guix. This will change git to format patches better. See the documentation, but you will probably just need to run
git config --local include.path etc/git/gitconfigin the repository to include the Guix gitconfig file. - Commit the changes. If using Emacs to write the commit messsage, tempel should provide an
addsnippet which automatically formats the commit message following GNU standards, but if not you can easily manually write this yourself. - Create a patch file by running
git format-patch origin. This command will produce a .patch file which contains the changes made to Guix. - Send an email to
guix-patches@gnu.orgwith the .patch file attached. The subject line should start with[PATCH]. After some time you should get a response email with a bug tracking number.
And at this point you have sent the package off to be reviewed and added! I haven't had any feedback or movement on my patch submission yet, but I understand Guix devs are busy people, and it's no issue for me because I can keep that same package definition in my personal Guix configuration. There was a lot to do for the first time, but now that I understand the process I imagine adding packages in the future will be easier.