Building Your Own: Packages, Patches, and Overrides

Part 5 of The Thingino Developer’s Journey / <- Part 4

You’ve built firmware, customized it, maybe ported a camera. Now you want to put your own software on the thing — or fix someone else’s. This is where Thingino stops being a build system you operate and becomes one you extend.

Anatomy of a package

Thingino is a BR2_EXTERNAL tree, so adding a package never touches Buildroot itself. Drop a directory into package/ and it’s auto-discovered:

package/mytool/
|-- Config.in       # the menuconfig entry
`-- mytool.mk       # how to fetch, build, install

A real, complete example from the tree — package/jsonpath/jsonpath.mk:

JSONPATH_SITE_METHOD = git
JSONPATH_SITE = https://github.com/openwrt/jsonpath
JSONPATH_VERSION = b9034210bd331749673416c6bf389cccd4e23610
JSONPATH_LICENSE = ISC, BSD-3-Clause
JSONPATH_DEPENDENCIES = json-c libubox

define JSONPATH_INSTALL_TARGET_CMDS
	$(INSTALL) -D -m 0755 $(@D)/jsonpath $(TARGET_DIR)/usr/bin/jsonpath
endef

$(eval $(cmake-package))

That’s the whole thing. Declare where the source lives, what it depends on, how to install the result, and which build-system class to use (cmake-package, autotools-package, generic-package…). Buildroot handles cross-compilation, sysroots, and ordering. Add BR2_PACKAGE_MYTOOL=y to a camera defconfig — or your local.fragment from Part 3 while experimenting — and it’s in the image.

Browse package/ for templates: thingino-daynightd shows a package with bundled files and init scripts; the thingino-webui tree shows how big one can get.

The one command that changes your life

Full rebuilds are for robots. When you’re working on one package:

CAMERA=<camera> make rebuild-mytool

This dircleans the package, rebuilds it, reinstalls it into the target filesystem, and re-finalizes the image — a couple of minutes instead of thirty. Pair it with IP=... make ota (or scp -O the binary straight over for the truly impatient) and your loop is tight.

Source overrides: hacking on the actual source

Package sources normally come from tarballs or git checkouts that Buildroot fetches and unpacks into output/. Editing files in output/build/... is a trap — your changes evaporate on the next dirclean. The correct tool is a source override: point Buildroot at a local checkout you control.

Thingino automates it:

./scripts/manage-package-overrides.sh -l            # list override candidates
./scripts/manage-package-overrides.sh prudynt-t     # clone + wire up an override

This clones the package’s source into overrides/prudynt-t/ and adds one line to local.mk:

PRUDYNT_T_OVERRIDE_SRCDIR = $(BR2_EXTERNAL)/overrides/prudynt-t

From now on, builds rsync from your checkout. Edit, then make rebuild-prudynt-t, flash, repeat. Real git history, real branches, real diffs — in a normal repository that happens to feed a firmware build. The same script disables (-d), re-enables (-e), updates (-u), and removes (-r) overrides.

Two things to know:

  • Overrides bypass Buildroot patches. If the package normally carries patches, apply them to your checkout manually before you start editing, or you’re debugging a source tree that never existed.
  • Override mappings can live in the root local.mk or in your scoped user layers (user/common/local.mk, user/<camera>/local.mk) — Part 3’s precedence rules apply.

Patch discipline

Fixing a bug in an upstream package you don’t want to fork? Buildroot’s patch convention: drop NNNN-description.patch files into the package directory and they apply in sort order at extraction time.

One Thingino-specific commandment: U-Boot support is delivered as a single enormous patch (package/all-patches/uboot/<version>/0001-from-<version>-to-thingino.patch). Never edit that patch. Add numbered follow-ups (0002-your-fix.patch, 0003-...) in the same directory; they apply after it, in order. Buildroot patches live in package/all-patches/buildroot/ and are managed by make update — same rule: append, don’t rewrite.

When producing patches, use your real identity and sign off (git format-patch, Signed-off-by: matching your git config) — patches are commits with a passport.

Streamers, briefly

The default video pipeline is prudynt; setting BR2_PACKAGE_RAPTOR_IPC=y switches a build to the newer raptor stack. If you’re planning to add an entirely new streamer backend, that’s a documented rabbit hole with its own Kconfig plumbing — see docs/streamer.md before digging.

What you learned

  • A package is two small files in package/; Buildroot does the rest.
  • make rebuild-<pkg> + OTA is the inner loop.
  • Source overrides give you real git checkouts for package development — just remember they bypass patches.
  • Patches append in numbered order; the big U-Boot patch is load-bearing and sacred.

Next up: Part 6 — Going Parallel: you now have ports in review, a package mid-refactor, and a bug report from the Discord. Time to stop switching branches and start multiplying yourself. Git worktrees, the god-level unlock.