Skip to main content

Panbuild

A literate build system for documents.

Panbuild turns a YAML configuration file (Panbuild.yml) plus a Markdown "index" file into a build.ninja file, which the ninja build tool then executes to produce documents (HTML pages, PDF documents, slide decks, ...) with pandoc.

How it works

The pipeline has two phases:

  1. panbuild is run in a directory containing a Panbuild.yml file. It parses the index Markdown into a pandoc AST, rewrites the !rule links it contains into build statements, and emits three files in the current directory:

    • build.ninja — variables, rules, build statements and defaults;
    • .index.native — the index document in pandoc "native" format, with the !rule links rewritten to point at the built outputs in the build directory;
    • .metadata.yml — the variables: section of the configuration, dumped back as YAML so that it can be reused by pandoc defaults files (-d flag).
  2. ninja executes build.ninja. The index itself is compiled to <build directory>/index.html from .index.native.

Requirements

  • Python 3.11 or later, managed with uv
  • ninja
  • the pandoc command-line tool (the Python pandoc package is only used to manipulate the document AST; the generated build commands shell out to the pandoc binary). Depending on your rules you may also need a PDF engine such as lualatex.

Installation

uv sync

Usage

cd example
uv run panbuild   # generate build.ninja
ninja             # build everything (ninja -n for a dry run)

panbuild reads Panbuild.yml from the current working directory; there are no command-line options.

The index file

The index is an ordinary Markdown file. Any link whose text starts with ! is a build target:

[!document](toto.md)
[!document tata](tata.md)
[!copy blob](toto.raw)

[!document tata](tata.md) means "build tata.md with the document rule". Panbuild emits the corresponding ninja statement and rewrites the link, so that the generated index page points at the built output; the !rule token is stripped from the link text (here the text becomes "tata").

Targets are written to the build directory as <prefix>-<source stem>.<rule extension> (for example build/m2dac_reds-tata.pdf); rules without an extension keep the source filename verbatim. Every generated target also gets a default statement, so a bare ninja builds everything.

Configuration

A typical Panbuild.yml:

variables:
  title: Example of a Panbuild.yml file
  date: 2023
  author: Great panbuilder
build:
  index: index.md
  directory: build/
  prefix: m2dac_reds
  rules:
    html:
      extension: html
      command: pandoc $options -s --toc --toc-depth=1 -o $out $in
    document:
      extension: pdf
      command: pandoc $options -s -d .metadata.yml -o $out $in
    copy:
      command: cp $in $out
  header: |
    options = --pdf-engine=lualatex --variable=papersize:a4

    rule rsync
      command = rsync -aP --delete $directory server:public_html/
  footer: |
    build publish: rsync

variables

Arbitrary metadata (title, author, date, theme, ...). Dumped verbatim to .metadata.yml, typically consumed by pandoc defaults files.

build

  • index: path of the index Markdown file.

  • directory: build directory for the generated outputs.

  • prefix: prefix prepended to target names; a leading $name is expanded from the scalar build values.

  • rules: mapping of rule names to ninja rules. command is the shell command (with the usual ninja $in/$out variables), extension is the output file extension.

    Note: an html rule is required, as the generated index page is always built with it.

  • header / footer: raw ninja syntax injected verbatim near the top and at the end of build.ninja. Typically used for extra variables (such as options), extra rules, and phony aggregate targets like build publish:.

All scalar keys under build: are emitted verbatim as ninja variables — including arbitrary keys that panbuild does not use itself — so they can be referenced as $directory, $unknownvar, etc. inside commands. Two caveats: values are formatted with Python str() (YAML flag: true becomes flag=True), and keys under the top-level variables: section are not passed to ninja at all; they end up in .metadata.yml.

Example

The example/ directory contains a complete Panbuild.yml and index file demonstrating the rules above. Note that the referenced source files (toto.md, tata.md, toto.raw) are absent on purpose: panbuild generation succeeds, only ninja complains about them.

License

GPL-3.0-or-later, see COPYING.

Release files for panbuild 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for panbuild 0.2.1
File Size Uploaded
panbuild-0.2.1.tar.gz 217.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for panbuild 0.2.1
File Interpreter ABI Platform
panbuild-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 234.9 kB

Release files / panbuild-0.2.1.tar.gz

Download URL panbuild-0.2.1.tar.gz
Size 217.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ac0043ee2ba74170d61eeeaadf8ab506377803859d5c9645129f98e54438d5ab
BLAKE2b-256 checksum
How to use checksums
7356d40182dc158afb491f29946e9586118369a02beb0e816886f1356c5ccd77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / panbuild-0.2.1-py3-none-any.whl

Download URL panbuild-0.2.1-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9fef65e229d1286960d0da45e80076922402dd99758e8e7395e75e10f4059c94
BLAKE2b-256 checksum
How to use checksums
c8f7c9665e7bbc888a609254da6aa223d9fe330392bfe7241ae121ce6694da04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

This release

0.2.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page