Contents

Composition of Nix Flakes

Nix flakes allow to include system dependencies from software that are not published on the nix packages repository but that are themselves flakes. This post will explain how to achieve this.

Context

We would like to create a document using the new markup language named typst. Typst presents itself as a markdown like language that produces pdf; moreover, typst promise to be as powerful as latex.

At the time of writing, typst is not yet published on nix package repository. Fortunately, typst supports nix flake (typst’s source code contains a flake.nix file in its root). Consequently, building typst is as simple as cloning the repository and running nix build .# in root of the repository.

Building a project that depends on typst

Starting Point

A good starting point for building a new flake is to use the below flake.nix file. This file “builds” the hello package and exposes a development shell with deno accessible. The sources are accessible here.

{
  description = "Good basic flake template";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
     flake-utils.lib.eachDefaultSystem (system:
       let
         pkgs = nixpkgs.legacyPackages.${system};
       in
       {
         packages = {
          default = pkgs.hello;
         };
         devShells.default = pkgs.mkShell {
            buildInputs = with pkgs; [deno];
         };
       }
     );
}

Adding Typst to the Inputs

The first step to use a third-party flake is to add an input. The input urls format specification is explained in the “Flake Schema” section of the flakes documentation. In the typst case, the sources are hosted on github at https://github.com/typst/typst which will translate in the github:typst/typst url.

{
  description = "Basic flake with typst as input";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    typst-pkg.url = "github:typst/typst";
  };

  outputs = { self, nixpkgs, flake-utils, typst-pkg }:
     flake-utils.lib.eachDefaultSystem (system:
       let
         pkgs = nixpkgs.legacyPackages.${system};
       in
       {
         packages = {
          default = pkgs.hello;
         };
         devShells.default = pkgs.mkShell {
            buildInputs = with pkgs; [deno];
         };
       }
     );
}

Using Typst In Development Shell

The second step to use a third party flake is to understand how to use the inputed flake. Generally, the location of the “program” exposed in a flake is${input}.packages.${system}.default. In our example above, the location is typst-pkg.packages.${system}.default

If it is required to build another target then the default (which is the one used by nix build .#), the location ${input}.packages.${system}.${target} is the one to use.

Finally, using typst in the development shell can be done using the below flake.nix.

{
  description = "Using typst as 3rd party flake in devshell";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    typst-pkg.url = "github:typst/typst";
  };

  outputs = { self, nixpkgs, flake-utils, typst-pkg }:
     flake-utils.lib.eachDefaultSystem (system:
       let
         pkgs = nixpkgs.legacyPackages.${system};
         typst = typst-pkg.packages.${system}.default;
       in
       {
         packages = {
          default = pkgs.hello;
         };
         devShells.default = pkgs.mkShell {
            buildInputs = with pkgs; [deno typst];
         };
       }
     );
}

Bonus: Building a Document With Typts

It would be a shame not to add a script to build the typst document; let’s add it.

From typst’s command line help, it seems that typst compile main.typ main.pdf will compile the typst source file main.typ in a pdf located at main.pdf.

Wrapping typst compile command in a derivation can be done by the mkDerivation function as:

pkgs.stdenv.mkDerivation {
    name = "document";
    src = ./.;
    buildInputs = [typst];
    buildPhase = "${pkgs.typst}/bin/typst compile main.typ main.pdf";
    installPhase =  ''
        mkdir $out
        mv main.pdf $out
    '';
}

The final typst/nix flake project can be seen at https://gitlab.com/all-dressed-programming/composition-of-nix-flake with the flake.nix displayed here.

{
  description = "Simple typst application";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    typst-pkg.url = "github:typst/typst";
  };

  outputs = { self, nixpkgs, flake-utils, typst-pkg }:
     flake-utils.lib.eachDefaultSystem (system:
       let
         pkgs = nixpkgs.legacyPackages.${system};
         typst = typst-pkg.packages.${system}.default;
       in
       {
         packages = {
          default = pkgs.stdenv.mkDerivation {
            name = "document";
            src = ./.;
            buildInputs = [typst];
            buildPhase = "${pkgs.typst}/bin/typst compile main.typ main.pdf";
            installPhase =  ''
            mkdir $out
            mv main.pdf $out
            '';
          };
         };
         devShells.default = pkgs.mkShell {
            buildInputs = with pkgs; [deno typst];
         };
       }
     );
}