diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-10 04:33:48 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-10 21:04:20 +0000 |
| commit | 6f53d7763906f24d86e89da4da744421c52d591c (patch) | |
| tree | f37ff082e424b1e4aaa820c8e2db07b31146f091 | |
| parent | a71e7b0499c51c74686e41e7810e2f202d851ce6 (diff) | |
| download | meta-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>
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 @@ | |||
| 1 | SUMMARY = "Packages for a self-hosting Yocto build container" | ||
| 2 | DESCRIPTION = "Build tools required to compile the Yocto Project. \ | ||
| 3 | Based on packagegroup-self-hosted but without x11/opengl requirements." | ||
| 4 | LICENSE = "MIT" | ||
| 5 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | ||
| 6 | |||
| 7 | PACKAGE_ARCH = "${TUNE_PKGARCH}" | ||
| 8 | |||
| 9 | inherit packagegroup | ||
| 10 | |||
| 11 | PACKAGES = "\ | ||
| 12 | packagegroup-yocto-builder \ | ||
| 13 | packagegroup-yocto-builder-base \ | ||
| 14 | packagegroup-yocto-builder-toolchain \ | ||
| 15 | packagegroup-yocto-builder-extras \ | ||
| 16 | " | ||
| 17 | |||
| 18 | RDEPENDS: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 | ||
| 25 | RDEPENDS: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 | ||
| 42 | RDEPENDS: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 | ||
| 90 | RDEPENDS: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 @@ | |||
| 1 | SUMMARY = "Entrypoint and user configuration for Yocto builder container" | ||
| 2 | DESCRIPTION = "CROPS-style entrypoint script that creates a builder user \ | ||
| 3 | matching the /workdir mount owner, then hands off to systemd." | ||
| 4 | LICENSE = "MIT" | ||
| 5 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | ||
| 6 | |||
| 7 | SRC_URI = "file://builder-entry.sh" | ||
| 8 | |||
| 9 | S = "${UNPACKDIR}" | ||
| 10 | RDEPENDS:${PN} = "bash sudo shadow" | ||
| 11 | |||
| 12 | inherit allarch | ||
| 13 | |||
| 14 | do_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 | |||
| 23 | FILES:${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 | |||
| 10 | WORKDIR="/workdir" | ||
| 11 | DEFAULT_UID=1000 | ||
| 12 | DEFAULT_GID=1000 | ||
| 13 | |||
| 14 | # Determine UID/GID | ||
| 15 | if [ -n "$BUILDER_UID" ] && [ -n "$BUILDER_GID" ]; then | ||
| 16 | TARGET_UID="$BUILDER_UID" | ||
| 17 | TARGET_GID="$BUILDER_GID" | ||
| 18 | elif [ -d "$WORKDIR" ] && [ "$(stat -c %u "$WORKDIR")" != "0" ]; then | ||
| 19 | TARGET_UID=$(stat -c %u "$WORKDIR") | ||
| 20 | TARGET_GID=$(stat -c %g "$WORKDIR") | ||
| 21 | else | ||
| 22 | TARGET_UID=$DEFAULT_UID | ||
| 23 | TARGET_GID=$DEFAULT_GID | ||
| 24 | fi | ||
| 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 | ||
| 31 | if ! getent group builder >/dev/null 2>&1; then | ||
| 32 | groupadd -g "$TARGET_GID" builder 2>/dev/null || groupadd builder | ||
| 33 | fi | ||
| 34 | if ! getent passwd builder >/dev/null 2>&1; then | ||
| 35 | useradd -m -u "$TARGET_UID" -g builder -s /bin/bash builder | ||
| 36 | fi | ||
| 37 | |||
| 38 | # Ensure /workdir is accessible | ||
| 39 | [ -d "$WORKDIR" ] && chown builder:builder "$WORKDIR" | ||
| 40 | |||
| 41 | # Grant passwordless sudo | ||
| 42 | if [ -d /etc/sudoers.d ]; then | ||
| 43 | echo "builder ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/builder | ||
| 44 | chmod 0440 /etc/sudoers.d/builder | ||
| 45 | fi | ||
| 46 | |||
| 47 | # Hand off to systemd | ||
| 48 | exec /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 @@ | |||
| 1 | SUMMARY = "Yocto Project builder container with systemd" | ||
| 2 | DESCRIPTION = "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." | ||
| 6 | LICENSE = "MIT" | ||
| 7 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | ||
| 8 | |||
| 9 | IMAGE_FSTYPES = "container oci" | ||
| 10 | inherit image | ||
| 11 | inherit image-oci | ||
| 12 | |||
| 13 | # Multi-layer OCI image | ||
| 14 | OCI_LAYER_MODE = "multi" | ||
| 15 | OCI_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 | ||
| 22 | OCI_IMAGE_ENTRYPOINT = "/usr/bin/builder-entry.sh" | ||
| 23 | |||
| 24 | # OCI metadata | ||
| 25 | OCI_IMAGE_AUTHOR ?= "meta-virtualization" | ||
| 26 | OCI_IMAGE_TAG ?= "latest" | ||
| 27 | |||
| 28 | # All packages listed here to trigger builds (multi-layer requirement) | ||
| 29 | IMAGE_INSTALL = "packagegroup-yocto-builder" | ||
| 30 | |||
| 31 | # No kernel needed for container | ||
| 32 | IMAGE_CONTAINER_NO_DUMMY = "1" | ||
| 33 | |||
| 34 | # Minimize image | ||
| 35 | IMAGE_FEATURES = "" | ||
| 36 | IMAGE_LINGUAS = "" | ||
| 37 | NO_RECOMMENDATIONS = "1" | ||
