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:
-
panbuildis run in a directory containing aPanbuild.ymlfile. It parses the index Markdown into a pandoc AST, rewrites the!rulelinks 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!rulelinks rewritten to point at the built outputs in the build directory;.metadata.yml— thevariables:section of the configuration, dumped back as YAML so that it can be reused by pandoc defaults files (-dflag).
-
ninjaexecutesbuild.ninja. The index itself is compiled to<build directory>/index.htmlfrom.index.native.
Requirements
- Python 3.11 or later, managed with uv
ninja- the
pandoccommand-line tool (the Pythonpandocpackage 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 aslualatex.
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$nameis expanded from the scalarbuildvalues. -
rules: mapping of rule names to ninja rules.commandis the shell command (with the usual ninja$in/$outvariables),extensionis the output file extension.Note: an
htmlrule 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 ofbuild.ninja. Typically used for extra variables (such asoptions), extra rules, and phony aggregate targets likebuild 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)
| File | Size | Uploaded | |
|---|---|---|---|
| panbuild-0.2.1.tar.gz | 217.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|