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 (all three can be gitignored):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
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$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: — 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.0
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.3.0.tar.gz | 316.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| panbuild-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 334.3 kB
Release files / panbuild-0.3.0.tar.gz
| Download URL | panbuild-0.3.0.tar.gz |
|---|---|
| Size | 316.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
08a1b2a4970b469809ea2e9f27b1471cf674e45cbcec18833016ae588425caee
|
|
BLAKE2b-256 checksum How to use checksums |
504138a9cf7cb7a304ec73e4a399bc2443ae56984ff191571f5614160bb707b7
|
| 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.0-py3-none-any.whl
| Download URL | panbuild-0.3.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6bcd55085ce31669eb7a274a236060e0e8d7496335a7d9f8e72c5fd995c97494
|
|
BLAKE2b-256 checksum How to use checksums |
0f8226d18274777f0728a675a4b2c069ad82e6252e91e5570554a983f774cdb1
|
| 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}
|