Asset Pantry
One mechanism for every piece of bundled content
Every piece of bundled content in OctoMY — robot firmware templates, board definitions, icons, help pages — ships inside the application binary through one mechanism: the asset pantry. A library declares a bundle, the build embeds it, and any consumer can read it on any machine, with no source checkout and no installer step.
Why it exists
Bundled content used to reach a binary through five different mechanisms, and one of them was broken by construction: it compiled an absolute path from the build machine into the binary. The result compiled robot presets perfectly on the machine that built it, and on no other machine. The pantry replaces all five mechanisms with one, and makes that failure structurally impossible: a release binary carries the content itself, never a path to where it once lived.
Declaring a bundle
A library declares its bundle in its lib.qbs. There is no file list to maintain — the build enumerates the directory on every build, so a file added to git is in the shipped bundle automatically:
pantry.bundles: [{
name: "fixo-templates",
sourceDir: "fixo",
filters: ["**/*.hpp", "**/*.h"],
mount: "/fixo"
}]
name— the bundle's identity, unique across the application.sourceDir— the directory holding the content, relative to the library.filters— glob patterns over the relative path.**/matches any directory chain,*matches within one path segment. Defaults to everything.mount— the resource prefix the content appears under. Defaults to/+ the bundle name. A mount of/publishes at the resource root.
The build generates a .qrc and a manifest — the bundle's name, mount, and every member with its size and SHA-256 hash. Both are compiled into the binary. Content that a build step produces (rather than a git directory) can also be bundled: the producing tool hands its own output manifest to the generator, so a stale file left in a build directory can never ride along.
The three layers
At runtime, a bundle resolves each member through three layers, in order:
- Live source — the git working tree. Debug builds only: the debug manifest records where the sources live, so a developer can edit a template and see the change without rebuilding. A release manifest never carries this path.
- Extracted cache — a real directory the bundle has been materialized into.
- Resource — the copy compiled into the binary. Always present, which is why a cleared cache is a recoverable event rather than a broken installation.
Two command-line flags control the layers (there is deliberately no environment variable):
--pantry-layers=cache,resourcedisables the live layer — useful for checking that the shipped path works on a developer machine, where the live layer would otherwise always win.--pantry-source=/path/to/checkoutpoints a release binary at a source checkout for one run. Nothing is read from the checkout unless you ask by name.
Reading a bundle
A consumer asks for the shape it needs; the bundle does not change:
pantry::Bundle bundle = pantry::Bundle::open("fixo-templates");
// Bytes, for ordinary Qt code:
QByteArray types = bundle.file("/fixo/Types.hpp");
// A real directory, for an external process:
QString dir = bundle.materialize(); // dir + "/fixo/Types.hpp" exists
// Any code that takes a path string:
QFile f("octomy-pantry:/fixo/Types.hpp");
In-process Clang gets a fourth shape: the compiler infrastructure mounts bundles into its virtual filesystem, so generated code can #include "fixo/Types.hpp" with no file on disk at all.
A bundle can also carry generated members — content produced at runtime by a callback, indistinguishable from static members in every shape above. The firmware compiler uses this to place a generated config.h beside the static template headers.
Diagnostics in the field
Four OPAL tasks answer bundled-content questions on a running node:
| Task | Answers |
|---|---|
pantry.list |
Which bundles this binary carries |
pantry.info |
Mount, member count, total size, and whether a live source tree is available |
pantry.verify |
Does every member resolve, and does each hash match the build record |
pantry.extract |
Materialize one bundle and report where |
pantry.verify always reports the denominator — how many members were checked — so an empty bundle reads as empty, never as healthy.
Guarantees the build enforces
- A release binary contains no build-machine path: no qbs define may bake a source directory, and the check fails the build.
- Exactly one place in the tree constructs the compiler's in-memory filesystem.
- Every declared bundle's generated resource is registered by the owning library, and every
:/path referenced in code is provided by some bundle or resource — both checked on every build.