diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16be8f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/output/ diff --git a/Config.in b/Config.in new file mode 100644 index 0000000..f83650c --- /dev/null +++ b/Config.in @@ -0,0 +1,6 @@ +menu "RPI Common packages" + +source "$BR2_EXTERNAL_RPI_COMMON_PATH/package/my-shared-lib/Config.in" +source "$BR2_EXTERNAL_RPI_COMMON_PATH/package/my-common-tool/Config.in" + +endmenu diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a390258 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +# br2-external-common standalone build wrapper +# +# Usage: +# make rpi_common_test_defconfig - Load a defconfig +# make - Build the image +# make menuconfig - Open configuration menu +# make savedefconfig - Save config back to the original defconfig +# make linux-rebuild - Rebuild a specific package +# make clean - Clean build artifacts +# +# Any valid Buildroot target works — it is passed through directly. +# +# The buildroot source is expected at ./buildroot (git submodule). +# The output directory is ./output (gitignored). + +BUILDROOT_DIR := $(CURDIR)/buildroot +OUTPUT_DIR := $(CURDIR)/output +BR2_EXTERNAL := $(CURDIR) + +CONFIG_EXISTS := $(wildcard $(OUTPUT_DIR)/.config) + +.PHONY: all + +all: +ifeq ($(CONFIG_EXISTS),) + @echo "No .config found. Load a defconfig first, e.g.:" + @echo " make rpi_common_test_defconfig" + @exit 1 +endif + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) + +%: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) $@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a5b724 --- /dev/null +++ b/README.md @@ -0,0 +1,356 @@ +# br2-external-common + +Common Buildroot BR2_EXTERNAL tree for all Raspberry Pi projects. This +repository provides shared board support, packages, rootfs overlays, and +patches that are used across multiple projects. + +This repository also pins the upstream Buildroot version as a git submodule, +serving as the single source of truth for which Buildroot release is used. When +a project pulls in this repo, it transitively gets the tested Buildroot version. + +## Repository layout + +``` +br2-external-common/ +├── external.desc Buildroot external tree descriptor (name: RPI_COMMON) +├── external.mk Includes all common package .mk files +├── Config.in Top-level Kconfig menu for common packages +├── Makefile Wrapper for standalone build invocations +├── buildroot/ [submodule] Upstream Buildroot, pinned to release tag +├── board/ +│ └── raspberrypi/ +│ ├── post_build.sh Shared post-build script (rootfs fixups) +│ ├── post_image.sh Shared post-image script (genimage invocation) +│ ├── genimage.cfg SD card partition layout +│ └── overlay/ Shared rootfs overlay (copied into target rootfs) +│ └── etc/ +│ └── network/ +│ └── interfaces +├── package/ +│ ├── my-shared-lib/ Example shared library (CMake package) +│ └── my-common-tool/ Example shared tool (generic package) +├── patches/ Patches to upstream Buildroot packages +└── configs/ + └── rpi_common_test_defconfig Minimal defconfig for standalone CI testing +``` + +## Standalone setup + +Use this to develop and test the common tree without any project-specific +additions. This is also what CI should run on every commit to this repo. + +### Prerequisites + +A Linux host with standard Buildroot dependencies installed. See the Buildroot +manual for the full list. On Debian/Ubuntu: + +```sh +sudo apt-get install -y build-essential gcc g++ bash patch gzip bzip2 \ + perl tar cpio unzip rsync file bc wget libncurses-dev git +``` + +### Clone and initialize + +```sh +git clone git@github.com:your-org/br2-external-common.git +cd br2-external-common +git submodule update --init +``` + +This pulls the Buildroot source into `buildroot/` at the pinned release tag. + +### Build the test image + +The wrapper Makefile handles the Buildroot invocation: + +```sh +make defconfig # Loads rpi_common_test_defconfig +make # Builds the full image (takes a while on first run) +``` + +The SD card image will be at `output/images/sdcard.img`. + +### Useful build commands + +```sh +make menuconfig # Open Buildroot configuration menu +make savedefconfig # Save current .config back to the test defconfig +make linux-rebuild # Rebuild just the kernel (any Buildroot target works) +make my-shared-lib-rebuild # Rebuild a specific package +make clean # Clean build artifacts +make distclean # Remove the entire output directory +``` + +You can use a different defconfig by passing DEFCONFIG: + +```sh +make defconfig DEFCONFIG=rpi_common_minimal_defconfig +``` + +## Creating a new project + +A project is a separate git repository that uses this common tree as a +submodule. Each project is its own BR2_EXTERNAL and contains only +project-specific packages, overlays, defconfigs, and board overrides. + +### Step 1: Create the project repository + +```sh +mkdir br2-external-myproject +cd br2-external-myproject +git init +``` + +### Step 2: Add common as a submodule + +```sh +git submodule add git@github.com:your-org/br2-external-common.git common +git submodule update --init --recursive +``` + +The `--recursive` is important because it also pulls common's own Buildroot +submodule into `common/buildroot/`. + +### Step 3: Create the BR2_EXTERNAL descriptor + +Create `external.desc` at the project root. The name must be unique across all +external trees and must be a valid C identifier (uppercase, underscores only): + +``` +name: MY_PROJECT +desc: My project-specific Buildroot configuration +``` + +### Step 4: Create external.mk + +This file includes only the project's own packages. The common packages are +handled by the common tree's own `external.mk` — Buildroot merges them +automatically because both trees are passed as separate BR2_EXTERNAL paths. + +```makefile +include $(sort $(wildcard $(BR2_EXTERNAL_MY_PROJECT_PATH)/package/*/*.mk)) +``` + +### Step 5: Create Config.in + +Same principle — only source the project's own package configs: + +``` +menu "My Project packages" + +source "$BR2_EXTERNAL_MY_PROJECT_PATH/package/my-app/Config.in" + +endmenu +``` + +### Step 6: Create a project defconfig + +Create `configs/rpi_myproject_defconfig`. Start by copying the common test +defconfig and modify it: + +```sh +cp common/configs/rpi_common_test_defconfig configs/rpi_myproject_defconfig +``` + +Edit the defconfig to: + +1. Add both overlays (common first, then project-specific): + +``` +BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RPI_COMMON_PATH)/board/raspberrypi/overlay $(BR2_EXTERNAL_MY_PROJECT_PATH)/overlay" +``` + +2. Add project-specific packages: + +``` +BR2_PACKAGE_MY_APP=y +``` + +The common packages (`BR2_PACKAGE_MY_SHARED_LIB`, etc.) remain as-is. + +### Step 7: Create the wrapper Makefile + +Create a `Makefile` at the project root: + +```makefile +BUILDROOT_DIR := $(CURDIR)/common/buildroot +OUTPUT_DIR := $(CURDIR)/output +BR2_EXTERNAL := $(CURDIR)/common:$(CURDIR) + +DEFCONFIG ?= rpi_myproject_defconfig + +CONFIG_EXISTS := $(wildcard $(OUTPUT_DIR)/.config) + +.PHONY: all defconfig menuconfig savedefconfig clean distclean + +all: +ifeq ($(CONFIG_EXISTS),) + @echo "No .config found. Run 'make defconfig' first." + @exit 1 +endif + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) + +defconfig: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) $(DEFCONFIG) + +menuconfig: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) menuconfig + +savedefconfig: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) savedefconfig \ + BR2_DEFCONFIG=$(CURDIR)/configs/$(DEFCONFIG) + +clean: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) clean + +distclean: + rm -rf $(OUTPUT_DIR) + +%: + $(MAKE) -C $(BUILDROOT_DIR) BR2_EXTERNAL=$(BR2_EXTERNAL) O=$(OUTPUT_DIR) $@ +``` + +Note the key difference from the common Makefile: + +- `BUILDROOT_DIR` points to `common/buildroot/` (nested submodule) +- `BR2_EXTERNAL` lists both trees, colon-separated: `common:project` + +### Step 8: Create supporting directories + +```sh +mkdir -p overlay/etc +mkdir -p package +mkdir -p board +mkdir -p configs + +echo '/output/' > .gitignore +``` + +### Step 9: Commit and build + +```sh +git add -A +git commit -m "Initial project skeleton" + +make defconfig +make +``` + +### Project directory structure + +After following these steps, your project should look like this: + +``` +br2-external-myproject/ +├── external.desc +├── external.mk +├── Config.in +├── Makefile +├── .gitmodules +├── .gitignore +├── common/ [submodule -> br2-external-common] +│ ├── buildroot/ [nested submodule -> upstream Buildroot] +│ ├── board/raspberrypi/ +│ ├── package/ +│ └── ... +├── configs/ +│ └── rpi_myproject_defconfig +├── overlay/ Project-specific rootfs overlay +├── package/ Project-specific packages +│ └── my-app/ +└── output/ Build output (gitignored) +``` + +## Branching strategy + +This repository uses long-lived branches to support multiple Buildroot +versions simultaneously, mirroring Buildroot's own LTS release model. + +### Branch naming + +- `main` — Tracks the latest Buildroot release (currently 2026.02). Active + development of shared packages happens here. +- `YYYY.MM.x` — Maintenance branches for older Buildroot LTS lines (e.g., + `2023.02.x`). These receive only bug fixes and minor Buildroot bumps within + the same LTS series. + +### Creating a maintenance branch + +When a project needs to stay on an older Buildroot version: + +```sh +# Start from the last commit that used the older Buildroot +git checkout -b 2023.02.x + +# Verify the buildroot submodule points to the right tag +cd buildroot +git log --oneline -1 # Should show 2023.02.x tag +cd .. + +git push -u origin 2023.02.x +``` + +### Bumping a minor Buildroot version on a maintenance branch + +For example, upgrading from 2023.02.1 to 2023.02.2: + +```sh +git checkout 2023.02.x + +cd buildroot +git fetch --tags +git checkout 2023.02.2 +cd .. + +git add buildroot +git commit -m "buildroot: bump to 2023.02.2" +git push +``` + +Then in the project that uses this branch: + +```sh +cd common +git pull origin 2023.02.x +cd .. + +git add common +git commit -m "common: pick up buildroot 2023.02.2" +``` + +### Backporting shared package fixes + +When a bug fix on `main` also applies to a maintenance branch: + +```sh +git checkout 2023.02.x +git cherry-pick +# Resolve conflicts if the package versions have diverged +# Test with: make defconfig && make +git push +``` + +### End-of-life + +When a legacy project is decommissioned, its maintenance branch can be +archived (renamed to `archive/2023.02.x`) or deleted. No other projects +are affected. + +## Adding a new shared package + +1. Create a directory under `package/` with the package name. +2. Add a `Config.in` with the Kconfig entry. +3. Add a `.mk` file with the Buildroot package recipe. +4. Source the new `Config.in` in the top-level `Config.in`. +5. Enable the package in `configs/rpi_common_test_defconfig`. +6. Build and verify: `make my-new-package-rebuild`. + +See `package/my-shared-lib/` (CMake example) and `package/my-common-tool/` +(generic example) for reference. + +## Adding patches to upstream Buildroot packages + +Place patch files under `patches//`. Buildroot automatically +applies patches from `BR2_EXTERNAL_RPI_COMMON_PATH/patches//` +during the package build. Patches must follow Buildroot's naming convention: +`NNNN-description.patch` (e.g., `0001-fix-cross-compilation.patch`). diff --git a/board/raspberrypi/genimage.cfg b/board/raspberrypi/genimage.cfg new file mode 100644 index 0000000..6c09b4f --- /dev/null +++ b/board/raspberrypi/genimage.cfg @@ -0,0 +1,53 @@ +/* genimage.cfg - SD card image layout for Raspberry Pi + * + * Partition layout: + * - boot (FAT32, 64MB): kernel, device tree, bootloader files + * - rootfs (ext4, 256MB): root filesystem + * + * The final image is written to sdcard.img. + * + * Adjust partition sizes as needed for your project. Projects can + * override this file by providing their own genimage.cfg and setting + * GENIMAGE_CFG in their post-image script. + */ + +image boot.vfat { + vfat { + files = { + "bcm2710-rpi-3-b.dtb", + "bcm2711-rpi-4-b.dtb", + "rpi-firmware/bootcode.bin", + "rpi-firmware/start.elf", + "rpi-firmware/fixup.dat", + "rpi-firmware/config.txt", + "rpi-firmware/cmdline.txt", + "Image" + } + } + + size = 64M +} + +image rootfs.ext4 { + ext4 { + label = "rootfs" + } + + size = 256M +} + +image sdcard.img { + hdimage { + } + + partition boot { + partition-type = 0xC + bootable = "true" + image = "boot.vfat" + } + + partition rootfs { + partition-type = 0x83 + image = "rootfs.ext4" + } +} diff --git a/board/raspberrypi/overlay/etc/network/interfaces b/board/raspberrypi/overlay/etc/network/interfaces new file mode 100644 index 0000000..aa30214 --- /dev/null +++ b/board/raspberrypi/overlay/etc/network/interfaces @@ -0,0 +1,12 @@ +# /etc/network/interfaces - Common network configuration +# +# This file is part of the shared rootfs overlay and provides a +# sensible default network configuration for all Raspberry Pi projects. +# Projects can override this by placing their own version in their +# project-specific overlay (which is applied after this one). + +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp diff --git a/board/raspberrypi/post_build.sh b/board/raspberrypi/post_build.sh new file mode 100755 index 0000000..50f5f23 --- /dev/null +++ b/board/raspberrypi/post_build.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# post_build.sh - Common post-build script for Raspberry Pi +# +# Called by Buildroot after assembling the target rootfs. +# Arguments: +# $1 = path to the target rootfs directory (e.g., output/target) +# +# Environment: +# BR2_EXTERNAL_RPI_COMMON_PATH = path to this external tree +# +# Use this script for: +# - Fixing file permissions +# - Creating symlinks +# - Generating config files that depend on build-time variables +# - Stripping unnecessary files from the rootfs +# +# Do NOT use this for: +# - Installing packages (use rootfs overlay or a package .mk instead) +# - Modifying files outside $1 + +set -e + +TARGET_DIR="$1" + +# Example: ensure /etc/hostname exists with a default value +if [ ! -f "${TARGET_DIR}/etc/hostname" ]; then + echo "rpi-common" > "${TARGET_DIR}/etc/hostname" +fi + +# Example: remove unnecessary documentation to save space +rm -rf "${TARGET_DIR}/usr/share/man" +rm -rf "${TARGET_DIR}/usr/share/doc" + +echo ">>> Common post-build script completed" diff --git a/board/raspberrypi/post_image.sh b/board/raspberrypi/post_image.sh new file mode 100755 index 0000000..d06ebc8 --- /dev/null +++ b/board/raspberrypi/post_image.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# post_image.sh - Common post-image script for Raspberry Pi +# +# Called by Buildroot after filesystem images (ext4, etc.) are generated. +# Arguments: +# $1 = path to the images directory (e.g., output/images) +# +# Environment: +# BR2_EXTERNAL_RPI_COMMON_PATH = path to this external tree +# BINARIES_DIR = same as $1 +# BUILD_DIR = path to the build directory +# +# This script uses genimage to assemble the final SD card image +# from the boot files and root filesystem. + +set -e + +BOARD_DIR="${BR2_EXTERNAL_RPI_COMMON_PATH}/board/raspberrypi" + +# Use project-specific genimage.cfg if it exists, otherwise use common one. +# Projects can override by setting GENIMAGE_CFG in their own post-image script. +GENIMAGE_CFG="${GENIMAGE_CFG:-${BOARD_DIR}/genimage.cfg}" + +# genimage requires a temporary directory +GENIMAGE_TMP="${BUILD_DIR}/genimage.tmp" +rm -rf "${GENIMAGE_TMP}" + +genimage \ + --rootpath "${TARGET_DIR}" \ + --tmppath "${GENIMAGE_TMP}" \ + --inputpath "${BINARIES_DIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${GENIMAGE_CFG}" + +echo ">>> SD card image generated: ${BINARIES_DIR}/sdcard.img" diff --git a/configs/rpi_common_test_defconfig b/configs/rpi_common_test_defconfig new file mode 100644 index 0000000..1fcdba7 --- /dev/null +++ b/configs/rpi_common_test_defconfig @@ -0,0 +1,36 @@ +# Architecture +BR2_aarch64=y + +# Toolchain +BR2_TOOLCHAIN_BUILDROOT_GLIBC=y + +# Kernel +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL=y +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,raspberrypi,linux,stable_20231123)/linux-stable_20231123.tar.gz" +BR2_LINUX_KERNEL_DEFCONFIG="bcm2711" +BR2_LINUX_KERNEL_DTS_SUPPORT=y +BR2_LINUX_KERNEL_INTREE_DTS_NAME="broadcom/bcm2711-rpi-4-b" +BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y + +# Bootloader +BR2_PACKAGE_RPI_FIRMWARE=y +BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI4=y + +# Filesystem +BR2_TARGET_ROOTFS_EXT2=y +BR2_TARGET_ROOTFS_EXT2_4=y + +# Host tools needed for image generation +BR2_PACKAGE_HOST_GENIMAGE=y +BR2_PACKAGE_HOST_DOSFSTOOLS=y +BR2_PACKAGE_HOST_MTOOLS=y + +# Rootfs overlay and scripts from this BR2_EXTERNAL +BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RPI_COMMON_PATH)/board/raspberrypi/overlay" +BR2_ROOTFS_POST_BUILD_SCRIPT="$(BR2_EXTERNAL_RPI_COMMON_PATH)/board/raspberrypi/post_build.sh" +BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_RPI_COMMON_PATH)/board/raspberrypi/post_image.sh" + +# Enable all common packages for testing +BR2_PACKAGE_MY_SHARED_LIB=y +BR2_PACKAGE_MY_COMMON_TOOL=y diff --git a/external.desc b/external.desc new file mode 100644 index 0000000..a713e64 --- /dev/null +++ b/external.desc @@ -0,0 +1,2 @@ +name: RPI_COMMON +desc: Common Raspberry Pi board support, shared packages, and overlays diff --git a/external.mk b/external.mk new file mode 100644 index 0000000..7ac357e --- /dev/null +++ b/external.mk @@ -0,0 +1 @@ +include $(sort $(wildcard $(BR2_EXTERNAL_RPI_COMMON_PATH)/package/*/*.mk)) diff --git a/package/my-common-tool/Config.in b/package/my-common-tool/Config.in new file mode 100644 index 0000000..e644d69 --- /dev/null +++ b/package/my-common-tool/Config.in @@ -0,0 +1,10 @@ +config BR2_PACKAGE_MY_COMMON_TOOL + bool "my-common-tool" + select BR2_PACKAGE_MY_SHARED_LIB + help + A command-line utility shared across multiple Raspberry Pi + projects. Depends on my-shared-lib. + + Installs the 'my-common-tool' binary into /usr/bin on the target. + + https://github.com/your-org/my-common-tool diff --git a/package/my-common-tool/my-common-tool.mk b/package/my-common-tool/my-common-tool.mk new file mode 100644 index 0000000..72894d2 --- /dev/null +++ b/package/my-common-tool/my-common-tool.mk @@ -0,0 +1,22 @@ +################################################################################ +# +# my-common-tool +# +################################################################################ + +MY_COMMON_TOOL_VERSION = 1.0.0 +MY_COMMON_TOOL_SITE = https://github.com/your-org/my-common-tool.git +MY_COMMON_TOOL_SITE_METHOD = git +MY_COMMON_TOOL_LICENSE = MIT +MY_COMMON_TOOL_LICENSE_FILES = LICENSE +MY_COMMON_TOOL_DEPENDENCIES = my-shared-lib + +define MY_COMMON_TOOL_BUILD_CMDS + $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) +endef + +define MY_COMMON_TOOL_INSTALL_TARGET_CMDS + $(INSTALL) -D -m 0755 $(@D)/my-common-tool $(TARGET_DIR)/usr/bin/my-common-tool +endef + +$(eval $(generic-package)) diff --git a/package/my-shared-lib/Config.in b/package/my-shared-lib/Config.in new file mode 100644 index 0000000..ddcc8bf --- /dev/null +++ b/package/my-shared-lib/Config.in @@ -0,0 +1,9 @@ +config BR2_PACKAGE_MY_SHARED_LIB + bool "my-shared-lib" + help + A shared library providing common functionality used across + multiple Raspberry Pi projects. + + Installs libmyshared.so into /usr/lib on the target. + + https://github.com/your-org/my-shared-lib diff --git a/package/my-shared-lib/my-shared-lib.mk b/package/my-shared-lib/my-shared-lib.mk new file mode 100644 index 0000000..35075ed --- /dev/null +++ b/package/my-shared-lib/my-shared-lib.mk @@ -0,0 +1,22 @@ +################################################################################ +# +# my-shared-lib +# +################################################################################ + +# IMPORTANT: Update this version and hash when upgrading the package. +# The hash file (my-shared-lib.hash) should also be updated accordingly. +MY_SHARED_LIB_VERSION = 1.0.0 +MY_SHARED_LIB_SITE = https://github.com/your-org/my-shared-lib.git +MY_SHARED_LIB_SITE_METHOD = git +MY_SHARED_LIB_LICENSE = MIT +MY_SHARED_LIB_LICENSE_FILES = LICENSE +MY_SHARED_LIB_INSTALL_STAGING = YES + +# CMake options passed to the package's build. +# Add any project-specific CMake definitions here. +MY_SHARED_LIB_CONF_OPTS = \ + -DBUILD_TESTS=OFF \ + -DBUILD_EXAMPLES=OFF + +$(eval $(cmake-package)) diff --git a/patches/.gitkeep b/patches/.gitkeep new file mode 100644 index 0000000..e69de29