Compare commits

...

10 Commits

Author SHA1 Message Date
Cyril Brulebois
b73e3124a9 Avoid sid-updates as well. 2026-03-27 00:44:08 +01:00
Cyril Brulebois
8ae9b8e5df Build for sid. 2026-03-26 23:24:40 +01:00
Cyril Brulebois
7d2b2aa1cb Add support for sid.
There's no sid-security suite, so we need to be careful.
2026-03-26 23:24:22 +01:00
Cyril Brulebois
97cea51e6d Simplify firmware management.
Given the supported distributions, only non-free-firmware matters.
2026-03-26 23:24:22 +01:00
Cyril Brulebois
6aaf342c32 Update documentation accordingly.
Link: https://salsa.debian.org/raspi-team/image-specs/-/issues/75
2026-03-26 23:24:22 +01:00
Cyril Brulebois
0d208d2f4d Rework images, grouping by architectures.
Building images for Pi 3 and Pi 4 families didn't make much sense since
the same image can run on both (modulo possible CMA tweaks for the
Compute Module 4). The same is almost true for the Pi 5 (given a
sufficiently high kernel version).

Let's build:
 - for armhf (Pi 2);
 - for arm64 (Pi 3, Pi 4, Pi 5).

Let's forget about armel, it's no longer supported in Debian anyway.

Link: https://salsa.debian.org/raspi-team/image-specs/-/issues/75
2026-03-26 23:24:18 +01:00
Cyril Brulebois
f15fe61586 Add guards against empty recipes/nonexistent images.
This is just a proof of concept at this point, trying to get `make` to
succeed.
2025-11-29 12:33:42 +01:00
Cyril Brulebois
bd54ac4476 Bootstrap support for forky.
Until now we've been happily building all version × suite combinations
but that's not possible anymore (armel was dropped).

Generating an empty recipe is just a proof of concept, based on the idea
the Python script contains the logic, and the Makefile doesn't/shouldn't
know about those details.
2025-11-29 12:31:22 +01:00
Cyril Brulebois
38d35687d4 Avoid hardcoding filenames in _clean_yaml. 2025-11-29 12:30:14 +01:00
Cyril Brulebois
8cf061c25d Start building images for forky.
This is just a stepping stone, see follow-up commit(s).
2025-11-29 12:29:49 +01:00
4 changed files with 89 additions and 83 deletions

View File

@@ -1,11 +1,11 @@
all: shasums all: shasums
# List all the supported and built Pi platforms here. They get expanded # List all the supported and built Pi platforms here. They get expanded
# to names like 'raspi_2_buster.yaml' and 'raspi_3_bullseye.img.xz'. # to names like 'raspi_arm64_trixie.yaml' and 'raspi_arm64_trixie.img.xz'.
BUILD_FAMILIES := 1 2 3 4 BUILD_ARCHS := armhf arm64
BUILD_RELEASES := bookworm trixie BUILD_RELEASES := bookworm trixie forky sid
platforms := $(foreach plat, $(BUILD_FAMILIES),$(foreach rel, $(BUILD_RELEASES), raspi_$(plat)_$(rel))) platforms := $(foreach plat, $(BUILD_ARCHS),$(foreach rel, $(BUILD_RELEASES), raspi_$(plat)_$(rel)))
shasums: $(addsuffix .img.sha256,$(platforms)) $(addsuffix .img.xz.sha256,$(platforms)) shasums: $(addsuffix .img.sha256,$(platforms)) $(addsuffix .img.xz.sha256,$(platforms))
xzimages: $(addsuffix .img.xz,$(platforms)) xzimages: $(addsuffix .img.xz,$(platforms))
@@ -24,40 +24,52 @@ endif
target_platforms: target_platforms:
@echo $(platforms) @echo $(platforms)
# Generate targets based on all family * release combinations: # Generate targets based on all architecture * release combinations:
define dynamic_yaml_target = define dynamic_yaml_target =
raspi_$(1)_$(2).yaml: raspi_master.yaml generate-recipe.py raspi_$(1)_$(2).yaml: raspi_master.yaml generate-recipe.py
raspi_$(1)_$(2).yaml: raspi_$(1)_$(2).yaml:
./generate-recipe.py $(1) $(2) ./generate-recipe.py $(1) $(2)
endef endef
$(foreach release,$(BUILD_RELEASES), \ $(foreach release,$(BUILD_RELEASES), \
$(foreach family,$(BUILD_FAMILIES), \ $(foreach arch,$(BUILD_ARCHS), \
$(eval $(call dynamic_yaml_target,$(family),$(release))))) $(eval $(call dynamic_yaml_target,$(arch),$(release)))))
%.img.sha256: %.img %.img.sha256: %.img
echo $@ if [ -e $< ]; then \
sha256sum $< > $@ echo $@; \
sha256sum $< > $@; \
fi
%.img.xz.sha256: %.img.xz %.img.xz.sha256: %.img.xz
echo $@ if [ -e $< ]; then \
sha256sum $< > $@ echo $@; \
sha256sum $< > $@; \
fi
%.img.xz: %.img %.img.xz: %.img
xz -f -k -z -9 $< if [ -e $< ]; then \
xz -f -k -z -9 $<; \
fi
%.img.bmap: %.img %.img.bmap: %.img
bmaptool create -o $@ $< if [ -e $< ]; then \
bmaptool create -o $@ $<; \
fi
%.img: %.yaml %.img: %.yaml
touch $(@:.img=.log) if [ ! -s $< ]; then \
time nice $(as_root) vmdb2 --verbose --rootfs-tarball=$(subst .img,.tar.gz,$@) --output=$@ $(subst .img,.yaml,$@) --log $(subst .img,.log,$@) echo "W: skipping empty recipe ($<)"; \
chmod 0644 $@ $(@,.img=.log) else \
touch $(@:.img=.log); \
time nice $(as_root) vmdb2 --verbose --rootfs-tarball=$(subst .img,.tar.gz,$@) --output=$@ $(subst .img,.yaml,$@) --log $(subst .img,.log,$@); \
chmod 0644 $@ $(@,.img=.log); \
fi
_ck_root: _ck_root:
[ `whoami` = 'root' ] # Only root can summon vmdb2 ☹ [ `whoami` = 'root' ] # Only root can summon vmdb2 ☹
_clean_yaml: _clean_yaml:
rm -f $(addsuffix .yaml,$(platforms)) raspi_base_bookworm.yaml raspi_base_trixie.yaml rm -f $(addsuffix .yaml,$(platforms)) $(foreach rel,$(BUILD_RELEASES),raspi_base_$(rel).yaml)
_clean_images: _clean_images:
rm -f $(addsuffix .img,$(platforms)) rm -f $(addsuffix .img,$(platforms))
_clean_xzimages: _clean_xzimages:

View File

@@ -57,44 +57,49 @@ Otherwise, because some steps of building the image require root privileges,
you'll need to execute `make` as root. you'll need to execute `make` as root.
The argument to `make` is constructed as follows: The argument to `make` is constructed as follows:
`raspi_<model>_<release>.<result-type>` `raspi_<arch>_<release>.<result-type>`
Whereby <model\> is one of `1`, `2`, `3` or `4`, <release\> is either Whereby `<arch>` is one of `armhf` or `arm64`, `<release>` is either
`bullseye`, `bookworm`, or `trixie`; and <result-type\> is `img` or `yaml`. `bookworm`, or `trixie`; and `<result-type>` is `img` or `yaml`.
Model `1` should be used for the Raspberry Pi 0, 0w and 1, models A and Architecture `armel` is no longer supported in Debian, and that would have been
B. Model `2` for the Raspberry Pi 2 models A and B. Model `3` for all used for the Raspberry Pi 0, 0w and 1, models A and B.
models of the Raspberry Pi 3 and model `4` for all models of the
Raspberry Pi 4. Architecture `armhf` is for Raspberry Pi 2 models A and B.
So if you want to build the default image for a Raspberry Pi 3B+ with
Bullseye, you can just issue: Architecture `arm64` is for Raspberry Pi 3, Pi 4, and Pi 5 (new in forky),
alongside Compute Module 3, Compule Module 4, and Pi 400.
So if you want to build the default image for a Raspberry Pi 3B+ with Trixie,
you can just issue:
```shell ```shell
make raspi_3_bullseye.img make raspi_arm64_trixie.img
``` ```
This will first create a `raspi_3_bullseye.yaml` file and then use that This will first create a `raspi_arm64_trixie.yaml` file and then use that
*yaml* recipe to build the image with `vmdb2`. *yaml* recipe to build the image with `vmdb2`.
You can also edit the `yaml` file to customize the built image. If you You can also edit the `yaml` file to customize the built image. If you
want to start from the platform-specific recipe, you can issue: want to start from the platform-specific recipe, you can issue:
```shell ```shell
make raspi_3_bullseye.yaml make raspi_arm64_trixie.yaml
``` ```
The recipe drives [vmdb2](https://vmdb2.liw.fi/), the successor to The recipe drives [vmdb2](https://vmdb2.liw.fi/), the successor to
`vmdebootstrap`. Please refer to [its `vmdebootstrap`. Please refer to [its
documentation](https://vmdb2.liw.fi/documentation/) for further details; documentation](https://vmdb2.liw.fi/documentation/) for further details;
it is quite an easy format to understand. it is quite an easy format to understand.
Copy the generated file to a name descriptive enough for you (say, Copy the generated file to a name descriptive enough for you (say,
`my_raspi_bullseye.yaml`). Once you have edited the recipe for your `my_raspi_trixie.yaml`). Once you have edited the recipe for your
specific needs, you can generate the image by issuing the following (as specific needs, you can generate the image by issuing the following (as
root): root):
```shell ```shell
vmdb2 --rootfs-tarball=my_raspi_bullseye.tar.gz --output \ vmdb2 --rootfs-tarball=my_raspi_trixie.tar.gz --output \
my_raspi_bullseye.img my_raspi_bullseye.yaml --log my_raspi_bullseye.log my_raspi_trixie.img my_raspi_trixie.yaml --log my_raspi_trixie.log
``` ```
This is, just follow what is done by the `_build_img` target of the This is, just follow what is done by the `_build_img` target of the
@@ -110,25 +115,25 @@ important parts of your system. Double check it's the correct
device!), copy the image onto the SD card: device!), copy the image onto the SD card:
```shell ```shell
bmaptool copy raspi_3_bullseye.img.xz /dev/mmcblk0 bmaptool copy raspi_arm64_trixie.img.xz /dev/mmcblk0
``` ```
Alternatively, if you don't have `bmap-tools` installed, you can use Alternatively, if you don't have `bmap-tools` installed, you can use
`dd` with the compressed image: `dd` with the compressed image:
```shell ```shell
xzcat raspi_3_bullseye.img.xz | dd of=/dev/mmcblk0 bs=64k oflag=dsync status=progress xzcat raspi_arm64_trixie.img.xz | dd of=/dev/mmcblk0 bs=64k oflag=dsync status=progress
``` ```
Or with the uncompressed image: Or with the uncompressed image:
```shell ```shell
dd if=raspi_3_bullseye.img of=/dev/mmcblk0 bs=64k oflag=dsync status=progress dd if=raspi_arm64_trixie.img of=/dev/mmcblk0 bs=64k oflag=dsync status=progress
``` ```
Then, plug the SD card into the Raspberry Pi, and power it up. Then, plug the SD card into the Raspberry Pi, and power it up.
The image uses the hostname `rpi0w`, `rpi2`, `rpi3`, or `rpi4` depending on the The image uses the hostname `rpi-armhf` or `rpi-arm64` depending on the
target build. The provided image will allow you to log in with the target build. The provided image will allow you to log in with the
`root` account with no password set, but only logging in at the `root` account with no password set, but only logging in at the
physical console (be it serial or by USB keyboard and HDMI monitor). physical console (be it serial or by USB keyboard and HDMI monitor).

View File

@@ -3,6 +3,7 @@
import re import re
import sys import sys
import subprocess import subprocess
from pathlib import Path
# pylint: disable=invalid-name # pylint: disable=invalid-name
@@ -12,55 +13,35 @@ if len(sys.argv) != 3:
print("E: need 2 arguments", file=sys.stderr) print("E: need 2 arguments", file=sys.stderr)
sys.exit(1) sys.exit(1)
version = sys.argv[1] arch = sys.argv[1]
if version not in ["1", "2", "3", "4"]: if arch not in ['armhf', 'arm64']:
print("E: unsupported version %s" % version, file=sys.stderr) print("E: unsupported arch %s" % arch, file=sys.stderr)
sys.exit(1) sys.exit(1)
suite = sys.argv[2] suite = sys.argv[2]
if suite not in ['bullseye', 'bookworm', 'trixie']: if suite not in ['bookworm', 'trixie', 'forky', 'sid']:
print("E: unsupported suite %s" % suite, file=sys.stderr) print("E: unsupported suite %s" % suite, file=sys.stderr)
sys.exit(1) sys.exit(1)
target_yaml = 'raspi_%s_%s.yaml' % (version, suite) target_yaml = 'raspi_%s_%s.yaml' % (arch, suite)
### Setting variables based on suite and version starts here ### Setting variables based on suite and version starts here
# Arch, kernel, DTB: # Arch, kernel, DTB:
if version == '1': if arch == 'armhf':
arch = 'armel'
linux = 'linux-image-rpi'
dtb = '/usr/lib/linux-image-*-rpi/bcm*rpi-*.dtb'
elif version == '2':
arch = 'armhf' arch = 'armhf'
linux = 'linux-image-armmp' linux = 'linux-image-armmp'
dtb = '/usr/lib/linux-image-*-armmp/bcm*rpi*.dtb' dtb = '/usr/lib/linux-image-*-armmp/bcm*rpi*.dtb'
elif version in ['3', '4']: elif arch == 'arm64':
arch = 'arm64'
linux = 'linux-image-arm64' linux = 'linux-image-arm64'
dtb = '/usr/lib/linux-image-*-arm64/broadcom/bcm*rpi*.dtb' dtb = '/usr/lib/linux-image-*-arm64/broadcom/bcm*rpi*.dtb'
# Bookworm introduced the 'non-free-firmware' component¹; before that, # wireless/bluetooth firmware:
# raspi-firmware was in 'non-free' if arch != 'armhf':
#
# ¹ https://www.debian.org/vote/2022/vote_003
if suite != 'bullseye':
firmware_component = 'non-free-firmware'
firmware_component_old = 'non-free'
else:
firmware_component = 'non-free'
firmware_component_old = ''
# wireless firmware:
if version != '2':
wireless_firmware = 'firmware-brcm80211' wireless_firmware = 'firmware-brcm80211'
else:
wireless_firmware = ''
# bluetooth firmware:
if version != '2':
bluetooth_firmware = 'bluez-firmware' bluetooth_firmware = 'bluez-firmware'
else: else:
wireless_firmware = ''
bluetooth_firmware = '' bluetooth_firmware = ''
# Pi 4 on buster required some backports. Let's keep variables around, ready to # Pi 4 on buster required some backports. Let's keep variables around, ready to
@@ -69,20 +50,20 @@ backports_enable = False
backports_suite = '%s-backports' % suite backports_suite = '%s-backports' % suite
# Serial console: # Serial console:
if version in ['1', '2']: if arch == 'armhf':
serial = 'ttyAMA0,115200' serial = 'ttyAMA0,115200'
elif version in ['3', '4']: elif arch == 'arm64':
serial = 'ttyS1,115200' serial = 'ttyS1,115200'
# CMA fixup: # CMA fixup:
extra_chroot_shell_cmds = [] extra_chroot_shell_cmds = []
if version == '4': if arch == 'arm64':
extra_chroot_shell_cmds = [ extra_chroot_shell_cmds = [
"sed -i 's/cma=64M //' /boot/firmware/cmdline.txt", "sed -i 's/cma=64M //' /boot/firmware/cmdline.txt",
] ]
# Hostname: # Hostname:
hostname = 'rpi_%s' % version hostname = 'rpi-%s' % arch
# Nothing yet! # Nothing yet!
extra_root_shell_cmds = [] extra_root_shell_cmds = []
@@ -94,15 +75,24 @@ extra_root_shell_cmds = []
if backports_enable: if backports_enable:
backports_stanza = """ backports_stanza = """
%s %s
deb http://deb.debian.org/debian/ %s main %s deb http://deb.debian.org/debian/ %s main non-free-firmware
""" % (backports_enable, backports_suite, firmware_component) """ % (backports_enable, backports_suite)
else: else:
# ugh # ugh
backports_stanza = """ backports_stanza = """
# Backports are _not_ enabled by default. # Backports are _not_ enabled by default.
# Enable them by uncommenting the following line: # Enable them by uncommenting the following line:
# deb http://deb.debian.org/debian %s main %s # deb http://deb.debian.org/debian %s main non-free-firmware
""" % (backports_suite, firmware_component) """ % backports_suite
# Enable updates and security for everyone but sid:
if suite != 'sid':
updates_stanza = 'deb http://deb.debian.org/debian %s-updates main non-free-firmware' % suite
security_stanza = 'deb http://security.debian.org/debian-security %s-security main non-free-firmware' % suite
else:
updates_stanza = ''
security_stanza = ''
gitcommit = subprocess.getoutput("git show -s --pretty='format:%C(auto)%h (%s, %ad)' --date=short ") gitcommit = subprocess.getoutput("git show -s --pretty='format:%C(auto)%h (%s, %ad)' --date=short ")
buildtime = subprocess.getoutput("date --utc +'%Y-%m-%d %H:%M'") buildtime = subprocess.getoutput("date --utc +'%Y-%m-%d %H:%M'")
@@ -134,8 +124,6 @@ with open('raspi_master.yaml', 'r') as in_file:
out_text = in_text \ out_text = in_text \
.replace('__RELEASE__', suite) \ .replace('__RELEASE__', suite) \
.replace('__ARCH__', arch) \ .replace('__ARCH__', arch) \
.replace('__FIRMWARE_COMPONENT__', firmware_component) \
.replace('__FIRMWARE_COMPONENT_OLD__', firmware_component_old) \
.replace('__LINUX_IMAGE__', linux) \ .replace('__LINUX_IMAGE__', linux) \
.replace('__DTB__', dtb) \ .replace('__DTB__', dtb) \
.replace('__WIRELESS_FIRMWARE__', wireless_firmware) \ .replace('__WIRELESS_FIRMWARE__', wireless_firmware) \
@@ -147,7 +135,9 @@ with open('raspi_master.yaml', 'r') as in_file:
out_text = align_replace(out_text, '__EXTRA_ROOT_SHELL_CMDS__', extra_root_shell_cmds) out_text = align_replace(out_text, '__EXTRA_ROOT_SHELL_CMDS__', extra_root_shell_cmds)
out_text = align_replace(out_text, '__EXTRA_CHROOT_SHELL_CMDS__', extra_chroot_shell_cmds) out_text = align_replace(out_text, '__EXTRA_CHROOT_SHELL_CMDS__', extra_chroot_shell_cmds)
out_text = align_replace(out_text, '__UPDATES_', updates_stanza.splitlines())
out_text = align_replace(out_text, '__BACKPORTS__', backports_stanza.splitlines()) out_text = align_replace(out_text, '__BACKPORTS__', backports_stanza.splitlines())
out_text = align_replace(out_text, '__SECURITY__', security_stanza.splitlines())
# Try not to keep lines where the placeholder was replaced # Try not to keep lines where the placeholder was replaced
# with nothing at all (including on a "list item" line): # with nothing at all (including on a "list item" line):

View File

@@ -47,15 +47,14 @@ steps:
arch: __ARCH__ arch: __ARCH__
components: components:
- main - main
- __FIRMWARE_COMPONENT__ - non-free-firmware
- __FIRMWARE_COMPONENT_OLD__
unless: rootfs_unpacked unless: rootfs_unpacked
- create-file: /etc/apt/sources.list - create-file: /etc/apt/sources.list
contents: |+ contents: |+
deb http://deb.debian.org/debian __RELEASE__ main __FIRMWARE_COMPONENT__ __FIRMWARE_COMPONENT_OLD__ deb http://deb.debian.org/debian __RELEASE__ main non-free-firmware
deb http://deb.debian.org/debian __RELEASE__-updates main __FIRMWARE_COMPONENT__ __FIRMWARE_COMPONENT_OLD__ __UPDATES__
deb http://security.debian.org/debian-security __RELEASE__-security main __FIRMWARE_COMPONENT__ __FIRMWARE_COMPONENT_OLD__ __SECURITY__
__BACKPORTS__ __BACKPORTS__
unless: rootfs_unpacked unless: rootfs_unpacked