# How twist.nix Handles Info Documentation Generation: A Technical Deep Dive

> Learn how twist.nix generates Emacs Info documentation. This technical deep dive covers Texinfo compilation, INFOPATH aggregation, and Nix wrapper integration.

- Repository: [Emacs Twist/twist.nix](https://github.com/emacs-twist/twist.nix)
- Tags: deep-dive
- Published: 2026-03-01

---

**Twist.nix automatically detects Texinfo sources in Emacs packages, compiles them to `.info` files using `makeinfo`, and aggregates them into a unified `INFOPATH` through a Nix wrapper that maintains the `dir` index.**

Twist.nix is a Nix-based Emacs package manager that treats GNU Info documentation as a first-class build output. When building Emacs packages, the system identifies Texinfo sources, generates `.info` files as a separate Nix output, and wires them into the wrapped Emacs environment for seamless access via the `info` command.

## Building Info Files in the Package Builder

The core logic for Info generation resides in `pkgs/emacs/build/default.nix`. This builder runs as a hook during the standard Emacs package build process and handles the detection, compilation, and installation of documentation.

### Identifying Texinfo Sources

Before building, the derivation checks whether the package contains any documentation sources. The `canProduceInfo` predicate uses a regex to identify files ending in `.info`, `.texi`, or `.texinfo`.

```nix
canProduceInfo = hasFile (f: match ".+\\.(info|texi(nfo)?)" f != null);

```

If the derivation is configured to produce extra outputs (`wantExtraOutputs`) and `canProduceInfo` returns true, the builder appends `"info"` to the derivation's `outputs` list. This creates a separate Nix store path for the documentation.

```nix
outputs = ["out"] ++ lib.optional (wantExtraOutputs && canProduceInfo) "info";

```

### Compiling Texi Files with makeinfo

During the `buildPhase`, the `buildInfo` hook runs only when the `info` output is requested. It traverses the source tree, locates every `.texi` and `.texinfo` file, and invokes `makeinfo --no-split` to generate the corresponding `.info` files in the temporary build directory.

```bash
buildInfo = ''
  cwd="$PWD"
  cd $src
  for d in $(find -name '*.texi' -o -name '*.texinfo')
  do
    local basename=$(basename $d)
    local i=$cwd/''${basename%%.*}.info
    if [[ ! -e "$i" ]]
    then
      cd $src/$(dirname $d)
      makeinfo --no-split "$basename" -o "$i"
    fi
  done
  cd $cwd
'';

```

### Installing Info Files to the Nix Store

After compilation, the `installInfo` hook copies the generated `.info` files into the dedicated `info` output directory. This makes the documentation available as a separate Nix store path that can be referenced independently of the main package output.

```bash
installInfo = ''
  mkdir -p $info/share
  install -d $info/share/info
  for i in ${ename}*.info
  do
    install -t $info/share/info $i
  done
'';

```

## Aggregating Info Documentation in the Emacs Wrapper

Once individual packages produce their `.info` files, the `pkgs/emacs/wrapper.nix` component aggregates these into a cohesive environment. The wrapper constructs a custom Emacs distribution where all package documentation is accessible via the `info` command.

### Building the Package Environment

The wrapper creates a package environment that symlinks the `share/info` directories from all dependent packages. By setting `pathsToLink = [ "/share/info" ]`, the builder ensures that every package's documentation output is incorporated into the final environment.

### Updating the dir Index with install-info

After the environment is built, a `postBuild` hook runs `install-info` on every `.info` and `.info.gz` file found in the aggregated `share/info` directory. This updates the `dir` index file, which serves as the master table of contents for the `info` system.

```bash
postBuild = ''
  if [[ -w $out/share/info ]]
  then
    shopt -s nullglob
    for i in $out/share/info/*.info $out/share/info/*.info.gz; do
      install-info $i $out/share/info/dir
    done
  fi
'';

```

### Setting INFOPATH for Wrapped Binaries

Each generated Emacs binary is wrapped using `wrapProgram` to inject the correct `INFOPATH` environment variable. This path includes the Emacs system info directory, the wrapper's own aggregated info directory, and the combined `infoPath` of all packages.

```bash
wrapProgram $bin \
  --prefix INFOPATH : ${emacs}/share/info:$out/share/info:${infoPath} \
  …

```

The wrapper also installs Twist.nix's own documentation (`emacs-twist.info`) into `$out/share/info` and registers it with `install-info`, ensuring the package manager's manual is available alongside other package documentation.

## Generating the twist.nix Manual

Twist.nix includes a dedicated Flake app for building its own manual. Defined in `doc/flake.nix`, the `generate-info` app invokes Emacs in batch mode to process `doc/emacs-twist.texi` with `makeinfo`, producing the `.info` file.

```nix
apps.generate-info = flake-utils.lib.mkApp {
  name = "generate-info";
  program = builtins.toString (pkgs.runCommand "generate-info" {
    nativeBuildInputs = [ texinfo ];
  } ''
    cd ${self}
    for t in *.texi *.texinfo; do
      makeinfo --no-split "$t" -o "''${t%%.*}.info"
    done
  '');
};

```

Running `nix run .#generate-info` produces `doc/emacs-twist.info`, which the wrapper subsequently installs into the final Emacs environment alongside other package documentation.

## Summary

- **Automatic Detection**: The builder in `pkgs/emacs/build/default.nix` uses `canProduceInfo` to detect `.texi`, `.texinfo`, and `.info` files, conditionally adding an `info` output to the derivation.
- **Compilation**: The `buildInfo` hook runs `makeinfo --no-split` during the build phase to generate `.info` files from Texinfo sources.
- **Installation**: The `installInfo` hook copies compiled `.info` files into `$info/share/info`, making documentation available as a separate Nix store path.
- **Aggregation**: The wrapper in `pkgs/emacs/wrapper.nix` symlinks all `share/info` directories, runs `install-info` to update the `dir` index, and sets `INFOPATH` via `wrapProgram`.
- **Self-Documentation**: The `doc/flake.nix` app generates the Twist.nix manual using `makeinfo`, integrating it into the wrapped environment.

## Frequently Asked Questions

### How does twist.nix detect if a package contains Texinfo sources?

The builder evaluates `canProduceInfo = hasFile (f: match ".+\\.(info|texi(nfo)?)" f != null)` to check for files ending in `.info`, `.texi`, or `.texinfo`. If found, and if `wantExtraOutputs` is enabled, the derivation adds `"info"` to its `outputs` list, triggering the documentation build process.

### Where are the generated .info files stored in the Nix store?

The `installInfo` hook in `pkgs/emacs/build/default.nix` installs files to `$info/share/info` within a separate `info` output. This creates a distinct Nix store path (e.g., `/nix/store/...-emacs-package-info/`) containing only the documentation, which can be referenced independently or browsed directly.

### How does the Emacs wrapper ensure all package manuals are accessible?

The wrapper in `pkgs/emacs/wrapper.nix` aggregates documentation by setting `pathsToLink = [ "/share/info" ]` to symlink all package info directories. It then runs `install-info` in a `postBuild` hook to update the `dir` index, and uses `wrapProgram` to prepend the aggregated paths to the `INFOPATH` environment variable.

### Can I generate the twist.nix manual locally?

Yes. Running `nix run .#generate-info` executes the Flake app defined in `doc/flake.nix`, which processes `doc/emacs-twist.texi` with `makeinfo --no-split` to produce `doc/emacs-twist.info`. This manual is then integrated into the wrapped Emacs environment alongside other package documentation.