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 (all three can be gitignored):

    • 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

With uv:

uv tool install panbuild

Usage

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

panbuild reads Panbuild.yml from the current working directory. A few options are available: -c/--config to select another configuration file, -f/--index to override the index file, and -v/--verbose to list the discovered targets.

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/myprefix-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: myprefix
  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: — except header and footer — 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.3.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.3.1
File Size Uploaded
panbuild-0.3.1.tar.gz 23.5 kB Details

Built distribution (wheel)

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

Total release size: 41.1 kB

Release files / panbuild-0.3.1.tar.gz

Download URL panbuild-0.3.1.tar.gz
Size 23.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b2c3ed8bff86f56376ae500b6013aa60f3fa424100e0ff2ef2d033deea5e932c
BLAKE2b-256 checksum
How to use checksums
cb08646fa50d065dd151ef579be8ff56437afd4fc08401ac3dc36ebd6480bc8d
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.3.1-py3-none-any.whl

Download URL panbuild-0.3.1-py3-none-any.whl
Size 17.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
19688ecc75550e76f1835243118bcffb7ac72459af95406cd9092eef3bb0e676
BLAKE2b-256 checksum
How to use checksums
0d9380fc71a285249a37f755461e03ce8999dfcba059e139082669ce321712d5
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

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.1

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