Gix: Literate Programming with Emacs org-mode & GNU/Guix
Gix is a literate programming environment that extends Emacs org-mode
with specialized source blocks that enable GNU/Guix based package
management per document. Gix provides a CLI client gix
, which can
run org documents directly from the command-line or in a
containerized environment.
Install
Requirements
- Linux system equipped with
GNU/Guix
. See the official Guix installation instructions.
To install Gix first clone the gix project repository
to some directory of choice Here,~/opt/gix
is treated as the install path
mkdir -p ~/opt
cd ~/opt
git clone https://gitlab.com/aarongile/projects/gix
Next integrate Gix with your shell environment by adding
the snippet below to either Bash ~/.bashrc
or Zsh ~/.zshenv
# point this variable to the your Gix install directory
export GIX_DIR=${HOME}/opt/gix
# point this variable to the root of your custom Gix
# component directory. This is the directory where
# custom gix components are stored
export GIX_COMPDIR=${HOME}/Documents/gix-components
# source the gix environment setup, which defines
# necessary environment variables and adds the gix command
# to PATH
. ${GIX_DIR}/.gixrc
Finally start a new shell and run the Gix configuration command to finalize the install:
gix configure
That’s it, Gix is installed and ready to go!
Getting Started
Literate programming using Emacs involves writing an org document that mixes prose and code.
Using Gix, in addition to mixing code with prose, it is also possible to specify code dependency and execution environment. This is made possible by leveraging GNU/Guix’s software management features Introduction to Guix Features.
To get a better sense of how this can be done with Gix, consider
the following hello world literate program hello.org
#+title: hello world
This is a hello world literate program using Gix.
It displays a greeting message using the Python.
#+begin_src shell :tangle main
#! /usr/bin/env python3
print("Hello World!")
#+end_src
To run this document =python= has to be available locally.
The dependency can be specified using a scheme manifest
source block
#+begin_src scheme :tangle manifest
(specifications->manifest '("python"))
#+end_src
To run the program switch to a directory where,
hello.org
is located and execute:
# verbose is optional
gix hello --verbose
The command should produce the expected output:
> Hello World!
Managing Dependencies
A Gix document can specify program dependency using specialized Guile/Scheme or Emacs/Lisp source block.
The Guile/Scheme based source block specifies package dependencies using a Guix manifest specification syntax See Basic Setup with Manifests.
#+comment: emacs.org
#+title: Emacs
This is an example Gix program =emacs.org= which installs
the latest Emacs binary along with the Emacs package Helm,
which is a popular command completion system.
* Program Dependencies
#+begin_src scheme :tangle manifest
(specifications->manifest '("emacs-next" "emacs-helm"))
#+end_src
As shown in the example document emacs.org
, the block’s tangle
property is set to the keyword manifest
. This pseudo tangle target
instructs Gix that all document dependencies are specified in
this one block.
Alternatively, instead of specifying multiple dependencies in
a single manifest, each dependency can be specified separately
using an Emacs/Lisp block and a use-package
syntax In contrast
to a Guile/Scheme based manifest, an Emacs/Lisp use-package
based package specification is not required to have a tangle target.
Instead the keyword :guix
is applied on a per
package basis.
#+begin_src emacs-lisp
;; Emacs
(use-package emacs-next
;; the keyword :guix instructs Gix to install
;; this package using the available Guix software
;; channel
:guix (:using channel :name emacs-next ))
;; Helm
(use-package helm
:guix (:using channel :name emacs-helm)
:config
(helm-mode 1))
#+end_src
The second approach is particularly useful when managing Emacs packages. Since it is customary to place package specification along with its configuration for readability.
In either of the two case, emacs.org
program can
be installed on a local machine with:
# notice we drop .org extension
gix build emacs --verbose
Running Code
A Gix document can expose a script block that can be run from the command line.
This is accomplished using a main
block, that is a shell
source block with its :tangle
property
set to the keyword main
.
Returning to emacs.org
example, See section 1
and updating it to include a main block looks like,
* Running Emacs
Emacs can be run as a daemon using the =--daemon= option.
Once the daemon is started a light weight Emacs instance
can be used to connect the daemon using =emacsclinet=.
This approach will significantly improve Emacs startup time
by applying load Emacs only once policy.
#+begin_src shell :tangle bin/emacs.sh
,#!/bin/env bash
if ! emacsclient -e 0 >&/dev/null; then
emacs --daemon &
fi
emacsclient -c "$@"
#+end_src
It should be possible to run the script block from the command line with:
# verbose is optional
gix emacs --verbose
As stated in the prose, the script in the main
block launches an Emacs frame after connecting to
a running Emacs daemon.
It is also worth mentioning that a Gix document can
make its dependencies available from the command line if it
contains a host.env
script block More on host.env
script block in the next section.
This will work even if the block is an empty place-holder:
#+begin_src shell :tangle host.env
# nothing to do here
#+end_src
Setting up a Shell Environment
In addition to making Gix documents runnable it is also possible
to provide code block that enables the documents to be to hooked
into the host machine’s shell environment. This is achieved using
host.env
code block.
A host.env
block is useful for setting up shell variables, functions, and aliases.
Any entry that goes into host.env
block will eventually be sourced
into ~/.bashrc
.
The code snippet below Following example developed in
sections 1,2
creates the alias command e
, that introduces a shortcut for
launching Emacs using Gix:
* Startup Settings
Settings in this section is intended to be sourced
as part of the host's interactive shell session.
#+begin_src shell :tangle host.env
alias e="gix emacs $@"
#+end_src
Once this configuration is applied using gix build emacs
,
typing in e
on the terminal should launch Emacs using Gix.
Setting up Container Environment
By default gix DOCUMENT
command runs DOCUMENT
in the host machine. It can,
however, be made to run in a GNU/Guix container environment.
This is achieved using container.opt
shell block. The block contains
a list of Guix container environment options See Invoking Guix Environment.
Building on the emacs.org
example from the last three
sections See section 1,
2,
and 3, and adding
the org snippet below will instruct Gix to run Emacs in a GNU/Guix
container environment:
* Containerization
This code snippet below configures Emacs to run
in a Guix container environment.
#+begin_src shell :tangle container.opt
# enable access to network
--network
# working directory
--share="${HOME}/Documents/dev=/home/user/dev
# X11 client/server communication
--expose=/tmp/.X11-unix
#+end_src
Further, the block configures the container’s access to several
system resources. The --expose
option, for example, is used to grant
read-only access to selected file system entries. In this case, to /tmp/.X11-unix
,
which enables Emacs to connect with an X11 server.
In contrast, the --share
option is used to grant both read and
write access to file system entries. In the example above,
it grants the container read-write access to ${HOME}/Documents/dev
directory, which
maps to /home/user/dev
within the container environment This is a powerful feature and enables composing custom file system layout by combining several paths from the host file system.
In general, container.opt
options configure access to system resources such
as the file system and network. Some useful resource access
configuration options are summarized in table-1
Resource Access ControlSummary of Network, Computing, and File System Resource Access Control Options
Resource Access Control | Comment |
---|---|
--network | allow containers to have network access |
--timeout=T | Signal error if the container does not load after T second. |
--max-jobs=N | specify the number of build jobs during container construction. |
--cores=N | specify the number of CPU core to use. |
--expose=SPEC | allow containers to have read-only access to host |
file or directory specified with SPEC | |
--share=SPEC | allow containers to have both read and write access to |
host file system specified with SPEC | |
SPEC has the form: | |
--share /tmp/t1=/tmp : map host /tmp/t1 => container /tmp | |
--share /tmp/t1 : map host /tmp/t1 => =/tmp/t1 |
Improving Command Line Documentation
Gix shell scripts are not in general suited for org-mode based documentation. Since she script documentation is preferably accessed via the command line during use.
To help write better command line manuals, Gix implements
a utility command gix help
, that can generate a man page on
the fly. To take advantage this command line documentation
facility a Gix program must contain a man
shell block, that is
formatted following Perl’s Plain Old Documentation(POD)
format https://perldoc.perl.org/perlpod.
Returning to emacs.org
example one last time, its command line
interface documentation can be written as follows:
* Man Page
#+begin_src shell :tangle man
=pod
=head1 NAME
gix emacs - Run Emacs in a GNU/Guix container environment.
=head1 DESCRIPTION
Emacs is much better than Vim. Yes it is!
=head1 SYNOPSIS
gix emacs
=head1 LICENSE
Copyright (C) 2021 <author@email.place>
=cut
#+end_src
The documentation can be accessed from the command line via,
gix help --command=emacs