From f51dab51db288dd492f91f56b47713bb619924ee Mon Sep 17 00:00:00 2001 From: Marvin Friedrich Date: Tue, 2 Jun 2026 21:38:47 +0200 Subject: [PATCH] shit --- Dockerfile | 5 + recipes/base-files/files/etc/fstab | 7 + recipes/base-files/files/etc/group | 12 ++ recipes/base-files/files/etc/hostname | 1 + recipes/base-files/files/etc/ld.so.conf | 3 + recipes/base-files/files/etc/os-release | 5 + recipes/base-files/files/etc/passwd | 3 + recipes/base-files/files/etc/profile | 9 ++ .../base-files/files/etc/profile.d/weston.sh | 12 ++ recipes/base-files/files/etc/shadow | 3 + .../files/etc/xdg/weston/weston.ini | 16 +++ recipes/base-files/recipe.py | 41 ++++++ recipes/brotli.py | 15 ++ recipes/cairo.py | 22 +++ recipes/dinit/files/dinit.d/boot | 9 ++ recipes/dinit/files/dinit.d/early-fs | 4 + .../dinit/files/dinit.d/scripts/early-fs.sh | 17 +++ .../files/dinit.d/scripts/udev-trigger.sh | 14 ++ recipes/dinit/files/dinit.d/seatd | 6 + recipes/dinit/files/dinit.d/tty1 | 7 + recipes/dinit/files/dinit.d/udev-trigger | 5 + recipes/dinit/files/dinit.d/udevd | 5 + recipes/dinit/recipe.py | 51 +++++++ recipes/elfutils.py | 39 ++++++ recipes/eudev.py | 22 +++ recipes/expat.py | 20 +++ recipes/fontconfig.py | 19 +++ recipes/freetype2.py | 23 ++++ recipes/fribidi.py | 18 +++ recipes/glib2.py | 25 ++++ recipes/harfbuzz.py | 26 ++++ recipes/hwdata.py | 22 +++ recipes/kmod.py | 23 ++++ recipes/libcap.py | 40 ++++++ recipes/libdisplay-info.py | 32 +++++ recipes/libdrm.py | 31 +++++ recipes/libevdev.py | 18 +++ recipes/libffi.py | 16 +++ recipes/libglvnd.py | 22 +++ recipes/libinput.py | 22 +++ recipes/libjpeg-turbo.py | 19 +++ recipes/libpciaccess.py | 13 ++ recipes/libpng.py | 13 ++ recipes/libxcrypt.py | 23 ++++ recipes/libxkbcommon.py | 21 +++ recipes/libxml2.py | 21 +++ recipes/linux/recipe.py | 30 ++++ recipes/llvm.py | 72 ++++++++++ recipes/mesa.py | 56 ++++++++ recipes/mtdev.py | 13 ++ recipes/pango.py | 28 ++++ recipes/pcre2.py | 19 +++ recipes/pixman.py | 19 +++ recipes/seatd.py | 23 ++++ recipes/shadow.py | 33 +++++ recipes/ttf-dejavu.py | 18 +++ recipes/util-linux.py | 58 ++++++++ recipes/wayland-protocols.py | 16 +++ recipes/wayland.py | 23 ++++ recipes/weston.py | 58 ++++++++ recipes/xkeyboard-config.py | 16 +++ recipes/zlib.py | 2 +- recipes/zstd.py | 29 ++++ scripts/mkimage.sh | 128 ++++++++++++++++++ src/builder.py | 66 +++++++-- src/cli.py | 14 +- src/context.py | 11 ++ src/fetch.py | 17 ++- src/lib/meson.py | 95 +++++++++++++ src/recipe.py | 9 +- 70 files changed, 1632 insertions(+), 21 deletions(-) create mode 100644 recipes/base-files/files/etc/fstab create mode 100644 recipes/base-files/files/etc/group create mode 100644 recipes/base-files/files/etc/hostname create mode 100644 recipes/base-files/files/etc/ld.so.conf create mode 100644 recipes/base-files/files/etc/os-release create mode 100644 recipes/base-files/files/etc/passwd create mode 100644 recipes/base-files/files/etc/profile create mode 100644 recipes/base-files/files/etc/profile.d/weston.sh create mode 100644 recipes/base-files/files/etc/shadow create mode 100644 recipes/base-files/files/etc/xdg/weston/weston.ini create mode 100644 recipes/base-files/recipe.py create mode 100644 recipes/brotli.py create mode 100644 recipes/cairo.py create mode 100644 recipes/dinit/files/dinit.d/boot create mode 100644 recipes/dinit/files/dinit.d/early-fs create mode 100644 recipes/dinit/files/dinit.d/scripts/early-fs.sh create mode 100644 recipes/dinit/files/dinit.d/scripts/udev-trigger.sh create mode 100644 recipes/dinit/files/dinit.d/seatd create mode 100644 recipes/dinit/files/dinit.d/tty1 create mode 100644 recipes/dinit/files/dinit.d/udev-trigger create mode 100644 recipes/dinit/files/dinit.d/udevd create mode 100644 recipes/dinit/recipe.py create mode 100644 recipes/elfutils.py create mode 100644 recipes/eudev.py create mode 100644 recipes/expat.py create mode 100644 recipes/fontconfig.py create mode 100644 recipes/freetype2.py create mode 100644 recipes/fribidi.py create mode 100644 recipes/glib2.py create mode 100644 recipes/harfbuzz.py create mode 100644 recipes/hwdata.py create mode 100644 recipes/kmod.py create mode 100644 recipes/libcap.py create mode 100644 recipes/libdisplay-info.py create mode 100644 recipes/libdrm.py create mode 100644 recipes/libevdev.py create mode 100644 recipes/libffi.py create mode 100644 recipes/libglvnd.py create mode 100644 recipes/libinput.py create mode 100644 recipes/libjpeg-turbo.py create mode 100644 recipes/libpciaccess.py create mode 100644 recipes/libpng.py create mode 100644 recipes/libxcrypt.py create mode 100644 recipes/libxkbcommon.py create mode 100644 recipes/libxml2.py create mode 100644 recipes/llvm.py create mode 100644 recipes/mesa.py create mode 100644 recipes/mtdev.py create mode 100644 recipes/pango.py create mode 100644 recipes/pcre2.py create mode 100644 recipes/pixman.py create mode 100644 recipes/seatd.py create mode 100644 recipes/shadow.py create mode 100644 recipes/ttf-dejavu.py create mode 100644 recipes/util-linux.py create mode 100644 recipes/wayland-protocols.py create mode 100644 recipes/wayland.py create mode 100644 recipes/weston.py create mode 100644 recipes/xkeyboard-config.py create mode 100644 recipes/zstd.py create mode 100755 scripts/mkimage.sh diff --git a/Dockerfile b/Dockerfile index 0dfb869..50b0c6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ RUN apk add --no-cache \ zlib-dev \ git \ pkgconf \ + kmod \ patchelf \ gperf \ python3 \ @@ -50,6 +51,10 @@ RUN apk add --no-cache \ ninja \ meson \ glslang \ + py3-jinja2 \ + wayland-dev \ + wayland-protocols \ + glib-dev \ elfutils-dev \ libffi-dev \ expat-dev \ diff --git a/recipes/base-files/files/etc/fstab b/recipes/base-files/files/etc/fstab new file mode 100644 index 0000000..d2782e6 --- /dev/null +++ b/recipes/base-files/files/etc/fstab @@ -0,0 +1,7 @@ +# +proc /proc proc nosuid,noexec,nodev 0 0 +sysfs /sys sysfs nosuid,noexec,nodev 0 0 +devtmpfs /dev devtmpfs nosuid 0 0 +devpts /dev/pts devpts gid=5,mode=620 0 0 +tmpfs /run tmpfs nosuid,nodev 0 0 +tmpfs /dev/shm tmpfs nosuid,nodev 0 0 diff --git a/recipes/base-files/files/etc/group b/recipes/base-files/files/etc/group new file mode 100644 index 0000000..788ae5b --- /dev/null +++ b/recipes/base-files/files/etc/group @@ -0,0 +1,12 @@ +root:x:0: +tty:x:5:user +disk:x:6: +kmem:x:9: +wheel:x:10:user +kvm:x:24:user +video:x:27:user +input:x:28:user +seat:x:29:user +render:x:30:user +nobody:x:65534: +user:x:1000: diff --git a/recipes/base-files/files/etc/hostname b/recipes/base-files/files/etc/hostname new file mode 100644 index 0000000..fecadc0 --- /dev/null +++ b/recipes/base-files/files/etc/hostname @@ -0,0 +1 @@ +orchid diff --git a/recipes/base-files/files/etc/ld.so.conf b/recipes/base-files/files/etc/ld.so.conf new file mode 100644 index 0000000..3dd3872 --- /dev/null +++ b/recipes/base-files/files/etc/ld.so.conf @@ -0,0 +1,3 @@ +/lib +/usr/lib +/usr/lib64 diff --git a/recipes/base-files/files/etc/os-release b/recipes/base-files/files/etc/os-release new file mode 100644 index 0000000..3166f82 --- /dev/null +++ b/recipes/base-files/files/etc/os-release @@ -0,0 +1,5 @@ +NAME="Orchid" +PRETTY_NAME="Orchid Linux" +ID=orchid +ANSI_COLOR="0;35" +HOME_URL="https://example.invalid/" diff --git a/recipes/base-files/files/etc/passwd b/recipes/base-files/files/etc/passwd new file mode 100644 index 0000000..f724299 --- /dev/null +++ b/recipes/base-files/files/etc/passwd @@ -0,0 +1,3 @@ +root:x:0:0:root:/root:/bin/bash +user:x:1000:1000:Orchid User:/home/user:/bin/bash +nobody:x:65534:65534:Nobody:/:/usr/bin/nologin diff --git a/recipes/base-files/files/etc/profile b/recipes/base-files/files/etc/profile new file mode 100644 index 0000000..5be8b1c --- /dev/null +++ b/recipes/base-files/files/etc/profile @@ -0,0 +1,9 @@ +# System-wide shell profile (POSIX sh / bash login shells). +export PATH=/usr/local/bin:/usr/bin:/bin +export PAGER=less +umask 022 + +for _f in /etc/profile.d/*.sh; do + [ -r "$_f" ] && . "$_f" +done +unset _f diff --git a/recipes/base-files/files/etc/profile.d/weston.sh b/recipes/base-files/files/etc/profile.d/weston.sh new file mode 100644 index 0000000..83ca025 --- /dev/null +++ b/recipes/base-files/files/etc/profile.d/weston.sh @@ -0,0 +1,12 @@ +# Launch Weston automatically after autologin on the first VT. +# seatd (running as a service) grants access to DRM/input devices, and +# XDG_RUNTIME_DIR is created here since there is no logind to do it. +if [ "$(tty)" = "/dev/tty1" ] && [ -z "${WAYLAND_DISPLAY:-}" ] && [ -z "${DISPLAY:-}" ]; then + export XDG_RUNTIME_DIR="/run/user/$(id -u)" + if [ ! -d "$XDG_RUNTIME_DIR" ]; then + mkdir -p "$XDG_RUNTIME_DIR" + fi + chmod 0700 "$XDG_RUNTIME_DIR" + export XDG_SESSION_TYPE=wayland + exec weston +fi diff --git a/recipes/base-files/files/etc/shadow b/recipes/base-files/files/etc/shadow new file mode 100644 index 0000000..6bbeb25 --- /dev/null +++ b/recipes/base-files/files/etc/shadow @@ -0,0 +1,3 @@ +root:!:19700:0:99999:7::: +user::19700:0:99999:7::: +nobody:!:19700:0:99999:7::: diff --git a/recipes/base-files/files/etc/xdg/weston/weston.ini b/recipes/base-files/files/etc/xdg/weston/weston.ini new file mode 100644 index 0000000..4eedf36 --- /dev/null +++ b/recipes/base-files/files/etc/xdg/weston/weston.ini @@ -0,0 +1,16 @@ +[core] +idle-time=0 +require-input=false +xwayland=false + +[shell] +background-color=0xff1e1e2e +panel-position=top +locking=false + +[keyboard] +keymap_layout=us + +[terminal] +font=DejaVu Sans Mono +font-size=14 diff --git a/recipes/base-files/recipe.py b/recipes/base-files/recipe.py new file mode 100644 index 0000000..4a6bc3b --- /dev/null +++ b/recipes/base-files/recipe.py @@ -0,0 +1,41 @@ +version = "0.1" +revision = 1 +description = "Base filesystem layout, accounts and system configuration" +license = "MIT" +url = "https://example.invalid/" +# Config-only package: no upstream source, just static files + an install phase. +# Install the full target set (`orchid install `) to get a bootable system; +# this package supplies the glue (accounts, fstab, /sbin/init, weston.ini). +host_deps = [] +# bash provides the system shell; base-files points /usr/bin/sh at it. +deps = ["bash"] + + +def install(self): + d = self.dest_dir + self.run( + "sh", + "-c", + "set -e; " + f"install -d -m0755 '{d}/etc' '{d}/etc/profile.d' '{d}/etc/xdg/weston' " + f"'{d}/etc/udev/rules.d' '{d}/sbin' '{d}/usr/bin' '{d}/root' '{d}/home/user' " + f"'{d}/run' '{d}/var' '{d}/proc' '{d}/sys' '{d}/dev'; " + # POSIX shell for #!/bin/sh scripts (dinit services, etc.). + f"ln -sf bash '{d}/usr/bin/sh'; " + # Static config tree from files/. + f"cp -rp '{self.files}/etc/.' '{d}/etc/'; " + # dinit as PID 1 (boot with kernel cmdline init=/sbin/init). + f"ln -sf /usr/bin/dinit '{d}/sbin/init'; " + # Conventional compat symlinks: the ELF interpreter is /lib64/ld-linux, + # but glibc lives in /lib; programs/shells live in /usr/bin. + f"ln -sf lib '{d}/lib64'; " + f"ln -sf usr/bin '{d}/bin'; " + # /var/run -> /run (dinit binds its control socket at /var/run/dinitctl). + f"ln -sf /run '{d}/var/run'; " + # Ownership + sensitive perms. + f"chown -R 0:0 '{d}/etc' '{d}/root'; " + f"chmod 0600 '{d}/etc/shadow'; " + f"chmod 0700 '{d}/root'; " + f"chown -R 1000:1000 '{d}/home/user'; " + f"chmod 0755 '{d}/home/user'", + ) diff --git a/recipes/brotli.py b/recipes/brotli.py new file mode 100644 index 0000000..be235f0 --- /dev/null +++ b/recipes/brotli.py @@ -0,0 +1,15 @@ +version = "1.1.0" +revision = 1 +description = "Generic-purpose lossless compression algorithm (Brotli)" +license = "MIT" +url = "https://github.com/google/brotli" +source = tarball( + url=f"https://github.com/google/brotli/archive/refs/tags/v{version}.tar.gz", + sha256="e720a6ca29428b803f4ad165371771f5398faba397edf6778837a18599ea13ff", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = cmake( + configure_args=["-DBROTLI_DISABLE_TESTS=ON"], +) diff --git a/recipes/cairo.py b/recipes/cairo.py new file mode 100644 index 0000000..ebf1738 --- /dev/null +++ b/recipes/cairo.py @@ -0,0 +1,22 @@ +version = "1.18.4" +revision = 1 +description = "2D graphics library with multiple output backends" +license = "LGPL-2.1-only OR MPL-1.1" +url = "https://www.cairographics.org/" +source = tarball( + url=f"https://www.cairographics.org/releases/cairo-{version}.tar.xz", + sha256="445ed8208a6e4823de1226a74ca319d3600e83f6369f99b14265006599c32ccb", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "pixman", "freetype2", "fontconfig", "libpng", "glib2", "zlib"] + +configure, build, install = meson( + configure_args=[ + # Wayland-only: no X backends. (cairo 1.18 has no GL-backend option.) + "-Dxlib=disabled", + "-Dxcb=disabled", + "-Dtests=disabled", + "-Dspectre=disabled", + "-Dsymbol-lookup=disabled", # avoids a libbfd/binutils dependency + ], +) diff --git a/recipes/dinit/files/dinit.d/boot b/recipes/dinit/files/dinit.d/boot new file mode 100644 index 0000000..e9d39f0 --- /dev/null +++ b/recipes/dinit/files/dinit.d/boot @@ -0,0 +1,9 @@ +# Default boot target. dinit starts this service as PID 1; it pulls in the +# rest of the system. waits-for (rather than depends-on) keeps the machine +# coming up even if an individual service fails. +type = internal +waits-for = early-fs +waits-for = udevd +waits-for = udev-trigger +waits-for = seatd +waits-for = tty1 diff --git a/recipes/dinit/files/dinit.d/early-fs b/recipes/dinit/files/dinit.d/early-fs new file mode 100644 index 0000000..bf6cd97 --- /dev/null +++ b/recipes/dinit/files/dinit.d/early-fs @@ -0,0 +1,4 @@ +# Mount the early virtual filesystems needed before anything else runs. +type = scripted +command = /etc/dinit.d/scripts/early-fs.sh +restart = false diff --git a/recipes/dinit/files/dinit.d/scripts/early-fs.sh b/recipes/dinit/files/dinit.d/scripts/early-fs.sh new file mode 100644 index 0000000..4688bed --- /dev/null +++ b/recipes/dinit/files/dinit.d/scripts/early-fs.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Bring up the kernel virtual filesystems and runtime dirs before other +# services start. Safe to re-run: each mount is guarded by mountpoint checks. +set -eu +export PATH=/usr/bin:/usr/sbin:/bin:/sbin + +mountpoint -q /proc || mount -t proc proc /proc +mountpoint -q /sys || mount -t sysfs sysfs /sys +mountpoint -q /dev || mount -t devtmpfs devtmpfs /dev +mkdir -p /dev/pts /dev/shm +mountpoint -q /dev/pts || mount -t devpts devpts /dev/pts +mountpoint -q /dev/shm || mount -t tmpfs tmpfs /dev/shm +# NOTE: /run is intentionally NOT remounted here. dinit (PID 1) binds its +# control socket under /run before this script runs; on the all-RAM initramfs +# boot /run is already writable, and a fresh tmpfs mount would hide that socket. +# (A disk-rooted system should instead mount /run before starting dinit.) +mkdir -p /run/user /run/udev diff --git a/recipes/dinit/files/dinit.d/scripts/udev-trigger.sh b/recipes/dinit/files/dinit.d/scripts/udev-trigger.sh new file mode 100644 index 0000000..bce775a --- /dev/null +++ b/recipes/dinit/files/dinit.d/scripts/udev-trigger.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Coldplug devices that already existed before udevd started: replay "add" +# uevents for subsystems then devices, and wait for processing to finish so +# DRM/input nodes (and their autoloaded modules) are present before Weston. +set -eu +export PATH=/usr/bin:/usr/sbin:/bin:/sbin + +udevadm trigger --action=add --type=subsystems +udevadm trigger --action=add --type=devices +udevadm settle || true + +# Safety net for QEMU/virtio: ensure the virtio-gpu DRM driver is loaded even if +# coldplug autoload missed it. (Input/evdev is built into the kernel.) +modprobe virtio_gpu 2>/dev/null || true diff --git a/recipes/dinit/files/dinit.d/seatd b/recipes/dinit/files/dinit.d/seatd new file mode 100644 index 0000000..eb2d113 --- /dev/null +++ b/recipes/dinit/files/dinit.d/seatd @@ -0,0 +1,6 @@ +# Seat management daemon. Weston (via libseat) asks seatd to open DRM/input +# devices, so no logind is needed. The "seat" group may use the seatd socket. +type = process +command = /usr/bin/seatd -g seat +restart = true +depends-on = early-fs diff --git a/recipes/dinit/files/dinit.d/tty1 b/recipes/dinit/files/dinit.d/tty1 new file mode 100644 index 0000000..607aaf7 --- /dev/null +++ b/recipes/dinit/files/dinit.d/tty1 @@ -0,0 +1,7 @@ +# Autologin getty on tty1. login runs the user's profile, which launches Weston +# (see /etc/profile.d/weston.sh shipped by base-files). +type = process +command = /usr/bin/agetty --autologin user --noclear --login-program /usr/bin/login tty1 linux +restart = true +depends-on = seatd +depends-on = udev-trigger diff --git a/recipes/dinit/files/dinit.d/udev-trigger b/recipes/dinit/files/dinit.d/udev-trigger new file mode 100644 index 0000000..c926b50 --- /dev/null +++ b/recipes/dinit/files/dinit.d/udev-trigger @@ -0,0 +1,5 @@ +# Coldplug: replay add events so udev autoloads DRM/input modules, then settle. +type = scripted +command = /etc/dinit.d/scripts/udev-trigger.sh +restart = false +depends-on = udevd diff --git a/recipes/dinit/files/dinit.d/udevd b/recipes/dinit/files/dinit.d/udevd new file mode 100644 index 0000000..d003de6 --- /dev/null +++ b/recipes/dinit/files/dinit.d/udevd @@ -0,0 +1,5 @@ +# eudev device manager daemon (udevd). Loads/uevents DRM/input drivers. +type = process +command = /usr/bin/udevd +restart = true +depends-on = early-fs diff --git a/recipes/dinit/recipe.py b/recipes/dinit/recipe.py new file mode 100644 index 0000000..0dc2dbe --- /dev/null +++ b/recipes/dinit/recipe.py @@ -0,0 +1,51 @@ +version = "0.20.0" +revision = 1 +description = "Service manager / init system (dinit)" +license = "Apache-2.0" +url = "https://davmac.org/projects/dinit/" +source = tarball( + url=f"https://github.com/davmac314/dinit/archive/refs/tags/v{version}.tar.gz", + sha256="cd75b572a2eab4a9bd0610a2bb8cc154da7e80074e61cb1059a996dfd977baae", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "libstdc++"] + + +def configure(self): + # dinit's hand-written configure expects an in-tree build and reads the + # toolchain from the environment; CXX_FOR_BUILD compiles its small build-time + # helper with the native compiler. + self.run("cp", "-rp", f"{self.source_dir}/.", self.build_dir) + self.run( + "./configure", + "--prefix=/usr", + "--sbindir=/usr/bin", + "--enable-utmpx", + "--enable-shutdown", + "--disable-strip", + env={ + "CXX": f"{self.triple}-g++", + "CXX_FOR_BUILD": "g++", + # dinit 0.20.0 uses std::allocator::construct(), removed from the + # default C++ mode in GCC 16's libstdc++; build it as C++17 where it + # still exists. + "CXXFLAGS": self.options["cxxflags"] + " -std=gnu++17", + "LDFLAGS": self.options["ldflags"], + }, + ) + + +def build(self): + self.run("make", f"-j{self.jobs}") + + +def install(self): + self.run("make", "install", f"DESTDIR={self.dest_dir}") + # Ship the dinit service set (boot target + udev/seatd/agetty services). + self.run("install", "-d", self.dest_dir / "etc/dinit.d") + self.run( + "sh", + "-c", + f"cp -rp {self.files}/dinit.d/. {self.dest_dir}/etc/dinit.d/ && " + f"chmod +x {self.dest_dir}/etc/dinit.d/scripts/*.sh", + ) diff --git a/recipes/elfutils.py b/recipes/elfutils.py new file mode 100644 index 0000000..2f02aac --- /dev/null +++ b/recipes/elfutils.py @@ -0,0 +1,39 @@ +version = "0.192" +revision = 1 +description = "ELF object file access library and utilities (libelf)" +license = "GPL-3.0-or-later AND LGPL-3.0-or-later" +url = "https://sourceware.org/elfutils/" +source = tarball( + url=f"https://sourceware.org/elfutils/ftp/{version}/elfutils-{version}.tar.bz2", + sha256="616099beae24aba11f9b63d86ca6cc8d566d968b802391334c91df54eab416b4", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# libstdc++: elfutils builds a C++ tool (srcfiles) needing the C++ headers. +deps = [profile["libc"], "libstdc++", "zlib", "xz", "zstd"] + + +def configure(self): + # debuginfod would pull libcurl/microhttpd; we only need libelf for Mesa. + # elfutils builds with -Werror by default, which GCC 16 trips on. + autotools_configure( + self, + [ + "--disable-debuginfod", + "--disable-libdebuginfod", + "--disable-nls", + "--enable-deterministic-archives", + "--program-prefix=eu-", + ], + { + "CFLAGS": self.options["cflags"] + " -Wno-error", + "CXXFLAGS": self.options["cxxflags"] + " -Wno-error", + }, + ) + + +def build(self): + autotools_build(self) + + +def install(self): + autotools_install(self) diff --git a/recipes/eudev.py b/recipes/eudev.py new file mode 100644 index 0000000..80278e0 --- /dev/null +++ b/recipes/eudev.py @@ -0,0 +1,22 @@ +version = "3.2.14" +revision = 1 +description = "Standalone udev fork: device manager (udevd, udevadm, libudev)" +license = "GPL-2.0-only AND LGPL-2.1-or-later" +url = "https://github.com/eudev-project/eudev" +source = tarball( + url=f"https://github.com/eudev-project/eudev/releases/download/v{version}/eudev-{version}.tar.gz", + sha256="8da4319102f24abbf7fff5ce9c416af848df163b29590e666d334cc1927f006f", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# blkid (libblkid/util-linux) and kmod (libkmod) builtins are enabled by default. +deps = [profile["libc"], "kmod", "util-linux"] + +configure, build, install = autotools( + configure_args=[ + "--disable-manpages", + "--disable-introspection", + "--enable-hwdb", + # Keep daemon/rules/libexec under /usr (merged-usr layout). + "--with-rootprefix=/usr", + ], +) diff --git a/recipes/expat.py b/recipes/expat.py new file mode 100644 index 0000000..f3e662f --- /dev/null +++ b/recipes/expat.py @@ -0,0 +1,20 @@ +version = "2.7.3" +revision = 1 +description = "Stream-oriented XML parser library (Expat)" +license = "MIT" +url = "https://libexpat.github.io/" +_tag = "R_" + version.replace(".", "_") +source = tarball( + url=f"https://github.com/libexpat/libexpat/releases/download/{_tag}/expat-{version}.tar.xz", + sha256="71df8f40706a7bb0a80a5367079ea75d91da4f8c65c58ec59bcdfbf7decdab9f", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = autotools( + configure_args=[ + "--without-docbook", + "--without-examples", + "--without-tests", + ], +) diff --git a/recipes/fontconfig.py b/recipes/fontconfig.py new file mode 100644 index 0000000..a667ac0 --- /dev/null +++ b/recipes/fontconfig.py @@ -0,0 +1,19 @@ +version = "2.16.2" +revision = 1 +description = "Library for configuring and customizing font access" +license = "MIT" +url = "https://www.freedesktop.org/wiki/Software/fontconfig/" +source = tarball( + url=f"https://gitlab.freedesktop.org/fontconfig/fontconfig/-/archive/{version}/fontconfig-{version}.tar.gz", + sha256="8b9368ae99d9faf2fe26491645fbeebf0272d911cacc5d3e9cf954c2157c151e", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "freetype2", "expat"] + +configure, build, install = meson( + configure_args=[ + "-Ddoc=disabled", + "-Dtests=disabled", + "-Dtools=enabled", + ], +) diff --git a/recipes/freetype2.py b/recipes/freetype2.py new file mode 100644 index 0000000..3b9ccb8 --- /dev/null +++ b/recipes/freetype2.py @@ -0,0 +1,23 @@ +version = "2.13.3" +revision = 1 +description = "FreeType font rendering library" +license = "FTL OR GPL-2.0-or-later" +url = "https://freetype.org/" +source = tarball( + url=f"https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.xz", + sha256="0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib", "libpng", "brotli"] + +configure, build, install = meson( + configure_args=[ + # First pass without HarfBuzz to break the freetype<->harfbuzz cycle; + # HarfBuzz is built afterwards and links back against this freetype. + "-Dharfbuzz=disabled", + "-Dbrotli=enabled", + "-Dbzip2=disabled", + "-Dpng=enabled", + "-Dzlib=enabled", + ], +) diff --git a/recipes/fribidi.py b/recipes/fribidi.py new file mode 100644 index 0000000..9820435 --- /dev/null +++ b/recipes/fribidi.py @@ -0,0 +1,18 @@ +version = "1.0.16" +revision = 1 +description = "Free Implementation of the Unicode Bidirectional Algorithm" +license = "LGPL-2.1-or-later" +url = "https://github.com/fribidi/fribidi" +source = tarball( + url=f"https://github.com/fribidi/fribidi/releases/download/v{version}/fribidi-{version}.tar.xz", + sha256="1b1cde5b235d40479e91be2f0e88a309e3214c8ab470ec8a2744d82a5a9ea05c", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = meson( + configure_args=[ + "-Dtests=false", + "-Ddocs=false", + ], +) diff --git a/recipes/glib2.py b/recipes/glib2.py new file mode 100644 index 0000000..8005def --- /dev/null +++ b/recipes/glib2.py @@ -0,0 +1,25 @@ +version = "2.84.2" +revision = 1 +description = "GLib core application building blocks (GLib/GObject/GIO)" +license = "LGPL-2.1-or-later" +url = "https://gitlab.gnome.org/GNOME/glib" +_series = ".".join(version.split(".")[:2]) +source = tarball( + url=f"https://download.gnome.org/sources/glib/{_series}/glib-{version}.tar.xz", + sha256="88e960dd937057407d61fcb3b45a860704b25923c37ae2478b85f2ecb5a4021f", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "libffi", "pcre2", "zlib"] + +configure, build, install = meson( + configure_args=[ + "-Dtests=false", + "-Dman-pages=disabled", + # gobject-introspection needs to run target binaries; skip when cross. + "-Dintrospection=disabled", + "-Dselinux=disabled", + # Avoid pulling util-linux (libmount) into the GIO build. + "-Dlibmount=disabled", + "-Dnls=enabled", + ], +) diff --git a/recipes/harfbuzz.py b/recipes/harfbuzz.py new file mode 100644 index 0000000..22f1f25 --- /dev/null +++ b/recipes/harfbuzz.py @@ -0,0 +1,26 @@ +version = "12.1.0" +revision = 1 +description = "OpenType text shaping engine" +license = "MIT" +url = "https://harfbuzz.github.io/" +source = tarball( + url=f"https://github.com/harfbuzz/harfbuzz/releases/download/{version}/harfbuzz-{version}.tar.xz", + sha256="e5c81b7f6e0b102dfb000cfa424538b8e896ab78a2f4b8a5ec8cae62ab43369e", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# C++ library: needs libstdc++ (its headers + runtime) in the sysroot. +deps = [profile["libc"], "libstdc++", "freetype2", "glib2", "fribidi"] + +configure, build, install = meson( + configure_args=[ + "-Dfreetype=enabled", + "-Dglib=enabled", + "-Dgobject=disabled", + "-Dicu=disabled", + # cairo support here would create a cycle (cairo is built after harfbuzz). + "-Dcairo=disabled", + "-Dtests=disabled", + "-Ddocs=disabled", + "-Dbenchmark=disabled", + ], +) diff --git a/recipes/hwdata.py b/recipes/hwdata.py new file mode 100644 index 0000000..695342c --- /dev/null +++ b/recipes/hwdata.py @@ -0,0 +1,22 @@ +version = "0.394" +revision = 1 +description = "Hardware identification data (pci.ids, usb.ids, ...)" +license = "GPL-2.0-or-later OR XFree86-1.1" +url = "https://github.com/vcrhonek/hwdata" +source = tarball( + url=f"https://github.com/vcrhonek/hwdata/archive/v{version}/hwdata-{version}.tar.gz", + sha256="b7c3fd7214a3b7c49d2661db127a712dc11cffd1799f793947aa1cb20aaf3298", +) +host_deps = ["pkgconf"] +# Pure data + a hwdata.pc; nothing links against it. +deps = [] + + +def configure(self): + # hwdata ships a hand-written configure that expects an in-tree build. + self.run("cp", "-rp", f"{self.source_dir}/.", self.build_dir) + self.run("./configure", "--prefix=/usr", "--disable-blacklist") + + +def install(self): + self.run("make", "install", f"DESTDIR={self.dest_dir}") diff --git a/recipes/kmod.py b/recipes/kmod.py new file mode 100644 index 0000000..1753789 --- /dev/null +++ b/recipes/kmod.py @@ -0,0 +1,23 @@ +version = "33" +revision = 1 +description = "Tools and library to handle kernel modules (modprobe/depmod/libkmod)" +license = "LGPL-2.1-or-later" +url = "https://github.com/kmod-project/kmod" +source = tarball( + url=f"https://www.kernel.org/pub/linux/utils/kernel/kmod/kmod-{version}.tar.xz", + sha256="dc768b3155172091f56dc69430b5481f2d76ecd9ccb54ead8c2540dbcf5ea9bc", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib", "xz", "zstd"] + +# The kernel.org release tarball ships the autotools build (no meson.build). +configure, build, install = autotools( + configure_args=[ + "--with-zstd", + "--with-xz", + "--with-zlib", + "--without-openssl", + "--disable-manpages", + "--disable-test-modules", + ], +) diff --git a/recipes/libcap.py b/recipes/libcap.py new file mode 100644 index 0000000..9d7bc75 --- /dev/null +++ b/recipes/libcap.py @@ -0,0 +1,40 @@ +version = "2.73" +revision = 1 +description = "POSIX 1003.1e capabilities library" +license = "BSD-3-Clause OR GPL-2.0-only" +url = "https://sites.google.com/site/fullycapable/" +source = tarball( + url=f"https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-{version}.tar.xz", + sha256="6405f6089cf4cdd8c271540cd990654d78dd0b1989b2d9bda20f933a75a795a5", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + + +def _args(self): + # libcap is plain-make: CC builds target objects, BUILD_CC builds the small + # native code generator. No PAM/Go bindings; force lib= so it lands in /usr/lib. + return [ + f"CC={self.triple}-gcc", + "BUILD_CC=gcc", + "GOLANG=no", + "PAM_CAP=no", + "lib=lib", + "prefix=/usr", + ] + + +def build(self): + # libcap builds in-tree; /sources is read-only, so work in /build. + self.run("cp", "-rp", f"{self.source_dir}/.", self.build_dir) + self.run("make", f"-j{self.jobs}", *_args(self)) + + +def install(self): + self.run( + "make", + "install", + f"DESTDIR={self.dest_dir}", + "RAISE_SETFCAP=no", + *_args(self), + ) diff --git a/recipes/libdisplay-info.py b/recipes/libdisplay-info.py new file mode 100644 index 0000000..4a5d1a5 --- /dev/null +++ b/recipes/libdisplay-info.py @@ -0,0 +1,32 @@ +version = "0.2.0" +revision = 1 +description = "EDID and DisplayID parsing library" +license = "MIT" +url = "https://gitlab.freedesktop.org/emersion/libdisplay-info" +source = tarball( + url=f"https://gitlab.freedesktop.org/emersion/libdisplay-info/-/releases/{version}/downloads/libdisplay-info-{version}.tar.xz", + sha256="5a2f002a16f42dd3540c8846f80a90b8f4bdcd067a94b9d2087bc2feae974176", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# Reads hwdata's pnp.ids at build time. +deps = [profile["libc"], "hwdata"] + +def configure(self): + # libdisplay-info generates a C table from hwdata's pnp.ids at build time, + # looking on the *build* machine (/usr/share/hwdata/pnp.ids). Our hwdata is + # in the target sysroot, so stage the (arch-independent) data there first. + self.run( + "sh", + "-c", + "mkdir -p /usr/share/hwdata && " + "cp -f /sysroot/usr/share/hwdata/pnp.ids /usr/share/hwdata/pnp.ids", + ) + meson_configure(self) + + +def build(self): + meson_build(self) + + +def install(self): + meson_install(self) diff --git a/recipes/libdrm.py b/recipes/libdrm.py new file mode 100644 index 0000000..20dbc79 --- /dev/null +++ b/recipes/libdrm.py @@ -0,0 +1,31 @@ +version = "2.4.124" +revision = 1 +description = "Direct Rendering Manager userspace library" +license = "MIT" +url = "https://dri.freedesktop.org/" +source = tarball( + url=f"https://dri.freedesktop.org/libdrm/libdrm-{version}.tar.xz", + sha256="ac36293f61ca4aafaf4b16a2a7afff312aa4f5c37c9fbd797de9e3c0863ca379", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "libpciaccess"] + +configure, build, install = meson( + configure_args=[ + # udev support is optional and would create a cycle with systemd-udev, + # which itself is pulled in later; Mesa does not require it. + "-Dudev=false", + "-Dtests=false", + "-Dman-pages=disabled", + "-Dvalgrind=disabled", + # GPU-specific helper libs that Mesa's x86_64 drivers consume. + "-Dintel=enabled", + "-Dradeon=enabled", + "-Damdgpu=enabled", + "-Dnouveau=enabled", + # Embedded/SoC GPUs we don't target. + "-Dvc4=disabled", + "-Detnaviv=disabled", + "-Dfreedreno=disabled", + ], +) diff --git a/recipes/libevdev.py b/recipes/libevdev.py new file mode 100644 index 0000000..ba16286 --- /dev/null +++ b/recipes/libevdev.py @@ -0,0 +1,18 @@ +version = "1.13.6" +revision = 1 +description = "Wrapper library for evdev input devices" +license = "MIT" +url = "https://gitlab.freedesktop.org/libevdev/libevdev" +source = tarball( + url=f"https://www.freedesktop.org/software/libevdev/libevdev-{version}.tar.xz", + sha256="73f215eccbd8233f414737ac06bca2687e67c44b97d2d7576091aa9718551110", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = meson( + configure_args=[ + "-Dtests=disabled", + "-Ddocumentation=disabled", + ], +) diff --git a/recipes/libffi.py b/recipes/libffi.py new file mode 100644 index 0000000..3aab570 --- /dev/null +++ b/recipes/libffi.py @@ -0,0 +1,16 @@ +version = "3.4.6" +revision = 1 +description = "Portable foreign-function interface library" +license = "MIT" +url = "https://sourceware.org/libffi/" +source = tarball( + url=f"https://github.com/libffi/libffi/releases/download/v{version}/libffi-{version}.tar.gz", + sha256="b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = autotools( + # Install straight into ${libdir} rather than a gcc-triplet subdir. + configure_args=["--disable-multi-os-directory"], +) diff --git a/recipes/libglvnd.py b/recipes/libglvnd.py new file mode 100644 index 0000000..2cae3cb --- /dev/null +++ b/recipes/libglvnd.py @@ -0,0 +1,22 @@ +version = "1.7.0" +revision = 1 +description = "GL Vendor-Neutral Dispatch library" +license = "MIT" +url = "https://gitlab.freedesktop.org/glvnd/libglvnd" +source = tarball( + url=f"https://gitlab.freedesktop.org/glvnd/libglvnd/-/archive/v{version}/libglvnd-v{version}.tar.gz", + sha256="2b6e15b06aafb4c0b6e2348124808cbd9b291c647299eaaba2e3202f51ff2f3d", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = meson( + configure_args=[ + # Wayland-only: no X11/GLX dispatch. + "-Dx11=disabled", + "-Dglx=disabled", + "-Degl=true", + "-Dgles1=true", + "-Dgles2=true", + ], +) diff --git a/recipes/libinput.py b/recipes/libinput.py new file mode 100644 index 0000000..3b5b37c --- /dev/null +++ b/recipes/libinput.py @@ -0,0 +1,22 @@ +version = "1.28.1" +revision = 1 +description = "Input device handling library for Wayland compositors" +license = "MIT" +url = "https://gitlab.freedesktop.org/libinput/libinput" +source = tarball( + url=f"https://gitlab.freedesktop.org/libinput/libinput/-/archive/{version}/libinput-{version}.tar.gz", + sha256="a13f8c9a7d93df3c85c66afd135f0296701d8d32f911991b7aa4273fdd6a42a3", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# eudev provides libudev here. +deps = [profile["libc"], "eudev", "libevdev", "mtdev"] + +configure, build, install = meson( + configure_args=[ + "-Dlibwacom=false", + "-Ddebug-gui=false", + "-Dtests=false", + "-Ddocumentation=false", + "-Dudev-dir=/usr/lib/udev", + ], +) diff --git a/recipes/libjpeg-turbo.py b/recipes/libjpeg-turbo.py new file mode 100644 index 0000000..60ce4a3 --- /dev/null +++ b/recipes/libjpeg-turbo.py @@ -0,0 +1,19 @@ +version = "3.1.2" +revision = 1 +description = "SIMD-accelerated libjpeg-compatible JPEG codec" +license = "BSD-3-Clause AND IJG" +url = "https://libjpeg-turbo.org/" +source = tarball( + url=f"https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/{version}/libjpeg-turbo-{version}.tar.gz", + sha256="8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = cmake( + configure_args=[ + "-DENABLE_STATIC=OFF", + "-DENABLE_SHARED=ON", + "-DWITH_JPEG8=ON", + ], +) diff --git a/recipes/libpciaccess.py b/recipes/libpciaccess.py new file mode 100644 index 0000000..1b5ba44 --- /dev/null +++ b/recipes/libpciaccess.py @@ -0,0 +1,13 @@ +version = "0.18.1" +revision = 1 +description = "Generic PCI access library" +license = "MIT" +url = "https://gitlab.freedesktop.org/xorg/lib/libpciaccess" +source = tarball( + url=f"https://www.x.org/archive/individual/lib/libpciaccess-{version}.tar.xz", + sha256="4af43444b38adb5545d0ed1c2ce46d9608cc47b31c2387fc5181656765a6fa76", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib"] + +configure, build, install = meson() diff --git a/recipes/libpng.py b/recipes/libpng.py new file mode 100644 index 0000000..f4b4d5c --- /dev/null +++ b/recipes/libpng.py @@ -0,0 +1,13 @@ +version = "1.6.58" +revision = 1 +description = "Reference library for the PNG image format" +license = "libpng-2.0" +url = "http://www.libpng.org/pub/png/libpng.html" +source = tarball( + url=f"https://github.com/pnggroup/libpng/archive/refs/tags/v{version}.tar.gz", + sha256="a9d4df463d36a6e5f9c29bd6f4967312d17e996c1854f3511f833924eb1993cf", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib"] + +configure, build, install = autotools() diff --git a/recipes/libxcrypt.py b/recipes/libxcrypt.py new file mode 100644 index 0000000..b60f853 --- /dev/null +++ b/recipes/libxcrypt.py @@ -0,0 +1,23 @@ +version = "4.4.38" +revision = 1 +description = ( + "Extended crypt library (libcrypt: descrypt, md5crypt, sha*crypt, yescrypt)" +) +license = "LGPL-2.1-or-later" +url = "https://github.com/besser82/libxcrypt" +source = tarball( + url=f"https://github.com/besser82/libxcrypt/releases/download/v{version}/libxcrypt-{version}.tar.xz", + sha256="80304b9c306ea799327f01d9a7549bdb28317789182631f1b54f4511b4206dd6", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +# glibc no longer provides libcrypt/crypt(); libxcrypt is the replacement. +# Build the glibc-compatible libcrypt.so.1 with all hash methods. +configure, build, install = autotools( + configure_args=[ + "--enable-obsolete-api=glibc", + "--enable-hashes=all", + "--disable-failure-tokens", + ], +) diff --git a/recipes/libxkbcommon.py b/recipes/libxkbcommon.py new file mode 100644 index 0000000..fd855e1 --- /dev/null +++ b/recipes/libxkbcommon.py @@ -0,0 +1,21 @@ +version = "1.9.2" +revision = 1 +description = "Library to handle keyboard descriptions and key events" +license = "MIT" +url = "https://xkbcommon.org/" +source = tarball( + url=f"https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-{version}.tar.gz", + sha256="d2eab57e1ce79de991f8ceb3fcd726a6978b970382c8ac8c8f112b61ceaa9167", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "xkeyboard-config", "libxml2"] + +configure, build, install = meson( + configure_args=[ + # Weston uses core libxkbcommon; no X11/Wayland helper tools needed. + "-Denable-x11=false", + "-Denable-wayland=false", + "-Denable-docs=false", + "-Dxkb-config-root=/usr/share/X11/xkb", + ], +) diff --git a/recipes/libxml2.py b/recipes/libxml2.py new file mode 100644 index 0000000..a6622db --- /dev/null +++ b/recipes/libxml2.py @@ -0,0 +1,21 @@ +version = "2.14.2" +revision = 1 +description = "XML parsing and toolkit library (libxml2)" +license = "MIT" +url = "https://gitlab.gnome.org/GNOME/libxml2" +_series = ".".join(version.split(".")[:2]) +source = tarball( + url=f"https://download.gnome.org/sources/libxml2/{_series}/libxml2-{version}.tar.xz", + sha256="353f3c83535d4224a4e5f1e88c90b5d4563ea8fec11f6407df640fd28fc8b8c6", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib", "xz"] + +configure, build, install = autotools( + configure_args=[ + "--without-python", + "--with-zlib", + "--with-lzma", + "--without-icu", + ], +) diff --git a/recipes/linux/recipe.py b/recipes/linux/recipe.py index dd8dd96..9fdd19b 100644 --- a/recipes/linux/recipe.py +++ b/recipes/linux/recipe.py @@ -46,3 +46,33 @@ def install(self): self.build_dir / "arch/x86/boot/bzImage", self.dest_dir / f"boot/vmlinuz-{self.version}", ) + self.run( + "install", + "-Dm644", + self.build_dir / ".config", + self.dest_dir / f"boot/config-{self.version}", + ) + self.run( + "install", + "-Dm644", + self.build_dir / "System.map", + self.dest_dir / f"boot/System.map-{self.version}", + ) + # Install loadable modules so udev can autoload DRM/input drivers at boot. + # The kernel's modules_install target runs depmod itself (host kmod). + self.run( + *_make( + self, + f"INSTALL_MOD_PATH={self.dest_dir}", + "INSTALL_MOD_STRIP=1", + "modules_install", + ) + ) + # Drop the dangling build/source symlinks that point at the (ephemeral) + # build tree; they are useless in the target image. + self.run( + "sh", + "-c", + f"rm -f {self.dest_dir}/lib/modules/*/build " + f"{self.dest_dir}/lib/modules/*/source", + ) diff --git a/recipes/llvm.py b/recipes/llvm.py new file mode 100644 index 0000000..c521d75 --- /dev/null +++ b/recipes/llvm.py @@ -0,0 +1,72 @@ +version = "21.1.6" +revision = 1 +description = "LLVM compiler infrastructure (target libLLVM for Mesa)" +license = "Apache-2.0 WITH LLVM-exception" +url = "https://llvm.org/" +source = tarball( + url=f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/llvm-project-{version}.src.tar.xz", + sha256="ae67086eb04bed7ca11ab880349b5f1ab6f50e1b88cda376eaf8a845b935762b", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# Pinned independently of the host LLVM (22.x): this must be a release that +# Mesa 25.3 accepts. Provides target libLLVM for the llvmpipe/radeonsi gallium +# drivers; X86 + AMDGPU backends cover software rendering and AMD GPUs. +deps = [profile["libc"], "ncurses", "zlib", "zstd", "libffi", "libxml2"] + + +def configure(self): + p = self.options + self.run( + "cmake", + "-S", + self.source_dir / "llvm", + "-B", + self.build_dir, + "-GNinja", + "-DCMAKE_BUILD_TYPE=Release", + f"-DCMAKE_INSTALL_PREFIX={p.get('prefix', '/usr')}", + # Cross toolchain. CMAKE_CROSSCOMPILING triggers LLVM to build a native + # llvm-tblgen under build/NATIVE with the default host compiler. + "-DCMAKE_SYSTEM_NAME=Linux", + f"-DCMAKE_SYSTEM_PROCESSOR={self.arch}", + f"-DCMAKE_SYSROOT={self.sysroot}", + f"-DCMAKE_C_COMPILER={self.triple}-gcc", + f"-DCMAKE_CXX_COMPILER={self.triple}-g++", + "-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER", + "-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY", + "-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY", + "-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY", + f"-DLLVM_HOST_TRIPLE={self.triple}", + f"-DLLVM_DEFAULT_TARGET_TRIPLE={self.triple}", + "-DLLVM_TARGETS_TO_BUILD=X86;AMDGPU", + "-DLLVM_BUILD_LLVM_DYLIB=ON", + "-DLLVM_LINK_LLVM_DYLIB=ON", + "-DLLVM_ENABLE_RTTI=ON", + "-DLLVM_ENABLE_FFI=ON", + "-DLLVM_ENABLE_ZLIB=FORCE_ON", + "-DLLVM_ENABLE_ZSTD=FORCE_ON", + "-DLLVM_ENABLE_LIBXML2=FORCE_ON", + "-DLLVM_ENABLE_TERMINFO=ON", + "-DLLVM_INCLUDE_TESTS=OFF", + "-DLLVM_INCLUDE_EXAMPLES=OFF", + "-DLLVM_INCLUDE_BENCHMARKS=OFF", + "-DLLVM_INCLUDE_DOCS=OFF", + "-DLLVM_ENABLE_PROJECTS=", + "-Wno-dev", + env={ + "CFLAGS": p.get("cflags", ""), + "CXXFLAGS": p.get("cxxflags", ""), + "LDFLAGS": p.get("ldflags", ""), + }, + ) + + +def build(self): + self.run("cmake", "--build", self.build_dir, f"-j{self.jobs}") + + +def install(self): + self.run("cmake", "--install", self.build_dir, env={"DESTDIR": str(self.dest_dir)}) + # Runtime needs the shared libLLVM + headers/llvm-config; static archives are + # large and unnecessary for Mesa. + self.run("sh", "-c", f"rm -f {self.dest_dir}/usr/lib/*.a") diff --git a/recipes/mesa.py b/recipes/mesa.py new file mode 100644 index 0000000..67d0124 --- /dev/null +++ b/recipes/mesa.py @@ -0,0 +1,56 @@ +version = "25.3.0" +revision = 1 +description = "Mesa 3D graphics library (OpenGL/EGL/GLES, gallium drivers)" +license = "MIT" +url = "https://www.mesa3d.org/" +source = tarball( + url=f"https://archive.mesa3d.org/mesa-{version}.tar.xz", + sha256="0fd54fea7dbbddb154df05ac752b18621f26d97e27863db3be951417c6abe8ae", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [ + profile["libc"], + "libstdc++", # C++ components need the libstdc++ headers/runtime + "libdrm", + "expat", + "llvm", + "zlib", + "zstd", + "wayland", + "wayland-protocols", + "libglvnd", + "hwdata", + "eudev", # libudev: DRM device enumeration + "libdisplay-info", # EDID/DisplayID parsing for the KMS backend + "elfutils", # libelf: required by the radeonsi gallium driver +] + +_mesa_args = [ + "-Dplatforms=wayland", + "-Dglx=disabled", + "-Degl=enabled", + "-Dgbm=enabled", + "-Dgles1=enabled", + "-Dgles2=enabled", + "-Dglvnd=enabled", + "-Dshared-glapi=enabled", + "-Dllvm=enabled", + "-Dshared-llvm=enabled", + "-Dexpat=enabled", + "-Dzstd=enabled", + "-Dvideo-codecs=all_free", + "-Db_ndebug=true", + "-Dbuild-tests=false", + # Broad HW + software gallium set (Weston's GL renderer uses these). + # NOTE: 'iris' (modern Intel) is omitted because it requires libclc + # (a clang-based OpenCL-bitcode build); 'crocus' covers older Intel. + # Add iris back once a libclc recipe exists. + "-Dgallium-drivers=radeonsi,r600,crocus,nouveau,virgl,svga,zink,llvmpipe,softpipe", + # Vulkan deferred: needs a vulkan-headers + SPIRV-Headers/Tools sub-stack + # and is not required for Weston's GL renderer. Add radv/anv/lavapipe later. + "-Dvulkan-drivers=", +] + +# LLVM is found via the cross llvm-config emulator wired into the meson cross +# file (src/lib/meson.py), which reports sysroot paths — no target-binary exec. +configure, build, install = meson(configure_args=_mesa_args) diff --git a/recipes/mtdev.py b/recipes/mtdev.py new file mode 100644 index 0000000..1d42d82 --- /dev/null +++ b/recipes/mtdev.py @@ -0,0 +1,13 @@ +version = "1.1.7" +revision = 1 +description = "Multitouch protocol translation library" +license = "MIT" +url = "https://bitmath.org/code/mtdev/" +source = tarball( + url=f"https://bitmath.org/code/mtdev/mtdev-{version}.tar.gz", + sha256="a55bd02a9af4dd266c0042ec608744fff3a017577614c057da09f1f4566ea32c", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = autotools() diff --git a/recipes/pango.py b/recipes/pango.py new file mode 100644 index 0000000..f75d09f --- /dev/null +++ b/recipes/pango.py @@ -0,0 +1,28 @@ +version = "1.56.3" +revision = 1 +description = "Library for laying out and rendering internationalized text" +license = "LGPL-2.1-or-later" +url = "https://www.gtk.org/" +_series = ".".join(version.split(".")[:2]) +source = tarball( + url=f"https://download.gnome.org/sources/pango/{_series}/pango-{version}.tar.xz", + sha256="2606252bc25cd8d24e1b7f7e92c3a272b37acd6734347b73b47a482834ba2491", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [ + profile["libc"], + "glib2", + "harfbuzz", + "fribidi", + "fontconfig", + "freetype2", + "cairo", +] + +configure, build, install = meson( + configure_args=[ + "-Dintrospection=disabled", + "-Dgtk_doc=false", + "-Dxft=disabled", + ], +) diff --git a/recipes/pcre2.py b/recipes/pcre2.py new file mode 100644 index 0000000..e6ca78f --- /dev/null +++ b/recipes/pcre2.py @@ -0,0 +1,19 @@ +version = "10.45" +revision = 1 +description = "Perl-compatible regular expression library (PCRE2)" +license = "BSD-3-Clause" +url = "https://www.pcre.org/" +source = tarball( + url=f"https://github.com/PCRE2Project/pcre2/releases/download/pcre2-{version}/pcre2-{version}.tar.gz", + sha256="0e138387df7835d7403b8351e2226c1377da804e0737db0e071b48f07c9d12ee", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = autotools( + configure_args=[ + "--enable-pcre2-16", + "--enable-pcre2-32", + "--enable-jit", + ], +) diff --git a/recipes/pixman.py b/recipes/pixman.py new file mode 100644 index 0000000..4e21d37 --- /dev/null +++ b/recipes/pixman.py @@ -0,0 +1,19 @@ +version = "0.46.0" +revision = 1 +description = "Low-level pixel manipulation library" +license = "MIT" +url = "https://www.pixman.org/" +source = tarball( + url=f"https://www.x.org/archive/individual/lib/pixman-{version}.tar.xz", + sha256="d2eab57e1ce79de991f8ceb3fcd726a6978b970382c8ac8c8f112b61ceaa9167", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "libpng"] + +configure, build, install = meson( + configure_args=[ + "-Dtests=disabled", + "-Ddemos=disabled", + "-Dgtk=disabled", + ], +) diff --git a/recipes/seatd.py b/recipes/seatd.py new file mode 100644 index 0000000..5529a8e --- /dev/null +++ b/recipes/seatd.py @@ -0,0 +1,23 @@ +version = "0.6.3" +revision = 1 +description = "Seat management daemon and libseat (logind-free)" +license = "MIT" +url = "https://git.sr.ht/~kennylevinsen/seatd" +source = tarball( + url=f"https://git.sr.ht/~kennylevinsen/seatd/archive/{version}.tar.gz", + sha256="5226850c163b485aebe71da0d3f4941761637e146a5c9393cb40c52617ad84a8", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + +configure, build, install = meson( + configure_args=[ + # No logind: libseat talks to the bundled seatd daemon, which Weston + # uses (via libseat) to open DRM/input devices on the user's behalf. + "-Dlibseat-logind=disabled", + "-Dlibseat-seatd=enabled", + "-Dserver=enabled", + "-Dexamples=disabled", + "-Dman-pages=disabled", + ], +) diff --git a/recipes/shadow.py b/recipes/shadow.py new file mode 100644 index 0000000..132c9cc --- /dev/null +++ b/recipes/shadow.py @@ -0,0 +1,33 @@ +version = "4.16.0" +revision = 1 +description = "Password and account management utilities (login, passwd, useradd)" +license = "BSD-3-Clause" +url = "https://github.com/shadow-maint/shadow" +source = tarball( + url=f"https://github.com/shadow-maint/shadow/releases/download/{version}/shadow-{version}.tar.xz", + sha256="b78e3921a95d53282a38e90628880624736bf6235e36eea50c50835f59a3530b", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "libxcrypt"] + +configure, build, _ = autotools( + configure_args=[ + "--without-libpam", + "--without-libbsd", + "--without-selinux", + "--without-acl", + "--without-attr", + ], +) + + +def install(self): + autotools_install(self, [f"DESTDIR={self.dest_dir}"]) + # `groups` is also shipped by coreutils; drop shadow's copy (and its man + # page) so the two packages don't conflict at install time. + self.run( + "sh", + "-c", + f"rm -f {self.dest_dir}/usr/bin/groups " + f"{self.dest_dir}/usr/share/man/man1/groups.1", + ) diff --git a/recipes/ttf-dejavu.py b/recipes/ttf-dejavu.py new file mode 100644 index 0000000..adb5b04 --- /dev/null +++ b/recipes/ttf-dejavu.py @@ -0,0 +1,18 @@ +version = "2.37" +revision = 1 +description = "DejaVu TrueType font family" +license = "Bitstream-Vera" +url = "https://dejavu-fonts.github.io/" +_tag = "version_" + version.replace(".", "_") +source = tarball( + url=f"https://github.com/dejavu-fonts/dejavu-fonts/releases/download/{_tag}/dejavu-fonts-ttf-{version}.tar.bz2", + sha256="fa9ca4d13871dd122f61258a80d01751d603b4d3ee14095d65453b4e846e17d7", +) +host_deps = [] +deps = [] + + +def install(self): + dest = self.dest_dir / "usr/share/fonts/dejavu" + self.run("install", "-d", dest) + self.run("sh", "-c", f"install -m644 {self.source_dir}/ttf/*.ttf {dest}/") diff --git a/recipes/util-linux.py b/recipes/util-linux.py new file mode 100644 index 0000000..0d732bf --- /dev/null +++ b/recipes/util-linux.py @@ -0,0 +1,58 @@ +version = "2.41" +revision = 1 +description = "Miscellaneous system utilities (agetty, mount, libblkid, libmount)" +license = "GPL-2.0-or-later AND LGPL-2.1-or-later" +url = "https://github.com/util-linux/util-linux" +_series = "v" + ".".join(version.split(".")[:2]) +source = tarball( + url=f"https://www.kernel.org/pub/linux/utils/util-linux/{_series}/util-linux-{version}.tar.xz", + sha256="81ee93b3cfdfeb7d7c4090cedeba1d7bbce9141fd0b501b686b3fe475ddca4c6", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"], "zlib"] + +configure, build, _ = autotools( + configure_args=[ + # We need agetty + the libs (blkid/mount/uuid); login/su come from shadow + # to avoid a /usr/bin/login file conflict. + "--disable-login", + "--disable-su", + "--disable-nologin", + # liblastlog2 (new in 2.41) pulls in sqlite3; we don't need it. + "--disable-liblastlog2", + "--disable-pam-lastlog2", + # We only need agetty + the libs; skip the curses TUI tools (irqtop, + # cfdisk, ...) which otherwise trip over the ncurses termlib split. + "--without-ncurses", + "--without-ncursesw", + "--without-systemd", + "--without-systemdsystemunitdir", + "--without-python", + "--disable-bash-completion", + "--disable-rpath", + # These would chown/setuid during install, which fails unprivileged. + "--disable-makeinstall-chown", + "--disable-makeinstall-setuid", + ], +) + + +def install(self): + autotools_install(self, [f"DESTDIR={self.dest_dir}"]) + # libtool's install-time relink miscompiles libmount/libfdisk against the + # build host's musl libc instead of the target glibc (the pre-relink + # build-tree libs are correct). Restore the build-tree shared objects over + # the relinked ones and strip their build-tree rpath. + self.run( + "sh", + "-ec", + "for real in /build/.libs/lib*.so.*.*.*; do " + ' [ -e "$real" ] || continue; ' + ' base=$(basename "$real"); ' + f' dst="{self.dest_dir}/usr/lib/$base"; ' + ' [ -e "$dst" ] || continue; ' + ' cp -a "$real" "$dst"; ' + ' patchelf --remove-rpath "$dst" 2>/dev/null || true; ' + "done", + ) + diff --git a/recipes/wayland-protocols.py b/recipes/wayland-protocols.py new file mode 100644 index 0000000..db7e826 --- /dev/null +++ b/recipes/wayland-protocols.py @@ -0,0 +1,16 @@ +version = "1.43" +revision = 1 +description = "Wayland protocol extension definitions" +license = "MIT" +url = "https://gitlab.freedesktop.org/wayland/wayland-protocols" +source = tarball( + url=f"https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/{version}/downloads/wayland-protocols-{version}.tar.xz", + sha256="ba3c3425dd27c57b5291e93dba97be12479601e00bcab24d26471948cb643653", +) +host_deps = ["pkgconf"] +# Architecture-independent XML + pkg-config data; nothing to link. +deps = [] + +configure, build, install = meson( + configure_args=["-Dtests=false"], +) diff --git a/recipes/wayland.py b/recipes/wayland.py new file mode 100644 index 0000000..dbe31be --- /dev/null +++ b/recipes/wayland.py @@ -0,0 +1,23 @@ +# Must match the build machine's wayland-scanner (from the Alpine image), which +# wayland requires to be the exact same version when cross-compiling. +version = "1.25.0" +revision = 1 +description = "Core Wayland protocol library and scanner" +license = "MIT" +url = "https://wayland.freedesktop.org/" +source = tarball( + url=f"https://gitlab.freedesktop.org/wayland/wayland/-/releases/{version}/downloads/wayland-{version}.tar.xz", + sha256="c065f040afdff3177680600f249727e41a1afc22fccf27222f15f5306faa1f03", +) +host_deps = ["binutils", "gcc", "pkgconf"] +# The build machine's wayland-scanner (from the builder image) generates protocol +# glue; the target libwayland-{client,server} link libffi/expat/libxml2. +deps = [profile["libc"], "libffi", "expat", "libxml2"] + +configure, build, install = meson( + configure_args=[ + "-Dtests=false", + "-Ddocumentation=false", + "-Ddtd_validation=true", + ], +) diff --git a/recipes/weston.py b/recipes/weston.py new file mode 100644 index 0000000..3873a2e --- /dev/null +++ b/recipes/weston.py @@ -0,0 +1,58 @@ +version = "14.0.2" +revision = 1 +description = "Reference Wayland compositor (Weston)" +license = "MIT" +url = "https://gitlab.freedesktop.org/wayland/weston" +source = tarball( + url=f"https://gitlab.freedesktop.org/wayland/weston/-/releases/{version}/downloads/weston-{version}.tar.xz", + sha256="b47216b3530da76d02a3a1acbf1846a9cd41d24caa86448f9c46f78f20b6e0ac", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [ + profile["libc"], + "wayland", + "wayland-protocols", + "libxkbcommon", + "pixman", + "cairo", + "libinput", + "mesa", + "libdrm", + "libdisplay-info", + "seatd", + "pango", + "glib2", + "libjpeg-turbo", + "libpng", + "freetype2", + "fontconfig", + "ttf-dejavu", +] + +configure, build, install = meson( + configure_args=[ + # DRM backend with the GL renderer; seatd (libseat) launcher, no logind. + "-Dbackend-drm=true", + "-Dbackend-headless=true", + "-Dbackend-wayland=true", + "-Dbackend-x11=false", + "-Dbackend-rdp=false", + "-Dbackend-vnc=false", + "-Dbackend-pipewire=false", + "-Dbackend-drm-screencast-vaapi=false", + "-Drenderer-gl=true", + "-Dxwayland=false", + "-Dsystemd=false", + # libseat (seatd) is auto-detected in weston 14; no explicit option. + "-Dimage-jpeg=true", + "-Dimage-webp=false", + "-Dremoting=false", + "-Dpipewire=false", + "-Dcolor-management-lcms=false", + "-Ddemo-clients=true", + "-Dsimple-clients=damage,im,egl,shm,touch", + # Test suite needs extra generated protocol headers and isn't needed. + "-Dtests=false", + "-Dtest-junit-xml=false", + ], +) diff --git a/recipes/xkeyboard-config.py b/recipes/xkeyboard-config.py new file mode 100644 index 0000000..bd86f46 --- /dev/null +++ b/recipes/xkeyboard-config.py @@ -0,0 +1,16 @@ +version = "2.44" +revision = 1 +description = "X keyboard configuration database (keymaps for libxkbcommon)" +license = "MIT" +url = "https://www.freedesktop.org/wiki/Software/XKeyboardConfig/" +source = tarball( + url=f"https://www.x.org/archive/individual/data/xkeyboard-config/xkeyboard-config-{version}.tar.xz", + sha256="54d2c33eeebb031d48fa590c543e54c9bcbd0f00386ebc6489b2f47a0da4342a", +) +host_deps = ["pkgconf"] +# Pure keymap data installed under /usr/share/X11/xkb. +deps = [] + +configure, build, install = meson( + configure_args=["-Dxorg-rules-symlinks=true"], +) diff --git a/recipes/zlib.py b/recipes/zlib.py index 8627034..1ddfd4f 100644 --- a/recipes/zlib.py +++ b/recipes/zlib.py @@ -5,7 +5,7 @@ license = "Zlib" url = "https://zlib.net/" source = tarball( url=f"https://github.com/madler/zlib/releases/download/v{version}/zlib-{version}.tar.gz", - sha256="?", + sha256="bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16", ) host_deps = ["binutils", "gcc"] deps = [profile["libc"]] diff --git a/recipes/zstd.py b/recipes/zstd.py new file mode 100644 index 0000000..d670a11 --- /dev/null +++ b/recipes/zstd.py @@ -0,0 +1,29 @@ +version = "1.5.6" +revision = 1 +description = "Zstandard fast lossless compression library" +license = "BSD-3-Clause" +url = "https://facebook.github.io/zstd/" +source = tarball( + url=f"https://github.com/facebook/zstd/releases/download/v{version}/zstd-{version}.tar.gz", + sha256="8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1", +) +host_deps = ["binutils", "gcc", "pkgconf"] +deps = [profile["libc"]] + + +def configure(self): + # zstd's top-level autotools/Makefile don't cross-compile cleanly; use the + # maintained meson port shipped under build/meson. + meson_configure( + self, + ["-Dbin_programs=true", "-Dzlib=disabled", "-Dlzma=disabled", "-Dlz4=disabled"], + source_dir=self.source_dir / "build/meson", + ) + + +def build(self): + meson_build(self) + + +def install(self): + meson_install(self) diff --git a/scripts/mkimage.sh b/scripts/mkimage.sh new file mode 100755 index 0000000..21ab27e --- /dev/null +++ b/scripts/mkimage.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# +# Build a bootable initramfs from the installed package set and (optionally) +# launch it in QEMU to test the Weston desktop. +# +# Approach: the entire root filesystem is packed into a single initramfs and +# booted straight from RAM (no disk image, no loop mounts, no root needed). +# dinit runs as PID 1, brings up udev/seatd, autologins, and starts Weston. +# +# Usage: +# scripts/mkimage.sh [-C build-dir] [--run] [--mem MB] +# +# -C, --build-dir orchid build dir (default: $ORCHID_BUILD or build-x86_64) +# --out DIR output dir for artifacts (default: /qemu) +# --mem MB QEMU RAM in MiB (default: 4096) +# --run launch QEMU after building (otherwise just print the command) +# +# Artifacts written to : rootfs/ (installed tree), vmlinuz, initramfs.cpio.gz +set -euo pipefail + +here=$(cd "$(dirname "$0")/.." && pwd) +orchid="$here/orchid" + +build_dir=${ORCHID_BUILD:-build-x86_64} +out="" +mem=4096 +run=0 + +while [ $# -gt 0 ]; do + case "$1" in + -C|--build-dir) build_dir=$2; shift 2 ;; + --out) out=$2; shift 2 ;; + --mem) mem=$2; shift 2 ;; + --run) run=1; shift ;; + -h|--help) sed -n '2,20p' "$0"; exit 0 ;; + *) echo "unknown argument: $1" >&2; exit 2 ;; + esac +done + +[ -d "$build_dir" ] || { echo "build dir not found: $build_dir" >&2; exit 1; } +out=${out:-$build_dir/qemu} +rootfs="$out/rootfs" + +mkdir -p "$out" + +echo ">> installing packages into $rootfs" +rm -rf "$rootfs" +"$orchid" install -C "$build_dir" "$rootfs" + +echo ">> wiring up init" +# The kernel execs /init from an initramfs but does NOT auto-mount devtmpfs, so +# dinit would have no /dev/null to set up service stdio. This tiny bootstrap +# mounts the kernel vfs, then hands off to dinit (PID 1) which runs the rest. +mkdir -p "$rootfs"/{proc,sys,dev,run,tmp} +cat > "$rootfs/init" <<'EOF' +#!/bin/bash +export PATH=/usr/bin:/usr/sbin:/bin:/sbin +mount -t devtmpfs dev /dev 2>/dev/null || true +mount -t proc proc /proc 2>/dev/null || true +mount -t sysfs sys /sys 2>/dev/null || true +exec /sbin/init +EOF +chmod +x "$rootfs/init" + +echo ">> regenerating ld.so.cache" +# Target libs are split across /lib, /usr/lib and /usr/lib64; without a cache +# the loader can't find e.g. libstdc++ for dinit (PID 1, before any service +# runs). base-files ships /etc/ld.so.conf + the /lib64 and /bin compat links; +# build the cache now from the assembled rootfs. +# Prefer the *target* ldconfig (run via the target loader) so the cache is in a +# format the target glibc loader accepts; fall back to the host ldconfig. +loader="$rootfs/lib/ld-linux-x86-64.so.2" +tgt_ldconfig="$rootfs/sbin/ldconfig" +if [ -x "$loader" ] && [ -e "$tgt_ldconfig" ]; then + "$loader" --library-path "$rootfs/lib:$rootfs/usr/lib:$rootfs/usr/lib64" \ + "$tgt_ldconfig" -r "$rootfs" +elif command -v ldconfig >/dev/null; then + ldconfig -r "$rootfs" +else + echo " warning: no ldconfig available; libraries may not resolve" >&2 +fi + +echo ">> extracting kernel" +kernel=$(ls -1 "$rootfs"/boot/vmlinuz-* 2>/dev/null | head -n1 || true) +if [ -z "$kernel" ]; then + echo "no kernel in $rootfs/boot (build the 'linux' recipe first)" >&2 + exit 1 +fi +cp "$kernel" "$out/vmlinuz" + +if [ ! -d "$rootfs"/lib/modules ] && [ ! -d "$rootfs"/usr/lib/modules ]; then + echo "warning: no /lib/modules in rootfs — DRM/input drivers built as" >&2 + echo " modules won't load (rebuild 'linux' with modules_install)." >&2 +fi + +echo ">> packing initramfs" +# Pack as a newc cpio + gzip. Run from within rootfs so paths are relative. +( cd "$rootfs" && find . -print0 | cpio --null --create --format=newc --quiet ) \ + | gzip -9 > "$out/initramfs.cpio.gz" +echo " initramfs: $(du -h "$out/initramfs.cpio.gz" | cut -f1)" + +# Assemble the QEMU command. virtio-gpu gives Weston a DRM/KMS device; virtio +# keyboard/tablet feed libinput; serial is the console for boot logs. +kvm=() +[ -w /dev/kvm ] && kvm=(-enable-kvm -cpu host) + +cmd=( + qemu-system-x86_64 + "${kvm[@]}" + -m "$mem" -smp 4 + -kernel "$out/vmlinuz" + -initrd "$out/initramfs.cpio.gz" + -append "console=ttyS0,115200 loglevel=6" + -device virtio-gpu-pci + -device virtio-keyboard-pci + -device virtio-tablet-pci + -display gtk,gl=on + -serial mon:stdio +) + +echo "" +echo ">> QEMU command:" +printf ' %q' "${cmd[@]}"; echo + +if [ "$run" -eq 1 ]; then + echo ">> launching QEMU (Ctrl-A X to quit the serial console)" + exec "${cmd[@]}" +fi diff --git a/src/builder.py b/src/builder.py index b0fb9fd..131686a 100644 --- a/src/builder.py +++ b/src/builder.py @@ -40,11 +40,11 @@ def _source_tree(layout: Layout, r: Recipe, key: str | None) -> Path: return base / key -def _prepare_all_sources(layout: Layout, r: Recipe) -> None: +def _prepare_all_sources(layout: Layout, r: Recipe, *, strict: bool = False) -> None: for key, src in r.sources.items(): tree = _source_tree(layout, r, key) tree.parent.mkdir(parents=True, exist_ok=True) - fetch.prepare_source(layout, r.dir, src, tree) + fetch.prepare_source(layout, r.dir, src, tree, strict=strict) def _build_path_env(host_deps_order: list[str], layout: Layout) -> str: @@ -204,22 +204,64 @@ def _finalize_host(c: Container, layout: Layout, r: Recipe) -> None: layout.host_pkg_marker(r.name, r.version, r.revision).write_text("ok\n") -def _sysroot_sync(c: Container, r: Recipe) -> None: - direct_deps = list(dict.fromkeys((*r.deps, *r.build_deps))) - if not direct_deps: +def _target_output_index(rs: RecipeSet) -> dict[str, Recipe]: + """Map every installable target package name (incl. subpackages) to its recipe.""" + idx: dict[str, Recipe] = {} + for rec in rs.target.values(): + for out in rec.outputs: + idx[out] = rec + return idx + + +def _transitive_runtime_deps(rs: RecipeSet, r: Recipe) -> list[str]: + """Full closure of `r`'s deps, so the sysroot carries everything its + dependencies need (e.g. pkg-config Requires.private / linked SONAMEs). + + Starts from `r`'s direct deps + build_deps and follows each package's own + `deps`. Unknown names (external packages, subpackages we can't resolve) are + still installed but not recursed into. + """ + idx = _target_output_index(rs) + order: list[str] = [] + visited: set[str] = set() + + def visit(name: str) -> None: + if name in visited: + return + visited.add(name) + order.append(name) + dep_recipe = idx.get(name) + if dep_recipe is not None and dep_recipe.name != r.name: + for d in dep_recipe.deps: + visit(d) + + for name in (*r.deps, *r.build_deps): + visit(name) + return order + + +def _sysroot_sync(c: Container, rs: RecipeSet, r: Recipe) -> None: + pkgs = _transitive_runtime_deps(rs, r) + if not pkgs: return initdb = not apk.sysroot_initialized(c) - apk.sysroot_install(c, direct_deps, initdb=initdb) + apk.sysroot_install(c, pkgs, initdb=initdb) def build_one( - rs: RecipeSet, layout: Layout, profile: Profile, r: Recipe, *, forced: bool = False + rs: RecipeSet, + layout: Layout, + profile: Profile, + r: Recipe, + *, + forced: bool = False, + strict: bool = False, ) -> None: log.info_field("recipe", _recipe_ref(r)) if forced: _clear_stamps(layout, r) _wipe_workdir(layout, r) - _prepare_all_sources(layout, r) + _prepare_all_sources(layout, r, strict=strict) c, _host_order = _container_for(rs, layout, profile, r) c.start() @@ -227,7 +269,7 @@ def build_one( # Ensure base output dest dir exists. c.exec(["mkdir", "-p", f"/dest/{r.name}"]) - _sysroot_sync(c, r) + _sysroot_sync(c, rs, r) ctx = RecipeContext( recipe=r, profile=profile, container=c, jobs=os.cpu_count() or 1 @@ -247,13 +289,15 @@ def build_one( log.ok_field("done", _recipe_ref(r)) -def execute(plan: Plan, rs: RecipeSet, layout: Layout, profile: Profile) -> None: +def execute( + plan: Plan, rs: RecipeSet, layout: Layout, profile: Profile, *, strict: bool = False +) -> None: if not plan.order: log.info_field("plan", "nothing to do") return for k in plan.order: r = plan.recipes[k] - build_one(rs, layout, profile, r, forced=k in plan.forced) + build_one(rs, layout, profile, r, forced=k in plan.forced, strict=strict) def install_to( diff --git a/src/cli.py b/src/cli.py index fa45573..d7d6153 100644 --- a/src/cli.py +++ b/src/cli.py @@ -120,7 +120,7 @@ def cmd_build(args) -> int: p = plan.build_plan(rs, layout, args.recipes or None, rebuild=args.rebuild) if args.dry_run: return cmd_plan(args) - builder.execute(p, rs, layout, prof) + builder.execute(p, rs, layout, prof, strict=args.strict) return 0 @@ -159,7 +159,7 @@ def cmd_fetch(args) -> int: for k in targets: r = rs.get(k) for key, src in r.sources.items(): - fetch_mod.fetch(layout, src) + fetch_mod.fetch(layout, src, strict=args.strict) return 0 @@ -197,6 +197,11 @@ def make_parser() -> argparse.ArgumentParser: _common(p_build) p_build.add_argument("--rebuild", action="store_true") p_build.add_argument("-n", "--dry-run", action="store_true") + p_build.add_argument( + "--strict", + action="store_true", + help='fail on recipes with a placeholder checksum (sha256="?")', + ) p_build.set_defaults(func=cmd_build) p_inst = sub.add_parser( @@ -221,6 +226,11 @@ def make_parser() -> argparse.ArgumentParser: p_fetch = sub.add_parser("fetch", help="fetch sources only") _common(p_fetch) + p_fetch.add_argument( + "--strict", + action="store_true", + help='fail on recipes with a placeholder checksum (sha256="?")', + ) p_fetch.set_defaults(func=cmd_fetch) return parser diff --git a/src/context.py b/src/context.py index 55492fe..6d2f588 100644 --- a/src/context.py +++ b/src/context.py @@ -108,3 +108,14 @@ class RecipeContext: merged.update(env) cwd_s = str(cwd) if cwd is not None else "/build" self.container.exec(flat, env=merged, cwd=cwd_s) + + def write_text(self, path, content: str) -> None: + """Write *content* to *path* inside the container. + + Round-tripped through base64 so arbitrary text (quotes, newlines, + shell metacharacters) survives the shell without escaping. + """ + import base64 + + data = base64.b64encode(content.encode()).decode() + self.run("sh", "-c", f"echo '{data}' | base64 -d > '{path}'") diff --git a/src/fetch.py b/src/fetch.py index 008aa6a..bd78815 100644 --- a/src/fetch.py +++ b/src/fetch.py @@ -26,11 +26,16 @@ def cache_lock(layout: Layout): f.close() -def fetch_tarball(layout: Layout, src: Tarball) -> Path: +def fetch_tarball(layout: Layout, src: Tarball, *, strict: bool = False) -> Path: dest = layout.tarball_cache / src.sha256 if dest.is_file(): return dest if src.sha256 == "?": + if strict: + raise RuntimeError( + f"{src.url}: missing checksum (sha256=\"?\") is not allowed in " + f"strict mode; pin the real sha256 in the recipe" + ) log.warn(f"fetching {src.url} (sha256 unknown)") else: log.info(f"fetching {src.url}") @@ -82,10 +87,10 @@ def fetch_git(layout: Layout, src: Git) -> Path: return dest -def fetch(layout: Layout, src) -> Path: +def fetch(layout: Layout, src, *, strict: bool = False) -> Path: with cache_lock(layout): if isinstance(src, Tarball): - return fetch_tarball(layout, src) + return fetch_tarball(layout, src, strict=strict) if isinstance(src, Git): return fetch_git(layout, src) raise TypeError(f"unknown source type {type(src).__name__}") @@ -165,9 +170,11 @@ def apply_patches(tree: Path, recipe_dir: Path, patches: tuple[str, ...]) -> Non _patched_marker(tree).write_text("\n".join(patches) + "\n") -def prepare_source(layout: Layout, recipe_dir: Path, src, tree: Path) -> None: +def prepare_source( + layout: Layout, recipe_dir: Path, src, tree: Path, *, strict: bool = False +) -> None: """Fetch + extract + patch into `tree`. Idempotent via marker files.""" - cache_path = fetch(layout, src) + cache_path = fetch(layout, src, strict=strict) expected_patches = "\n".join(src.patches) + "\n" if src.patches else "\n" if ( _patched_marker(tree).is_file() diff --git a/src/lib/meson.py b/src/lib/meson.py index 6ce879c..bd58a06 100644 --- a/src/lib/meson.py +++ b/src/lib/meson.py @@ -1,5 +1,98 @@ +# A cross `llvm-config`. The target llvm-config is a glibc binary that can't run +# on the (musl) build host, so instead of executing it we emulate the queries +# meson/mesa make, reporting paths inside the sysroot. Self-configuring: it reads +# the LLVM version, library soname, and built targets from the installed sysroot, +# so it works for any LLVM consumer (mesa now, rust/etc. later) with no hardcoding. +_LLVM_CONFIG_EMULATOR = r'''#!/usr/bin/env python3 +import glob, re, sys + +SYSROOT = "/sysroot" +LIBDIR = SYSROOT + "/usr/lib" +INCDIR = SYSROOT + "/usr/include" + +_major = "" +for _p in sorted(glob.glob(LIBDIR + "/libLLVM-*.so*")): + _m = re.search(r"libLLVM-(\d+)", _p) + if _m: + _major = _m.group(1) + break + +_version = (_major + ".0.0") if _major else "0.0.0" +try: + with open(INCDIR + "/llvm/Config/llvm-config.h") as _f: + _m = re.search(r'LLVM_VERSION_STRING\s+"([^"]+)"', _f.read()) + if _m: + _version = _m.group(1) +except OSError: + pass + +_targets = [] +try: + with open(INCDIR + "/llvm/Config/Targets.def") as _f: + _targets = re.findall(r"LLVM_TARGET\((\w+)\)", _f.read()) +except OSError: + pass +if not _targets: + _targets = ["X86", "AMDGPU"] + +_GENERIC = ( + "aggressiveinstcombine all all-targets analysis asmparser asmprinter " + "binaryformat bitreader bitstreamreader bitwriter cfguard codegen " + "codegentypes core coroutines coverage debuginfocodeview debuginfodwarf " + "debuginfomsf debuginfopdb demangle dlltooldriver engine executionengine " + "extensions frontenddriver frontendhlsl frontendoffloading frontendopenmp " + "fuzzmutate globalisel instcombine instrumentation interpreter ipo irreader " + "irprinter jitlink libdriver lineeditor linker lto mc mca mcdisassembler " + "mcjit mcparser native nativecodegen object objectyaml option orcjit " + "orcshared orctargetprocess passes profiledata remarks runtimedyld " + "scalaropts selectiondag support symbolize target targetparser textapi " + "transformutils vectorize windowsdriver windowsmanifest" +).split() + + +def _components(): + comps = list(_GENERIC) + for t in _targets: + tl = t.lower() + comps += [tl, tl + "asmparser", tl + "codegen", tl + "desc", + tl + "disassembler", tl + "info", tl + "targetmca", tl + "utils"] + return " ".join(sorted(set(comps))) + + +_H = { + "--version": lambda: _version, + "--components": _components, + "--targets-built": lambda: " ".join(_targets), + "--prefix": lambda: SYSROOT + "/usr", + "--bindir": lambda: SYSROOT + "/usr/bin", + "--includedir": lambda: INCDIR, + "--libdir": lambda: LIBDIR, + "--cmakedir": lambda: LIBDIR + "/cmake/llvm", + "--has-rtti": lambda: "YES", + "--shared-mode": lambda: "shared", + "--libs": lambda: "-lLLVM-" + _major, + "--system-libs": lambda: "", + "--cflags": lambda: "-I" + INCDIR, + "--cppflags": lambda: ("-I" + INCDIR + + " -D__STDC_CONSTANT_MACROS" + + " -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS"), + "--cxxflags": lambda: ("-I" + INCDIR + + " -D__STDC_CONSTANT_MACROS" + + " -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS"), + "--ldflags": lambda: "-L" + LIBDIR, +} + +for _a in sys.argv[1:]: + if _a in _H: + print(_H[_a]()) +''' + + def meson_cross_file(self): cross = self.build_dir / "meson-cross.ini" + llvm_config = self.build_dir / "llvm-config" + self.write_text(llvm_config, _LLVM_CONFIG_EMULATOR) + self.run("chmod", "+x", llvm_config) self.write_text( cross, f"""\ @@ -12,6 +105,8 @@ objcopy = '{self.triple}-objcopy' ranlib = '{self.triple}-ranlib' strip = '{self.triple}-strip' pkg-config = '{self.triple}-pkg-config' +cmake = '/usr/bin/cmake' +llvm-config = '{llvm_config}' [host_machine] system = 'linux' diff --git a/src/recipe.py b/src/recipe.py index 4de125f..e2c8765 100644 --- a/src/recipe.py +++ b/src/recipe.py @@ -158,8 +158,8 @@ def _load_one( else: for k, v in multi_items: sources[k] = v - else: - raise ValueError(f"{name}: 'source' or 'sources' required") + # else: no source. Allowed for config-only recipes (e.g. base-files); the + # presence of at least one phase is validated below. if pure: for s in sources.values(): @@ -191,6 +191,11 @@ def _load_one( # Purely declarative pkgs are unusual but allowed pass + if not sources and not phases: + raise ValueError( + f"{name}: define a 'source'/'sources' or at least one build phase" + ) + enabled = bool(getattr(mod, "build_if", True)) return Recipe(