Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

bbtidy

Experimental formatter, linter, and lexer for BitBake metadata.

Description

bbtidy is an experimental tool for formatting and inspecting BitBake recipes and configuration files. It provides a lexer, a conservative formatter for top-level metadata assignments, and file-local linting suitable for CI.

Features

  • Tokenization: efficiently breaks down BitBake files into tokens (Identifiers, Strings, Keywords, etc.) using logos.
  • Span Reporting: Reports the exact location (line and column) of each token.
  • Lossless concrete syntax tree: Represents every top-level byte as a source-backed node with stable ranges while retaining comments, blank lines, unknown syntax, and embedded bodies verbatim.
  • Modern metadata syntax: Recognizes all eight assignment operators, colon overrides, key expansion, variable flags, multiline quoted values, and current BitBake directives.
  • Safe formatting boundaries: Formats complete, single-line top-level assignments while preserving shell functions, Python functions, continued statements, and unsupported syntax.
  • Fail-safe writes: Refuses to rewrite structurally incomplete input and replaces successfully formatted files atomically.
  • Automation-friendly CLI: Provides explicit format, check, lint, and lex commands, standard-input support, unified diffs, and documented exit codes.
  • Layer-wide operation: Recursively discovers supported BitBake files in deterministic path order.
  • Initial linting: Reports stable rule IDs, severity, line, and column for a focused set of reproducibility and metadata hygiene checks.

Installation

Install the native executable from PyPI with pip:

python -m pip install bbtidy
bbtidy --version

Because bbtidy is a command-line application, pipx install bbtidy is also a good choice for an isolated global installation. The Python distribution does not expose an importable module; it places the bbtidy executable in the environment's scripts directory, following the same binary-wheel model used by Rust-based Python tools such as Ruff.

Release wheels target:

  • Linux x86-64 and ARM64 using glibc (manylinux_2_17)
  • Linux x86-64 and ARM64 using musl (musllinux_1_2)
  • macOS on Intel and Apple silicon
  • Windows on x86-64

The wheels are independent of the Python interpreter implementation and support Python 3.8 or newer. Installing a matching wheel does not require Rust. A source distribution is also published as a fallback; building it requires Rust 1.85 or newer.

Usage

To inspect the tokens in a recipe:

bbtidy lex sample.bb

To print a formatted file without modifying it:

bbtidy format messy.bb

Standard input is accepted as -:

printf 'SUMMARY="Example"\n' | bbtidy format -

To inspect changes across a file or directory:

bbtidy format --diff recipes-example/

To check formatting in CI, then explicitly rewrite files when desired:

bbtidy check recipes-example/
bbtidy format --write recipes-example/

To lint a file, recipe directory, or layer:

bbtidy lint recipes-example/

Findings use an editor- and CI-friendly format:

recipes-example/example.bb:12:11: warning[BBT004]: SRCREV uses ${AUTOREV}; pin a source revision for reproducible builds

Formatting is intentionally limited while BitBake syntax support is being developed. Embedded shell and Python code is kept byte-for-byte unchanged.

Directory inputs are traversed recursively. .bb, .bbappend, .bbclass, and .inc metadata files are discovered automatically, except beneath recipe payload directories named files. .conf files are discovered inside a layer's conf directory, identified by its layer.conf. An explicit file input is always processed. Paths are sorted and deduplicated before processing. Standard input must be the only input, and --write cannot be used with it.

format writes formatted source to standard output and requires one input unless --diff or --write is selected. format --diff, lint, and lex can process multiple inputs without changing them. Before format --write changes any files, every input is read and formatted successfully; each changed file is then replaced atomically.

Exit codes

  • 0: the command completed successfully.
  • 1: check found formatting differences or lint found diagnostics.
  • 2: command usage, input/output, lexing, formatting, or lint analysis failed.

Operational diagnostics are written to standard error. Lexer error tokens remain part of the token stream on standard output and cause exit code 2. format --diff returns 0 when it successfully reports differences; use check when differences should fail a CI job.

Initial lint rules

Rule Name Detects
BBT001 trailing-whitespace Spaces or tabs at the end of a line
BBT002 final-newline A non-empty file without a final newline
BBT003 summary-length A static, literal SUMMARY longer than 80 characters
BBT004 autorev SRCREV variants that use ${AUTOREV}
BBT005 duplicate-inherit A static class inherited more than once in one file

All initial rules are warnings. Diagnostics are sorted by source location and exposed through the public lint, lint_rules, LintDiagnostic, LintRule, and LintSeverity Rust APIs. Structurally incomplete input is reported as an operational error instead of producing potentially misleading findings.

The semantic rules are intentionally conservative: they inspect top-level metadata, skip embedded shell and Python bodies, and avoid evaluating dynamic values or class names. Cross-file inheritance, configuration, suppression, and release-specific analysis remain future work.

Lossless syntax tree

The Rust library exposes parse as the shared structural front end for formatting and linting. Its SyntaxTree borrows the original source and divides it into ordered, contiguous SyntaxNode ranges. Concatenating node.text() for every node always reproduces the input byte-for-byte.

Recognized nodes provide structured data and absolute source ranges for assignments, directives, shell and Python functions, and top-level Python definitions. Blank lines, comments, and unsupported top-level constructs remain explicit nodes. Function and Python-definition bodies are deliberately opaque: bbtidy finds their safe boundaries but does not interpret or rewrite the embedded language.

use bbtidy::{SyntaxKind, format_syntax, lint_syntax, parse};

let source = "SUMMARY=\"Example\"\n";
let tree = parse(source)?;

if let SyntaxKind::Assignment(assignment) = tree.nodes()[0].kind() {
    assert_eq!(assignment.name(), "SUMMARY");
}

let formatted = format_syntax(&tree);
let diagnostics = lint_syntax(&tree);
# Ok::<(), bbtidy::SyntaxError>(())

format and lint are convenience entry points that parse source and then delegate to format_syntax and lint_syntax. Callers performing multiple operations can parse once and reuse the same tree.

Supported syntax

The 0.1.0-alpha.2 lexer recognizes:

  • Assignments using =, :=, ?=, ??=, +=, =+, .= and =.
  • Literal and dynamic overrides such as RDEPENDS:${PN}:class-native
  • Key expansion such as A${B} and variable flags such as do_fetch[network]
  • include, include_all, require, inherit, inherit_defer, addfragments, addpylib, addhandler, addtask, deltask, EXPORT_FUNCTIONS, export and unset
  • Single- and double-quoted values, including multiline values

Legacy underscore overrides remain lexically accepted as identifier and variable-reference components. They are not interpreted as override operations. The formatter remains deliberately conservative and only changes complete, single-line top-level assignments.

Development

Run the test suite with:

cargo test

Build and verify the Python wheel and source distribution with:

maturin build --release --locked --sdist --out dist
python scripts/smoke_test_package.py --kind wheel dist
python scripts/smoke_test_package.py --kind sdist dist

pip install . uses the same PEP 517 configuration for a local source build. The Cargo version is the release source of truth; maturin converts prereleases to PEP 440 automatically, for example 0.1.0-alpha.2 becomes 0.1.0a2.

The integration suite includes a representative fixture layer containing .bb, .bbappend, .bbclass, .inc, and .conf files. It verifies golden output, idempotence, byte-for-byte preservation of embedded code, structured errors, lint rule behavior, CLI modes and exit codes, deterministic directory handling, and the no-write guarantee for malformed input.

Upstream compatibility corpus

The extended compatibility check uses commit-pinned snapshots of OpenEmbedded-Core and the meta-oe, meta-python, and meta-networking layers. The revisions and minimum corpus sizes are recorded in tests/upstream-corpus.json; upstream repositories are downloaded into a temporary workspace and are not vendored in this repository.

On a supported Linux build host with the standard Yocto host packages installed, run the complete check with:

cargo build --release --locked
python scripts/check_upstream_corpus.py

The harness scans more than 3,300 real metadata files, formats a disposable copy, verifies idempotence, exercises lint analysis, checks that embedded functions and Python blocks remain byte-for-byte unchanged, and confirms that recipe payload files were not touched. It then initializes a disposable Poky build and parses core-image-minimal with all four formatted layers.

Existing pinned checkouts can be reused, and the BitBake parse can be omitted on a non-Linux development machine:

python scripts/check_upstream_corpus.py \
    --source-root /path/containing/poky-and-meta-openembedded \
    --skip-bitbake

The upstream compatibility workflow runs for relevant pull requests and pushes to main, as well as on a weekly schedule. A compatibility failure should be reduced to a focused local regression test before the pinned snapshot is advanced.

To validate the formatted corpus with a real BitBake parser, use a disposable build whose environment has already been initialized:

bbtidy_repository=/absolute/path/to/bbtidy
bitbake-layers add-layer "$bbtidy_repository/tests/fixtures/corpus/expected"
BBTIDY_BITBAKE_BUILD_DIR="$BUILDDIR" \
    "$bbtidy_repository/scripts/check-bitbake-parser.sh"

The script is opt-in because BitBake is not a project dependency. It invokes bitbake --parse-only example, ensuring the corpus recipe is available and parseable, and does not edit the selected build configuration.

Releasing to PyPI

The Python package workflow builds and installs a wheel on every pull request and push to main. The release workflow can be run manually to inspect all platform artifacts without publishing.

To publish a release:

  1. Update the version in Cargo.toml and finalize the changelog.
  2. Create a tag that exactly matches the Cargo version, such as v0.1.0-alpha.3.
  3. Push the tag. The release workflow builds and smoke-tests all wheels and the source distribution, then publishes them through PyPI Trusted Publishing.

Before the first automated publication, configure the bbtidy project on PyPI with GitHub owner jorisguex, repository bbtidy, workflow publish-pypi.yml, and environment pypi. No repository API token is needed. The version guard rejects mismatched tags before any release artifacts are built.

License

This project is licensed under the MIT License.

Metadata

Release files for bbtidy 0.1.0a2

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

Source distribution (sdist)

Source distribution for bbtidy 0.1.0a2
File Size Uploaded
bbtidy-0.1.0a2.tar.gz 49.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for bbtidy 0.1.0a2
File
bbtidy-0.1.0a2-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
bbtidy-0.1.0a2-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
bbtidy-0.1.0a2-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
bbtidy-0.1.0a2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
bbtidy-0.1.0a2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
bbtidy-0.1.0a2-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
bbtidy-0.1.0a2-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 3.4 MB

Release files / bbtidy-0.1.0a2.tar.gz

Download URL bbtidy-0.1.0a2.tar.gz
Size 49.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4320e708f282eb3ce5688053f73576f03ce2f90a8692ea5095e5e7e34be9f307
BLAKE2b-256 checksum
How to use checksums
662bddd55d4a026dd6b75789726fc1293cea26c8f0cf4f269624cf0cc9854fd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-win_amd64.whl

Download URL bbtidy-0.1.0a2-py3-none-win_amd64.whl
Size 380.0 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
e49babaa1df5f3ca515a9772a37203991f5fc3d9b602b03bf397d18523c227e6
BLAKE2b-256 checksum
How to use checksums
9727cc8e0211567ee8713f9cebdea2d3d012197309059ec970484a3558eda669
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-musllinux_1_2_x86_64.whl

Download URL bbtidy-0.1.0a2-py3-none-musllinux_1_2_x86_64.whl
Size 530.6 kB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
e78cb829633da10c6ecdff152f04c003e08033dde6cdcf2bc0f784541e50a726
BLAKE2b-256 checksum
How to use checksums
d6b92fe9b91b9fcf782a8a4a6b053f5fa8f69798deb3adb6b108e0da33935c2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-musllinux_1_2_aarch64.whl

Download URL bbtidy-0.1.0a2-py3-none-musllinux_1_2_aarch64.whl
Size 498.2 kB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
0061ba41326b8c3f0886b51ced980fcce1007b0b0fdf842292708ceecc30750d
BLAKE2b-256 checksum
How to use checksums
0da658715a12d39424743f2c93c382d4958c78101c49c61ad34109ae41c80f8d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL bbtidy-0.1.0a2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 484.4 kB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
0fcaa16cf3067455d2fbd3bab740a0e67268d86ebd04e876d4ba14454fa37cde
BLAKE2b-256 checksum
How to use checksums
b9882620c6e618243f88d4cfa36e7491ca77f5af860a93f24241183e4feb2898
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL bbtidy-0.1.0a2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 453.7 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
03ea8b18a3a8ebb6180a9ddb8354ebda6640ec65ab83bd45f338fcc1a2af4a37
BLAKE2b-256 checksum
How to use checksums
1cba04f0fc13f8cda5a1f4ec56e79f5365cd5accc900724a5c593133289f0e2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-macosx_11_0_arm64.whl

Download URL bbtidy-0.1.0a2-py3-none-macosx_11_0_arm64.whl
Size 440.7 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
475c291f11f36e60b615fb6b99b498b52f6b51a7d563e92ce78a8e04f8365ac9
BLAKE2b-256 checksum
How to use checksums
8bdb9be8643598b46e75e3b6b24dc69d93c477a5871f529f396eaacfabab0b52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / bbtidy-0.1.0a2-py3-none-macosx_10_12_x86_64.whl

Download URL bbtidy-0.1.0a2-py3-none-macosx_10_12_x86_64.whl
Size 519.5 kB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
a45c86ddeb30e9fd471c0529f045d488750e358c77205abb9e2a6c6e8ae89dba
BLAKE2b-256 checksum
How to use checksums
2d81ab018361fa6c92fe6fcd65df0c40ec22a931dd2270f52603ba77985e2b00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0a2 This release

8 release files

0.0.0

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