# How twist.nix Handles Native Compilation for Emacs 29+: A Complete Guide

> Discover how twist.nix enables native compilation for Emacs 29+. This guide explains detecting capabilities, running batch compilation, and loading pre-compiled .eln files for faster Emacs builds.

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

---

**twist.nix enables native compilation for Emacs 29+ by detecting the underlying Emacs binary's capabilities, conditionally running batch native compilation during the build phase, and configuring runtime environment variables to load pre-compiled `.eln` files.**

twist.nix is a Nix-based package manager for Emacs that automates the entire build pipeline. For Emacs 29 and later, it supports the **native compilation** (native-comp) feature, which translates Emacs Lisp into native machine code for significant performance improvements. The implementation spans feature detection, build-time compilation, and runtime configuration.

## How twist.nix Detects Native Compilation Support

Before running any compilation steps, twist.nix determines whether the target Emacs binary actually supports native compilation.

### The nativeComp Flag in wrapper.nix

In `pkgs/emacs/wrapper.nix` (line 22), twist.nix computes a Boolean flag `nativeComp` by checking the Emacs package attributes:

```nix
nativeComp = emacs.withNativeCompilation or emacs.nativeComp or false;

```

This flag propagates through the entire build pipeline. If `nativeComp` evaluates to `false`, twist.nix skips all native compilation steps, allowing the same configuration to work with older Emacs versions or custom builds without native-comp support.

## Build-Time Native Compilation in twist.nix

When `nativeComp` is true and the user opts into ahead-of-time compilation, twist.nix runs native compilation as a build phase after standard byte-compilation.

### The buildAndInstallNativeLisp Phase

In `pkgs/emacs/build/default.nix` (lines 71-79), the derivation includes a `buildAndInstallNativeLisp` script that executes after byte-compilation:

```bash
emacs --batch -L $lispDir -l ./comp-native.el \
  --eval "(push \"$nativeLispDir/\" native-comp-eln-load-path)" \
  --eval "(setq native-compile-target-directory \"$nativeLispDir/\")" \
  -f run-native-compile-sync $lispDir

```

This script:
1. Loads `comp-native.el` to provide the synchronous compilation wrapper
2. Configures `native-comp-eln-load-path` to point to the output directory
3. Invokes `run-native-compile-sync` to compile every `.el` file to `.eln` format
4. Places results in `$out/share/emacs/native-lisp`

### The comp-native.el Helper

The file `pkgs/emacs/build/comp-native.el` defines `run-native-compile-sync`, a thin wrapper around Emacs's built-in `native-compile-async`. It blocks until the async job queue empties, ensuring Nix's build sandbox waits for compilation completion before proceeding to the install phase.

## Runtime Configuration for Native Compilation

After building the packages, twist.nix configures the runtime environment so Emacs can locate and load the native-compiled libraries.

### Environment Variables and Load Paths

In `pkgs/emacs/wrapper.nix` (lines 162-171), the wrapper script sets critical environment variables:

```bash
--prefix EMACSNATIVELOADPATH : $nativeLisp:$nativeLoadPath

```

This prepends the `native-lisp` directory to `EMACSNATIVELOADPATH`, which Emacs 29+ checks when resolving native-compiled units. The wrapper also ensures `native-comp-eln-load-path` includes these directories during early initialization.

Additionally, the wrapper runs `batch-native-compile` on the generated `site-start.el` file. This guarantees that any Emacs Lisp generated during the Nix build process itself gets native-compiled before the user starts Emacs.

### Ahead-of-Time vs Lazy Compilation

twist.nix supports two modes controlled by the `nativeCompileAhead` flag (defined in `pkgs/emacs/default.nix`):

- **Ahead-of-time (AOT)**: When `nativeCompileAhead = true`, the build phase runs `run-native-compile-sync` on all `.el` files. This increases build time but eliminates compilation pauses when the user loads packages.
- **Lazy (JIT)**: When `false`, twist.nix skips the build-time native compilation. Emacs compiles packages on-demand during the first load, trading faster builds for potential initial lag.

The derivation checks both flags before enabling native compilation:

```nix
doNativeComp = nativeComp && nativeCompileAhead;

```

## Practical Configuration Examples

### Enabling Native Compilation in a Flake

To enable native compilation for your Emacs 29+ installation, set the `nativeComp` and `nativeCompileAhead` attributes in your flake:

```nix
{
  inputs.twist-nix.url = "github:emacs-twist/twist.nix";

  outputs = { self, nixpkgs, twist-nix }: {
    packages.x86_64-linux.emacsWithNative = twist-nix.packages.x86_64-linux.emacs // {
      nativeComp = true;                # Enable if Emacs binary supports native-comp

      nativeCompileAhead = true;        # Compile everything at build time

    };
  };
}

```

### Building Custom Packages with Native Comp

When defining custom packages that should benefit from native compilation, inherit the `nativeComp` flag from the surrounding Emacs package set:

```nix
{ pkgs, twist-nix }:

twist-nix.buildEmacsPackage {
  pname   = "my-awesome-mode";
  version = "1.0";

  src = pkgs.fetchFromGitHub {
    owner = "myself";
    repo  = "my-awesome-mode";
    rev   = "v1.0";
    sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
  };

  # Inherit nativeComp flag from the surrounding emacs package

  inherit (twist-nix.emacs) nativeComp;
}

```

When `twist-nix.emacs` has `nativeComp = true`, this package automatically undergoes native compilation during its build.

## Summary

- **Feature detection**: twist.nix checks `emacs.withNativeCompilation` or `emacs.nativeComp` in `pkgs/emacs/wrapper.nix` to determine if native compilation is available.
- **Build process**: When enabled, `pkgs/emacs/build/default.nix` runs `run-native-compile-sync` from `comp-native.el` to compile `.el` files to `.eln` format during the build phase.
- **Runtime support**: The wrapper in `pkgs/emacs/wrapper.nix` sets `EMACSNATIVELOADPATH` and `native-comp-eln-load-path` so Emacs 29+ can locate pre-compiled libraries.
- **Configuration**: Users control the feature via `nativeComp` (capability detection) and `nativeCompileAhead` (build-time vs. lazy compilation) flags.

## Frequently Asked Questions

### What Emacs versions support native compilation in twist.nix?

Native compilation requires Emacs 29 or later, which includes the `native-compile` feature. twist.nix automatically detects support by checking the `withNativeCompilation` or `nativeComp` attributes of the Emacs package. If you use Emacs 28 or earlier, twist.nix skips all native compilation steps and falls back to traditional byte-compilation only.

### How do I check if a package was native-compiled?

After building your Emacs configuration, inspect the `native-lisp` directory in the Nix store path. Native-compiled files have the `.eln` extension. For example, run `tree result/share/emacs/native-lisp` to see the directory structure. If the directory contains `.eln` files corresponding to your packages, native compilation succeeded. If the directory is empty or missing, check that `nativeComp` and `nativeCompileAhead` are set to `true` in your configuration.

### Can I disable native compilation for specific packages?

Yes. While twist.nix applies the `nativeComp` flag globally to the Emacs distribution, you can override it per-package when calling `buildEmacsPackage`. Set `nativeComp = false;` in the package derivation to skip native compilation for that specific package while keeping it enabled for others. This is useful for packages with known compatibility issues with the native compiler or when debugging compilation errors.

### What is the difference between nativeComp and nativeCompileAhead?

`nativeComp` is a Boolean flag that indicates whether the underlying Emacs binary supports native compilation and whether the user wants to enable the feature at all. When `false`, twist.nix performs no native compilation steps. `nativeCompileAhead` controls *when* compilation happens: when `true`, twist.nix runs `run-native-compile-sync` during the Nix build to compile all `.el` files to `.eln` format ahead of time; when `false`, Emacs compiles packages lazily at runtime on first load. You need `nativeComp = true` for `nativeCompileAhead` to have any effect.