Repository navigation
7. Parent Integration
dbl-buildroot is designed to be consumed as a git submodule by one or more downstream parent projects (also called "superprojects"). A superproject adds project-specific packages, configuration overlays, and CI on top of the base build system.
parent-repo/ (superproject)
├── modules/dbl-buildroot/ (submodule)
│ ├── lib.nix mkProject entry point
│ ├── inputs.nix canonical input URLs for target revisions
│ ├── nix/build.nix hermetic build derivation
│ └── support/parent/ integration scripts
├── overlay/ (BR2_EXTERNAL tree)
│ ├── external.desc
│ ├── Config.in
│ └── my-project.fragment (defconfig overlay)
├── flake.nix
├── Makefile
└── .pre-commit-config.yaml
The submodule provides the following:
-
lib.mkProject: Builds packages and devShells from a single call. -
inputs.nix: Revision-pinned input URLs, fetched bylib.nixto be inherited by parent flakes. -
support/parent.mk: Makefile fragment providingmake build,make develop,make update-dbl-buildroot,make check-dbl-buildroot, andmake nix-overlays. -
support/parent/update.sh: One-shot bumper for the submodule and all SHA pins. -
support/parent/check-pinned-shas.sh: Drift gate script. -
.github/workflows/check-reusable.yml: Reusable submodule-check workflow. -
.github/actions/setup-nix: Shared CI Nix-environment setup. -
.github/actions/lockfile: Compositebuildroot.lock/toolchain.lockdrift gate action. -
.github/actions/build: Composite build action. -
.pre-commit-hooks.yaml: Exported hooks for downstream repositories.
git init my-project && cd my-project
git submodule add git@github.com:deadbandlabs/dbl-buildroot.git modules/dbl-buildroot
modules/dbl-buildroot/support/parent/init.sh --name=my-project --board=myd-yf135This generates everything from the template in support/parent/template/:
| Generated file | Purpose |
|---|---|
flake.nix |
Nix flake calling mkProject with overlay and fragment |
Makefile |
Includes support/parent.mk; forwards make to submodule, provides make build for Nix |
overlay/ |
BR2_EXTERNAL tree (external.desc, Config.in, fragment, package/Config.in) |
.pre-commit-config.yaml |
Hook IDs from submodule, SHA pinned |
.github/workflows/build.yml |
Calls check-reusable.yml, then setup-nix plus the lockfile and build composite actions at pinned SHA |
.envrc |
direnv integration |
.gitignore |
Standard patterns |
REUSE.toml |
License annotations |
After init, review the generated files, stage, and verify:
git add -A
make check-dbl-buildroot
make buildThe first make command verifies that drift gates pass. The second performs a hermetic Nix build. Run init.sh --help for all options (copyright, board, etc.).
make update-dbl-buildroot
make update-dbl-buildroot REF=v1.2.3The first command advances the submodule to origin/main. The second advances to a specific tag.
This runs support/parent/update.sh, which:
- Fetches and checks out the target ref in the submodule.
- Propagates the new SHA to
.pre-commit-config.yamland all workflowuses:refs. - Runs consistency checks.
Stage everything it reports at the end:
git add modules/dbl-buildroot .pre-commit-config.yaml .github/workflows/One pre-commit hook catches drift of submodule references (SHAs) before it reaches CI:
| Hook | Script | What it checks |
|---|---|---|
dbl-buildroot-pinned-shas |
support/parent/check-pinned-shas.sh |
Submodule SHA vs. rev: in pre-commit config and uses:@<sha> in workflows |
This is a no-op when running inside the submodule itself (repositories without a modules/dbl-buildroot/ directory).
Bumping the buildroot, nixpkgs, or buildroot-nix pins themselves is done by editing modules/dbl-buildroot/inputs.nix upstream and bumping the submodule pointer in the parent. There is no parent-side input to keep in sync.
| Parameter | Required | Default | Description |
|---|---|---|---|
name |
yes | - | Project name (derivation name) |
board |
yes | - | Board name; drives defconfig and flashLayout defaults |
nixpkgs |
no | self-fetched from inputs.nix
|
Override nixpkgs source |
buildroot |
no | self-fetched from inputs.nix
|
Override Buildroot source |
buildroot-nix |
no | self-fetched from inputs.nix
|
Override buildroot.nix |
defconfig |
no | <board-with-underscores>_defconfig |
Override defconfig name |
flashLayout |
no | board/<board>/flashlayout.tsv |
Override flash layout path |
extraExternalSrcs |
no | [] |
Additional BR2_EXTERNAL trees |
extraRootfsOverlays |
no | ({ sdk, pkgs }: [ ]) |
Function returning extra rootfs overlay directories for Nix-built image content (see below) |
configFragment |
no | null |
Defconfig fragment to merge over base |
configFragmentDebug |
no | null |
Additional defconfig fragment merged on debug builds only |
lockfile |
no | submodule's buildroot.lock
|
Project-local package lock (see below) |
toolchainName |
no | myd-yf135-toolchain |
Toolchain SDK derivation name; keep the default to substitute the CI-cached SDK |
toolchainDefconfig |
no | submodule's configs/myd_yf135_toolchain_defconfig
|
Toolchain-only defconfig for the stage-1 SDK build |
toolchainLockfile |
no | submodule's toolchain.lock
|
Toolchain SDK source lock |
toolchainFragment |
no | submodule's configs/myd_yf135_external_toolchain.fragment
|
External-toolchain description merged into target builds |
system |
no | "x86_64-linux" |
Target system |
extraDevShellPackages |
no | [] |
Additional packages in development shell |
When a parent consumes the board layer through support/parent.mk, it can supply project-specific defconfig fragments that stack on top of the base configuration. The parent Makefile sets DBL_BR_FRAGMENT for the release fragment and, optionally, DBL_BR_FRAGMENT_DEBUG for an additional debug-only fragment. The merge proceeds in the following order:
base release defconfig
+ parent release fragment (DBL_BR_FRAGMENT)
+ base debug fragment (configs/myd_yf135_debug.fragment)
+ parent debug fragment (DBL_BR_FRAGMENT_DEBUG, optional)
Release builds stop after the parent release fragment. Debug builds extend the stack with the base debug fragment and the optional parent debug fragment. Each later layer takes priority on conflicting keys, while keys matching *_FRAGMENT_FILES and BR2_ROOTFS_OVERLAY accumulate. Refer to the defconfig merger for the merge semantics.
A parent Makefile selects the fragments as follows:
DBL_BR_OVERLAY := overlay
DBL_BR_FRAGMENT := overlay/my-project.fragment
DBL_BR_FRAGMENT_DEBUG := overlay/my-project-debug.fragment # optional
include modules/dbl-buildroot/support/parent.mkThe equivalent Nix build passes the same fragments to mkProject:
mkProject {
name = "my-project";
board = "myd-yf135";
extraExternalSrcs = [ ./overlay ];
configFragment = ./overlay/my-project.fragment;
configFragmentDebug = ./overlay/my-project-debug.fragment; # optional
}The support/parent.mk forwarder injects BR2_EXTERNAL_EXTRA, CONFIG_FRAGMENT, and CONFIG_FRAGMENT_DEBUG into the submodule build automatically, so a plain make or make debug in the parent applies the correct stack without any further flags.
A superproject building for its own board hardware may extend the base device trees. The parent may provide any of:
| File (parent BR2_EXTERNAL tree) | Extends |
|---|---|
board/dts/parent-linux.dtsi |
Linux DT (stm32mp135d-myd-yf135.dts) |
board/dts/parent-u-boot.dtsi |
U-Boot control DT (stm32mp135d-myd-yf135-u-boot.dtsi) |
board/dts/parent-optee.dtsi |
OP-TEE DT (stm32mp135d-myd-yf135-optee.dts) |
board/dts/parent-tf-a.dtsi |
TF-A BL2 DT (stm32mp135d-myd-yf135-tf-a.dtsi) |
board/dts/parent-tf-a-fw-config.dtsi |
TF-A load addresses / TZC regions (stm32mp135d-myd-yf135-tf-a-fw-config.dts) |
external.mk scans all consuming externals automatically following the above path convention (no hooks, defconfig keys, or Makefile variables are required in the parent).
Each base DT ends with an #include of a parent stub, so the parent is included after every base node and can override properties, /delete-node/, and add nodes. The dtb filename, DEVICE_TREE, and the U-Boot environment are kept, so RAUC bundles, the flash layout, and boot scripts are generally unaffected when a parent adopts its own DT.
- Linux: the
LINUX_MYD_YF135_COPY_DTSpre-build hook copies the empty stub into the kernel tree, then the parent's file over it. Like any Buildroot package source change, DT edits need a rebuild:make linux-rebuild(andmake uboot-rebuildforparent-u-boot.dtsi) before re-running the image build. - U-Boot: the
UBOOT_MYD_YF135_COPY_DTSIpre-build hook copies the parent'sparent-u-boot.dtsi(or stub) intoarch/arm/dts/, mirroring the Linux hook. U-Boot compiles the base Linux dts as its control DT source, so the hook also copies the empty baseparent-linux.dtsistub. - OP-TEE and TF-A: the
OPTEE_OS_MYD_YF135_COPY_DTSIandARM_TRUSTED_FIRMWARE_MYD_YF135_COPY_DTSIpre-build hooks copy the parent files (or the stubs) intocore/arch/arm/dts/andfdts/. OTP non-secure grants and TZC regions are security-critical configuration and should be modified with care.
- Parent dtsi files are included textually, not compiled standalone. They must not contain
/dts-v1/(dtc only accepts it at the top of the file), or#includethe base DT (recursive include). - Keep the U-Boot parent dtsi minimal (gpio hogs,
cd-gpios, and similar). The U-Boot control DT only needs what U-Boot itself uses; Linux peripheral additions (SPI, I2C, ADC, PWM) can cause U-Boot to probe additional drivers. -
gpio-hognodes are processed inboard_rinit.CONFIG_GPIO_HOGis already enabled in the baseuboot.config, so hogs work without parent-side U-Boot config. - At most one external may provide each parent DT file. Composing them from multiple externals is not supported.
Image content that is built by Nix instead of a Buildroot package (for example, a cross-compiled Rust binary) can be layered into the image as an extra rootfs overlay. The overlay directories are expressed as a generated defconfig fragment whose BR2_ROOTFS_OVERLAY value accumulates onto the base and parent overlays, so no defconfig editing is needed.
mkProject accepts extraRootfsOverlays, a function returning a list of overlay directories. It is passed the image's toolchain SDK and pkgs so binaries can be cross-built against the image libc:
mkProject {
# ...
extraRootfsOverlays = { sdk, pkgs }: [
(pkgs.callPackage ./nix/my-app-overlay.nix { inherit sdk; })
];
}Each entry is a directory laid out like a rootfs overlay (for example usr/bin/my-app inside it).
For interactive submodule builds, the parent Makefile lists flake attributes that produce the same overlay directories:
DBL_BR_NIX_ROOTFS_OVERLAYS := my-app-overlay
include modules/dbl-buildroot/support/parent.mkThe make nix-overlays target builds each attribute and symlinks the result at output/nix-overlays/<attr>. The all, debug, and release targets depend on nix-overlays and stack the built store paths onto BR2_ROOTFS_OVERLAY automatically, so plain make picks up overlay changes.
Note that raw Buildroot image targets (for example make rootfs-ubifs) skip the nix-overlays prerequisite and reuse whatever BR2_ROOTFS_OVERLAY the last all/debug build left in .config. After changing the overlay configuration, run a full variant target before raw image targets.
The build prefetches all Buildroot package sources from a buildroot.lock. The lockfile parameter defaults to the submodule's own lock, which only covers the base packages used by dbl-buildroot.
If your overlay adds packages (or bumps versions via configFragment), you must point lockfile at a project-local lock, or the build will fetch overlay sources at build time and may fail in the FHSEnv.
mkProject {
# ...
configFragment = ./overlay/my-project.fragment;
lockfile = ./buildroot.lock;
}Generate or refresh that lock from the merged (base plus overlay) config with:
make nix-lockThis writes ./buildroot.lock; commit the result. The make nix-lock command builds the #lockfile flake output, which enumerates the overlay-selected packages from the same externals and merged defconfig as the real build. Re-run and commit it whenever you change package versions or add packages.
In a parent, make nix-lock regenerates only the target lock. The toolchain SDK is reused from the submodule's cached build (substituted from Cachix when the toolchainName default is kept), so no parent-side toolchain.lock is needed. A parent that overrides the toolchain* parameters to build a custom toolchain must also generate a project-local lock from the #toolchain-lockfile flake output.
The generated build.yml calls the lockfile composite action, which regenerates the locks in CI and fails the build if a committed lock is stale or missing.
make develop
make build
make sdk
make nix-lock
make
make debugThe make develop command enters the Nix development shell. The make build command performs a hermetic release build. The make sdk command produces the SDK tarball. The make nix-lock command regenerates ./buildroot.lock after package changes. The plain make command starts an interactive Buildroot session (for debug, menuconfig, etc.). The make debug command builds the debug variant.