Skip to content

Commit b7716d5

Browse files
authored
Merge pull request #125 from learmj/feat_srcbuild
Install mission critical dependencies from source, improve dep handling
2 parents d477392 + c57f6b9 commit b7716d5

File tree

26 files changed

+1061
-121
lines changed

26 files changed

+1061
-121
lines changed

README.adoc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ sudo ./install_deps.sh
7070

7171
See the `depends` file for the complete list of required packages.
7272

73-
=== Important
73+
rpi-image-gen builds its own copies of critical host tools so it always has current, fully featured versions and supports architectures the workstation distribution might not package. Installing and running them from a private sysroot keeps the host system untouched. The trade-off is a small one-time build delay, plus additional native dependencies that most developer workstations already carry. The resulting tools live in a reusable sysroot that stays isolated from distribution packages and can be reused for every build.
7474

75-
rpi-image-gen has been developed on Raspberry Pi OS which, at the time of writing, is Debian Bookworm arm64. It heavily favours Debian-based systems and will run on non-arm64 platforms (such as x86_64) via QEMU emulation or inside container environments. However, there is currently no formal support for these non-native environments.
75+
=== Important
7676

77-
If utilising sparse images in your workflow, e.g. with *rpi-sb-provisioner* (https://github.com/raspberrypi/rpi-sb-provisioner), please refer to the link:docs/provisioning/index.adoc[provisioning documentation] for important advice.
77+
rpi-image-gen is developed on Raspberry Pi OS and supports builds on Debian Bookworm and Trixie arm64 systems. It expects a Debian-based host and will run on non-arm64 platforms (such as x86_64) via QEMU emulation or in container environments, although those non-native setups aren’t formally supported.
7878

7979
== Documentation
8080

@@ -98,6 +98,7 @@ See the `examples/` directory for tips and help
9898
* `layer/` - Layer library
9999
* `layer-hooks/` - Common hooks used by layer library
100100
* `lib/` - Execution helpers and macros, eg CLI handling, reusable constructs
101+
* `package/` - Build recipes and framework for tools
101102
* `scripts/` - Dedicated functional hooks, eg for bdebstrap
102103
* `site/` - Core Python engine classes
103104
* `templates/` - Templating assets, eg doc generation

bin/vfetch

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
# File fetch with verification and cache support
6+
# Usage:
7+
# [IGconf_sys_cachedir=/path/to/cache] <script> <metadata.file> </path/to/output.file>
8+
#
9+
# Metadata - mirrors supported, use consistent name:
10+
# <name> <algo:hex> <url or local path>
11+
# algo: sha256|sha256sum|sha512|sha512sum
12+
13+
14+
msg() {
15+
echo -e "$*"
16+
}
17+
18+
err (){
19+
>&2 msg "Error: $*"
20+
}
21+
22+
die (){
23+
[[ -n "$*" ]] && err "$*"
24+
exit 1
25+
}
26+
27+
28+
meta="${1:-}"; dest="${2:-}"
29+
[[ -n "$meta" && -n "$dest" ]] || die "Expected args: <metadata.file> </path/to/output.file>"
30+
31+
32+
# Enforce that dest is a file path, not a directory
33+
if [[ -d "$dest" || "$dest" == */ ]]; then
34+
die "Output path must be a file, not a directory"
35+
fi
36+
37+
38+
# Default cache location
39+
cache_dir="${IGconf_sys_cachedir:-/tmp}"
40+
mkdir -p -- "$cache_dir"
41+
42+
43+
# Output path is always an explicit file path
44+
out_path="$dest"
45+
out_dir="$(dirname -- "$out_path")"
46+
mkdir -p -- "$out_dir"
47+
48+
49+
# digest <algo> <file> -> hex
50+
digest()
51+
{
52+
case "$1" in
53+
sha256|sha256sum) sha256sum "$2" | sed 's/[[:space:]].*$//' ;;
54+
sha512|sha512sum) sha512sum "$2" | sed 's/[[:space:]].*$//' ;;
55+
*) die "Unsupported algo: $1" ;;
56+
esac
57+
}
58+
59+
60+
# atomic file copy <src> <dst>
61+
atomic_install_copy()
62+
{
63+
local src="$1" dst="$2" dstdir tmp
64+
dstdir="$(dirname -- "$dst")"
65+
tmp="$(mktemp -p "$dstdir" ".fetch.install.XXXXXX")"
66+
cp -f -- "$src" "$tmp"
67+
mv -f -- "$tmp" "$dst"
68+
}
69+
70+
71+
# Two-pass approach:
72+
# pass 1 tries cache against any candidate digest
73+
# pass 2 downloads per candidate
74+
target_name=""
75+
76+
77+
# Pass 1: determine target, verify cache against all candidate digests
78+
cache_path=""
79+
80+
while IFS= read -r line || [[ -n "$line" ]]; do
81+
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
82+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
83+
84+
set -- $line
85+
name="${1:-}"; algo_hex="${2:-}"; src="${3:-}"
86+
[[ -n "$name" && -n "$algo_hex" && -n "$src" ]] || continue
87+
88+
if [[ -z "$target_name" ]]; then
89+
target_name="$name"
90+
cache_path="${cache_dir}/${target_name}"
91+
fi
92+
[[ "$name" == "$target_name" ]] || continue
93+
94+
algo="${algo_hex%%:*}"
95+
expected="${algo_hex#*:}"
96+
expected="${expected,,}"
97+
if [[ "$algo" == "$expected" || -z "$expected" ]]; then
98+
die "Malformed metadata for $target_name: expected '<algo>:<hex>'"
99+
fi
100+
101+
if [[ -f "$cache_path" ]]; then
102+
got="$(digest "$algo" "$cache_path")"
103+
if [[ "${got,,}" == "$expected" ]]; then
104+
atomic_install_copy "$cache_path" "$out_path"
105+
msg "Using verified cache: $cache_path -> $out_path"
106+
exit 0
107+
fi
108+
fi
109+
done < "${meta}"
110+
111+
[[ -n "${target_name}" ]] || die "No metadata entries in $meta"
112+
113+
114+
# Pass 2: download each candidate and verify with its own expected digest
115+
while IFS= read -r line || [[ -n "${line}" ]]; do
116+
[[ "${line}" =~ ^[[:space:]]*$ ]] && continue
117+
[[ "${line}" =~ ^[[:space:]]*# ]] && continue
118+
119+
set -- $line
120+
name="${1:-}"; algo_hex="${2:-}"; src="${3:-}"
121+
[[ -n "$name" && -n "$algo_hex" && -n "$src" ]] || continue
122+
[[ "$name" == "$target_name" ]] || continue
123+
124+
algo="${algo_hex%%:*}"
125+
expected="${algo_hex#*:}"
126+
expected="${expected,,}"
127+
if [[ "$algo" == "$expected" || -z "$expected" ]]; then
128+
die "Malformed metadata for $target_name: expected '<algo>:<hex>'"
129+
fi
130+
131+
tmp_cache="$(mktemp -p "$cache_dir" ".fetch.${target_name}.XXXXXX")"
132+
if [[ "$src" == http://* || "$src" == https://* ]]; then
133+
if ! curl --fail --location --silent --show-error --retry 3 --retry-delay 2 \
134+
--connect-timeout 30 --output "$tmp_cache" "$src"; then
135+
rm -f -- "$tmp_cache"
136+
continue
137+
fi
138+
else
139+
if ! cp -f -- "$src" "$tmp_cache"; then
140+
rm -f -- "$tmp_cache"
141+
continue
142+
fi
143+
fi
144+
145+
got="$(digest "$algo" "$tmp_cache")"
146+
if [[ "${got,,}" != "$expected" ]]; then
147+
err "Checksum mismatch for ${target_name} from ${src}"
148+
rm -f -- "${tmp_cache}"
149+
continue
150+
fi
151+
152+
atomic_install_copy "$tmp_cache" "$out_path"
153+
mv -f -- "$tmp_cache" "$cache_path"
154+
msg "Fetched: $src -> $out_path cached at $cache_path"
155+
exit 0
156+
done < "${meta}"
157+
158+
159+
die "Failed: no verified source succeeded for ${target_name}"

depends

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,44 @@
1-
realpath:coreutils
2-
zip
3-
mkdosfs:dosfstools
4-
mke2fs:e2fsprogs
5-
grep
6-
rsync
7-
curl
8-
genimage
9-
mtools
10-
mmdebstrap
11-
bdebstrap
12-
podman
13-
zstd
14-
pv
15-
newuidmap:uidmap
16-
python-is-python3
17-
dbus-user-session
18-
btrfs-progs
19-
dctrl-tools
20-
uuidgen:uuid-runtime
21-
fdisk
22-
python3-yaml
23-
python3-debian
24-
python3-jsonschema
25-
# doc gen only
1+
all:realpath:coreutils
2+
all:bash
3+
all:mmdebstrap
4+
all:podman
5+
all:newuidmap:uidmap
6+
all::python-is-python3
7+
all::dbus-user-session
8+
9+
bootstrap::python3-yaml
10+
bootstrap::python3-debian
11+
12+
# image build essential
13+
build:zip
14+
build:mkdosfs:dosfstools
15+
build:mke2fs:e2fsprogs
16+
build:grep
17+
build:rsync
18+
build:curl
19+
build:mtools
20+
build:zstd
21+
build:pv
22+
build:mkfs.btrfs:btrfs-progs
23+
build::dctrl-tools
24+
build:uuidgen:uuid-runtime
25+
build:fdisk
26+
build::python3-jsonschema
27+
build::dpkg-dev
28+
29+
# pkg buildsys
30+
build::python3-pip
31+
build:make
32+
build::build-essential
33+
build:autoconf
34+
build:automake
35+
build::libtool
36+
build:autopoint
37+
build:flex
38+
build:gettext
39+
build:pkg-config
40+
41+
# maintainer only (docgen)
2642
# python3-markdown
2743
# asciidoctor
2844
# python3-jinja2

docs/config/index.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ rpi-image-gen provides a flexible, hierarchical approach to managing build varia
1515

1616
=== Variable Naming Convention
1717

18-
All configuration variables in rpi-image-gen use the `IGconf_` prefix followed by a structured naming pattern:
18+
Excluding environment keys, configuration variables in rpi-image-gen use the `IGconf_` prefix followed by a structured naming pattern:
1919

2020
[source]
2121
----
@@ -101,6 +101,9 @@ YAML provides the most flexible configuration format with native support for hie
101101
include:
102102
file: base.yaml
103103
104+
env:
105+
MYVAR: UNCHANGED
106+
104107
device:
105108
class: pi5
106109
storage_type: sd
@@ -120,6 +123,9 @@ INI format provides traditional section-based configuration:
120123
----
121124
!include base.cfg
122125
126+
[env]
127+
MYVAR = UNCHANGED
128+
123129
[device]
124130
class = pi5
125131
storage_type = sd

docs/config/index.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ <h2 id="_core_architecture"><a class="anchor" href="#_core_architecture"></a><a
234234
<div class="sect2">
235235
<h3 id="_variable_naming_convention"><a class="anchor" href="#_variable_naming_convention"></a><a class="link" href="#_variable_naming_convention">Variable Naming Convention</a></h3>
236236
<div class="paragraph">
237-
<p>All configuration variables in rpi-image-gen use the <code>IGconf_</code> prefix followed by a structured naming pattern:</p>
237+
<p>Excluding environment keys, configuration variables in rpi-image-gen use the <code>IGconf_</code> prefix followed by a structured naming pattern:</p>
238238
</div>
239239
<div class="listingblock">
240240
<div class="content">
@@ -357,6 +357,9 @@ <h3 id="_yaml_configuration_format"><a class="anchor" href="#_yaml_configuration
357357
<pre class="highlight"><code class="language-yaml" data-lang="yaml">include:
358358
file: base.yaml
359359

360+
env:
361+
MYVAR: UNCHANGED
362+
360363
device:
361364
class: pi5
362365
storage_type: sd
@@ -378,6 +381,9 @@ <h3 id="_ini_configuration_format"><a class="anchor" href="#_ini_configuration_f
378381
<div class="content">
379382
<pre class="highlight"><code class="language-ini" data-lang="ini">!include base.cfg
380383

384+
[env]
385+
MYVAR = UNCHANGED
386+
381387
[device]
382388
class = pi5
383389
storage_type = sd

docs/layer/sys-build-base.html

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,40 @@ <h2>Configuration Variables</h2>
233233
</td>
234234
</tr>
235235

236+
<tr>
237+
<td><code>IGconf_sys_workroot</code></td>
238+
<td>The root work directory. Depending on the system(s)
239+
being built, this directory can amount to a substantial amount of consumed
240+
disk space.</td>
241+
<td>
242+
243+
244+
<code class="long-default">$(realpath --canonicalize-missing ./work)</code>
245+
246+
247+
</td>
248+
<td>Non-empty string value</td>
249+
<td>
250+
<a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
251+
</td>
252+
</tr>
253+
254+
<tr>
255+
<td><code>IGconf_sys_cachedir</code></td>
256+
<td>Global cache root directory for all operations.</td>
257+
<td>
258+
259+
260+
<code class="long-default">${IGconf_sys_workroot}/cache</code>
261+
262+
263+
</td>
264+
<td>Non-empty string value</td>
265+
<td>
266+
<a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
267+
</td>
268+
</tr>
269+
236270
<tr>
237271
<td><code>IGconf_sys_apt_cachedir</code></td>
238272
<td>Cache directory for APT operations.
@@ -254,14 +288,30 @@ <h2>Configuration Variables</h2>
254288
</tr>
255289

256290
<tr>
257-
<td><code>IGconf_sys_workroot</code></td>
258-
<td>The root work directory. Depending on the system(s)
259-
being built, this directory can amount to a substantial amount of consumed
260-
disk space.</td>
291+
<td><code>IGconf_sys_buildroot</code></td>
292+
<td>Global build root directory for source builds.</td>
261293
<td>
262294

263295

264-
<code class="long-default">$(realpath --canonicalize-missing ./work)</code>
296+
<code class="long-default">${IGconf_sys_workroot}/build</code>
297+
298+
299+
</td>
300+
<td>Non-empty string value</td>
301+
<td>
302+
<a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
303+
</td>
304+
</tr>
305+
306+
<tr>
307+
<td><code>IGconf_sys_bootstrapdir</code></td>
308+
<td>Directory for bootstrap artefacts (e.g. env, layer
309+
order, metadata) used to resume the build in a containerised (or later)
310+
invocation.</td>
311+
<td>
312+
313+
314+
<code class="long-default">${IGconf_sys_workroot}/bootstrap</code>
265315

266316

267317
</td>

docs/provisioning/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ IDP requires fastboot to be installed in the client. On Debian systems, this can
4646

4747
[WARNING]
4848
====
49-
Older versions of `genimage` can be problematic when creating sparse images. For example, `genimage` v16 currently shipping in Debian Bookworm does not create usable sparse images of _vfat_ filesystems. Raspberry Pi provisioning tools such as `rpi-sb-provisioner` (https://github.com/raspberrypi/rpi-sb-provisioner) rely on sparse image format, so it is recommended to use the most up-to-date version of `genimage` as possible (e.g. manual build and install to `/usr/local/bin/genimage`).
49+
Raspberry Pi provisioning tools such as `rpi-sb-provisioner` (https://github.com/raspberrypi/rpi-sb-provisioner) rely on sparse images, so rpi-image-gen ships its own up-to-date `genimage` to avoid compatibility problems seen with older distribution versions.
5050
====
5151

5252

0 commit comments

Comments
 (0)