summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-02-10 04:33:48 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-10 21:04:20 +0000
commit6f53d7763906f24d86e89da4da744421c52d591c (patch)
treef37ff082e424b1e4aaa820c8e2db07b31146f091
parenta71e7b0499c51c74686e41e7810e2f202d851ce6 (diff)
downloadmeta-virtualization-6f53d7763906f24d86e89da4da744421c52d591c.tar.gz
container-yocto-builder: add Yocto build container with systemd
Multi-layer OCI container image that can compile the Yocto Project. Three layers: systemd-base, build-tools, yocto-extras. Features CROPS-style dynamic user creation matching /workdir volume owner UID/GID. Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--recipes-core/packagegroups/packagegroup-yocto-builder.bb124
-rw-r--r--recipes-extended/builder-container-config/builder-container-config_1.0.bb23
-rw-r--r--recipes-extended/builder-container-config/files/builder-entry.sh48
-rw-r--r--recipes-extended/images/container-yocto-builder.bb37
4 files changed, 232 insertions, 0 deletions
diff --git a/recipes-core/packagegroups/packagegroup-yocto-builder.bb b/recipes-core/packagegroups/packagegroup-yocto-builder.bb
new file mode 100644
index 00000000..f9cf8992
--- /dev/null
+++ b/recipes-core/packagegroups/packagegroup-yocto-builder.bb
@@ -0,0 +1,124 @@
1SUMMARY = "Packages for a self-hosting Yocto build container"
2DESCRIPTION = "Build tools required to compile the Yocto Project. \
3 Based on packagegroup-self-hosted but without x11/opengl requirements."
4LICENSE = "MIT"
5LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
6
7PACKAGE_ARCH = "${TUNE_PKGARCH}"
8
9inherit packagegroup
10
11PACKAGES = "\
12 packagegroup-yocto-builder \
13 packagegroup-yocto-builder-base \
14 packagegroup-yocto-builder-toolchain \
15 packagegroup-yocto-builder-extras \
16"
17
18RDEPENDS:packagegroup-yocto-builder = "\
19 packagegroup-yocto-builder-base \
20 packagegroup-yocto-builder-toolchain \
21 packagegroup-yocto-builder-extras \
22"
23
24# Layer 1: systemd core + container basics
25RDEPENDS:packagegroup-yocto-builder-base = "\
26 base-files \
27 base-passwd \
28 netbase \
29 systemd \
30 dbus \
31 bash \
32 coreutils \
33 packagegroup-core-base-utils \
34 packagegroup-core-ssh-openssh \
35 container-systemd-config \
36 builder-container-config \
37 iproute2 \
38 iputils-ping \
39"
40
41# Layer 2: compiler toolchain + dev tools
42RDEPENDS:packagegroup-yocto-builder-toolchain = "\
43 autoconf \
44 automake \
45 binutils \
46 binutils-symlinks \
47 ccache \
48 cpp \
49 cpp-symlinks \
50 gcc \
51 gcc-symlinks \
52 g++ \
53 g++-symlinks \
54 make \
55 libtool \
56 pkgconfig \
57 ldd \
58 less \
59 file \
60 findutils \
61 quilt \
62 sed \
63 libstdc++ \
64 libstdc++-dev \
65 diffutils \
66 git \
67 git-perltools \
68 python3 \
69 python3-modules \
70 perl \
71 perl-modules \
72 perl-dev \
73 perl-misc \
74 perl-pod \
75 perl-module-re \
76 perl-module-text-wrap \
77 patch \
78 gawk \
79 grep \
80 tar \
81 gzip \
82 bzip2 \
83 xz \
84 zstd \
85 rpcsvc-proto \
86 ${@bb.utils.contains('TCLIBC', 'glibc', 'glibc-utils', '', d)} \
87"
88
89# Layer 3: Yocto-specific tools + utilities
90RDEPENDS:packagegroup-yocto-builder-extras = "\
91 python3-jinja2 \
92 python3-pexpect \
93 python3-pip \
94 python3-git \
95 lz4 \
96 chrpath \
97 diffstat \
98 texinfo \
99 socat \
100 cpio \
101 unzip \
102 zip \
103 wget \
104 curl \
105 tmux \
106 screen \
107 sudo \
108 ${@bb.utils.contains('TCLIBC', 'glibc', 'pseudo', '', d)} \
109 gdb \
110 rsync \
111 strace \
112 openssl \
113 openssh-scp \
114 openssh-sftp-server \
115 ${@bb.utils.contains('TCLIBC', 'glibc', 'glibc-localedata-en-us glibc-gconv-ibm850', '', d)} \
116 rpm \
117 opkg \
118 opkg-utils \
119 elfutils \
120 readline \
121 ncurses \
122 ncurses-terminfo-base \
123 subversion \
124"
diff --git a/recipes-extended/builder-container-config/builder-container-config_1.0.bb b/recipes-extended/builder-container-config/builder-container-config_1.0.bb
new file mode 100644
index 00000000..6b023caa
--- /dev/null
+++ b/recipes-extended/builder-container-config/builder-container-config_1.0.bb
@@ -0,0 +1,23 @@
1SUMMARY = "Entrypoint and user configuration for Yocto builder container"
2DESCRIPTION = "CROPS-style entrypoint script that creates a builder user \
3 matching the /workdir mount owner, then hands off to systemd."
4LICENSE = "MIT"
5LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
6
7SRC_URI = "file://builder-entry.sh"
8
9S = "${UNPACKDIR}"
10RDEPENDS:${PN} = "bash sudo shadow"
11
12inherit allarch
13
14do_install() {
15 # Entrypoint script (CROPS-style user creation -> exec /sbin/init)
16 install -d ${D}${bindir}
17 install -m 0755 ${UNPACKDIR}/builder-entry.sh ${D}${bindir}/
18
19 # Create /workdir mount point
20 install -d ${D}/workdir
21}
22
23FILES:${PN} = "${bindir}/builder-entry.sh /workdir"
diff --git a/recipes-extended/builder-container-config/files/builder-entry.sh b/recipes-extended/builder-container-config/files/builder-entry.sh
new file mode 100644
index 00000000..a23d8dae
--- /dev/null
+++ b/recipes-extended/builder-container-config/files/builder-entry.sh
@@ -0,0 +1,48 @@
1#!/bin/bash
2# builder-entry.sh - Container entrypoint for Yocto builder
3# Creates a 'builder' user matching the /workdir owner's UID/GID,
4# then exec's /sbin/init (systemd).
5#
6# Usage:
7# docker run --privileged -v /home/user/yocto:/workdir builder-image
8# docker run --privileged -e BUILDER_UID=1000 -e BUILDER_GID=1000 builder-image
9
10WORKDIR="/workdir"
11DEFAULT_UID=1000
12DEFAULT_GID=1000
13
14# Determine UID/GID
15if [ -n "$BUILDER_UID" ] && [ -n "$BUILDER_GID" ]; then
16 TARGET_UID="$BUILDER_UID"
17 TARGET_GID="$BUILDER_GID"
18elif [ -d "$WORKDIR" ] && [ "$(stat -c %u "$WORKDIR")" != "0" ]; then
19 TARGET_UID=$(stat -c %u "$WORKDIR")
20 TARGET_GID=$(stat -c %g "$WORKDIR")
21else
22 TARGET_UID=$DEFAULT_UID
23 TARGET_GID=$DEFAULT_GID
24fi
25
26# Refuse UID/GID 0 (root)
27[ "$TARGET_UID" = "0" ] && TARGET_UID=$DEFAULT_UID
28[ "$TARGET_GID" = "0" ] && TARGET_GID=$DEFAULT_GID
29
30# Create group and user if they don't exist
31if ! getent group builder >/dev/null 2>&1; then
32 groupadd -g "$TARGET_GID" builder 2>/dev/null || groupadd builder
33fi
34if ! getent passwd builder >/dev/null 2>&1; then
35 useradd -m -u "$TARGET_UID" -g builder -s /bin/bash builder
36fi
37
38# Ensure /workdir is accessible
39[ -d "$WORKDIR" ] && chown builder:builder "$WORKDIR"
40
41# Grant passwordless sudo
42if [ -d /etc/sudoers.d ]; then
43 echo "builder ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/builder
44 chmod 0440 /etc/sudoers.d/builder
45fi
46
47# Hand off to systemd
48exec /sbin/init "$@"
diff --git a/recipes-extended/images/container-yocto-builder.bb b/recipes-extended/images/container-yocto-builder.bb
new file mode 100644
index 00000000..0dba4c31
--- /dev/null
+++ b/recipes-extended/images/container-yocto-builder.bb
@@ -0,0 +1,37 @@
1SUMMARY = "Yocto Project builder container with systemd"
2DESCRIPTION = "A self-hosting Yocto build container. Includes compiler \
3 toolchain, Python 3, Git, and all tools needed to compile the Yocto \
4 Project. Uses systemd init and supports CROPS-style dynamic user \
5 creation for volume-mounted builds."
6LICENSE = "MIT"
7LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
8
9IMAGE_FSTYPES = "container oci"
10inherit image
11inherit image-oci
12
13# Multi-layer OCI image
14OCI_LAYER_MODE = "multi"
15OCI_LAYERS = "\
16 systemd-base:packages:packagegroup-yocto-builder-base \
17 build-tools:packages:packagegroup-yocto-builder-toolchain \
18 yocto-extras:packages:packagegroup-yocto-builder-extras \
19"
20
21# Entrypoint: user setup script -> systemd
22OCI_IMAGE_ENTRYPOINT = "/usr/bin/builder-entry.sh"
23
24# OCI metadata
25OCI_IMAGE_AUTHOR ?= "meta-virtualization"
26OCI_IMAGE_TAG ?= "latest"
27
28# All packages listed here to trigger builds (multi-layer requirement)
29IMAGE_INSTALL = "packagegroup-yocto-builder"
30
31# No kernel needed for container
32IMAGE_CONTAINER_NO_DUMMY = "1"
33
34# Minimize image
35IMAGE_FEATURES = ""
36IMAGE_LINGUAS = ""
37NO_RECOMMENDATIONS = "1"