simfish:

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.

cover image

Install

Requirements

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 ControlComment
--networkallow containers to have network access
  
--timeout=TSignal error if the container does not load after T second.
--max-jobs=Nspecify the number of build jobs during container construction.
--cores=Nspecify the number of CPU core to use.
  
--expose=SPECallow containers to have read-only access to host
 file or directory specified with SPEC
  
--share=SPECallow 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