things
This commit is contained in:
Generated
+1
@@ -458,6 +458,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"hex",
|
"hex",
|
||||||
|
"libc",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ license = "MIT"
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
libc = "0.2"
|
||||||
reqwest = { version = "0.12", default-features = false, features = [
|
reqwest = { version = "0.12", default-features = false, features = [
|
||||||
"blocking",
|
"blocking",
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ RUN apk upgrade --no-cache && apk add --no-cache \
|
|||||||
file \
|
file \
|
||||||
findutils \
|
findutils \
|
||||||
flex \
|
flex \
|
||||||
|
gawk \
|
||||||
gettext-dev \
|
gettext-dev \
|
||||||
git \
|
git \
|
||||||
|
grep \
|
||||||
gzip \
|
gzip \
|
||||||
elfutils-dev \
|
elfutils-dev \
|
||||||
gmp-dev \
|
gmp-dev \
|
||||||
@@ -26,6 +28,9 @@ RUN apk upgrade --no-cache && apk add --no-cache \
|
|||||||
libtool \
|
libtool \
|
||||||
linux-headers \
|
linux-headers \
|
||||||
meson \
|
meson \
|
||||||
|
mtools \
|
||||||
|
nasm \
|
||||||
|
ncurses \
|
||||||
ninja \
|
ninja \
|
||||||
openssl \
|
openssl \
|
||||||
openssl-dev \
|
openssl-dev \
|
||||||
|
|||||||
@@ -38,4 +38,13 @@ options = dict(
|
|||||||
ldflags = target_ldflags,
|
ldflags = target_ldflags,
|
||||||
|
|
||||||
libc = libc,
|
libc = libc,
|
||||||
|
|
||||||
|
prefix = "/usr",
|
||||||
|
bindir = "/usr/bin",
|
||||||
|
sbindir = "/usr/bin",
|
||||||
|
libdir = "/usr/lib",
|
||||||
|
libexecdir = "/usr/libexec",
|
||||||
|
includedir = "/usr/include",
|
||||||
|
sysconfdir = "/etc",
|
||||||
|
localstatedir = "/var",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ source = tarball_source(
|
|||||||
def configure(ctx):
|
def configure(ctx):
|
||||||
configure_args = [
|
configure_args = [
|
||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=/",
|
||||||
"--target=" + options.target_triple,
|
"--target=" + options.target_triple,
|
||||||
"--with-sysroot=" + ctx.sysroot,
|
"--with-sysroot=" + ctx.sysroot,
|
||||||
"--with-pic",
|
"--with-pic",
|
||||||
@@ -28,10 +28,9 @@ def configure(ctx):
|
|||||||
"--enable-threads",
|
"--enable-threads",
|
||||||
"--disable-nls",
|
"--disable-nls",
|
||||||
"--disable-werror",
|
"--disable-werror",
|
||||||
]
|
|
||||||
# gprofng's libcollector relies on glibc-specific internals.
|
# gprofng's libcollector relies on glibc-specific internals.
|
||||||
if options.libc == "musl":
|
"--disable-gprofng",
|
||||||
configure_args.append("--disable-gprofng")
|
]
|
||||||
|
|
||||||
ctx.run(configure_args, env = {
|
ctx.run(configure_args, env = {
|
||||||
"CFLAGS": options.host_cflags,
|
"CFLAGS": options.host_cflags,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def configure(ctx):
|
|||||||
ctx.run([
|
ctx.run([
|
||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--target=" + options.target_triple,
|
"--target=" + options.target_triple,
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=/",
|
||||||
"--with-sysroot=" + ctx.sysroot,
|
"--with-sysroot=" + ctx.sysroot,
|
||||||
"--without-headers",
|
"--without-headers",
|
||||||
"--with-newlib",
|
"--with-newlib",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def configure(ctx):
|
|||||||
ctx.run([
|
ctx.run([
|
||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--target=" + options.target_triple,
|
"--target=" + options.target_triple,
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=/",
|
||||||
"--with-sysroot=" + ctx.sysroot,
|
"--with-sysroot=" + ctx.sysroot,
|
||||||
"--with-build-sysroot=" + ctx.sysroot,
|
"--with-build-sysroot=" + ctx.sysroot,
|
||||||
"--enable-languages=c,c++,lto",
|
"--enable-languages=c,c++,lto",
|
||||||
@@ -48,7 +48,6 @@ def build(ctx):
|
|||||||
|
|
||||||
def install(ctx, pkg):
|
def install(ctx, pkg):
|
||||||
ctx.run(["make", "install-strip"], env = {"DESTDIR": pkg.dest_dir})
|
ctx.run(["make", "install-strip"], env = {"DESTDIR": pkg.dest_dir})
|
||||||
# Drop libtool archives — they bake build-time paths into target programs.
|
|
||||||
ctx.run([
|
# Drop libtool archives.
|
||||||
"find", pkg.dest_dir + ctx.prefix, "-name", "*.la", "-delete",
|
ctx.run(["find", pkg.dest_dir, "-name", "*.la", "-delete"])
|
||||||
])
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def configure(ctx):
|
|||||||
"-B", ctx.build_dir,
|
"-B", ctx.build_dir,
|
||||||
"-G", "Ninja",
|
"-G", "Ninja",
|
||||||
"-DCMAKE_BUILD_TYPE=Release",
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
"-DCMAKE_INSTALL_PREFIX=" + ctx.prefix,
|
"-DCMAKE_INSTALL_PREFIX=/",
|
||||||
"-DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra;lld",
|
"-DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra;lld",
|
||||||
"-DLLVM_ENABLE_RUNTIMES=compiler-rt",
|
"-DLLVM_ENABLE_RUNTIMES=compiler-rt",
|
||||||
"-DLLVM_TARGETS_TO_BUILD=X86;AArch64;RISCV",
|
"-DLLVM_TARGETS_TO_BUILD=X86;AArch64;RISCV",
|
||||||
|
|||||||
+13
-34
@@ -1,4 +1,4 @@
|
|||||||
# Commonly used helpers.
|
# Autotools
|
||||||
|
|
||||||
def autotools_configure(ctx, extra_args = [], extra_env = {}):
|
def autotools_configure(ctx, extra_args = [], extra_env = {}):
|
||||||
env = {
|
env = {
|
||||||
@@ -11,12 +11,12 @@ def autotools_configure(ctx, extra_args = [], extra_env = {}):
|
|||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--host=" + options.target_triple,
|
"--host=" + options.target_triple,
|
||||||
"--with-sysroot=" + ctx.sysroot,
|
"--with-sysroot=" + ctx.sysroot,
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=" + options.prefix,
|
||||||
"--sysconfdir=/etc",
|
"--sysconfdir=" + options.sysconfdir,
|
||||||
"--localstatedir=/var",
|
"--localstatedir=" + options.localstatedir,
|
||||||
"--bindir=" + ctx.prefix / "bin",
|
"--bindir=" + options.bindir,
|
||||||
"--sbindir=" + ctx.prefix / "bin",
|
"--sbindir=" + options.sbindir,
|
||||||
"--libdir=" + ctx.prefix / "lib",
|
"--libdir=" + options.libdir,
|
||||||
"--disable-static",
|
"--disable-static",
|
||||||
"--enable-shared",
|
"--enable-shared",
|
||||||
] + extra_args, env = env)
|
] + extra_args, env = env)
|
||||||
@@ -25,7 +25,7 @@ def autotools_build(ctx, extra_args = []):
|
|||||||
ctx.run(["make", "-j" + str(ctx.jobs)] + extra_args)
|
ctx.run(["make", "-j" + str(ctx.jobs)] + extra_args)
|
||||||
|
|
||||||
def autotools_install(ctx, pkg, extra_args = []):
|
def autotools_install(ctx, pkg, extra_args = []):
|
||||||
ctx.run(["make", "install", "DESTDIR=" + pkg.dest_dir] + extra_args)
|
ctx.run(["make", "install"] + extra_args, env = { "DESTDIR": pkg.dest_dir })
|
||||||
|
|
||||||
def autotools(configure_args = [], configure_env = {}, build_args = [], install_args = []):
|
def autotools(configure_args = [], configure_env = {}, build_args = [], install_args = []):
|
||||||
def _configure(ctx):
|
def _configure(ctx):
|
||||||
@@ -36,29 +36,7 @@ def autotools(configure_args = [], configure_env = {}, build_args = [], install_
|
|||||||
autotools_install(ctx, pkg, extra_args = install_args)
|
autotools_install(ctx, pkg, extra_args = install_args)
|
||||||
return _configure, _build, _install
|
return _configure, _build, _install
|
||||||
|
|
||||||
def host_autotools_configure(ctx, extra_args = [], extra_env = {}):
|
# CMake
|
||||||
env = {
|
|
||||||
"CFLAGS": options.host_cflags,
|
|
||||||
"CXXFLAGS": options.host_cxxflags,
|
|
||||||
"LDFLAGS": options.host_ldflags,
|
|
||||||
}
|
|
||||||
env.update(extra_env)
|
|
||||||
ctx.run([
|
|
||||||
ctx.source_dir / "configure",
|
|
||||||
"--prefix=" + ctx.prefix,
|
|
||||||
"--sysconfdir=/etc",
|
|
||||||
"--localstatedir=/var",
|
|
||||||
"--disable-nls",
|
|
||||||
] + extra_args, env = env)
|
|
||||||
|
|
||||||
def host_autotools(configure_args = [], configure_env = {}, build_args = [], install_args = []):
|
|
||||||
def _configure(ctx):
|
|
||||||
host_autotools_configure(ctx, extra_args = configure_args, extra_env = configure_env)
|
|
||||||
def _build(ctx):
|
|
||||||
autotools_build(ctx, extra_args = build_args)
|
|
||||||
def _install(ctx, pkg):
|
|
||||||
autotools_install(ctx, pkg, extra_args = install_args)
|
|
||||||
return _configure, _build, _install
|
|
||||||
|
|
||||||
def cmake_configure(ctx, extra_args = [], extra_env = {}, host = False):
|
def cmake_configure(ctx, extra_args = [], extra_env = {}, host = False):
|
||||||
if host:
|
if host:
|
||||||
@@ -92,9 +70,9 @@ def cmake_configure(ctx, extra_args = [], extra_env = {}, host = False):
|
|||||||
"-B", ctx.build_dir,
|
"-B", ctx.build_dir,
|
||||||
"-G", "Ninja",
|
"-G", "Ninja",
|
||||||
"-DCMAKE_BUILD_TYPE=Release",
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
"-DCMAKE_INSTALL_PREFIX=" + ctx.prefix,
|
"-DCMAKE_INSTALL_PREFIX=" + options.prefix,
|
||||||
"-DCMAKE_INSTALL_SYSCONFDIR=/etc",
|
"-DCMAKE_INSTALL_SYSCONFDIR=" + options.sysconfdir,
|
||||||
"-DCMAKE_INSTALL_LOCALSTATEDIR=/var",
|
"-DCMAKE_INSTALL_LOCALSTATEDIR=" + options.localstatedir,
|
||||||
] + toolchain_args + extra_args, env = env)
|
] + toolchain_args + extra_args, env = env)
|
||||||
|
|
||||||
def cmake_build(ctx, extra_args = []):
|
def cmake_build(ctx, extra_args = []):
|
||||||
@@ -114,3 +92,4 @@ def cmake(configure_args = [], configure_env = {}, build_args = [], install_args
|
|||||||
def _install(ctx, pkg):
|
def _install(ctx, pkg):
|
||||||
cmake_install(ctx, pkg, extra_args = install_args)
|
cmake_install(ctx, pkg, extra_args = install_args)
|
||||||
return _configure, _build, _install
|
return _configure, _build, _install
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ source = tarball_source(
|
|||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
deps = ["ncurses", "readline"]
|
deps = [options.libc, "ncurses", "readline"]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_args = [
|
configure, build, install = autotools(configure_args = [
|
||||||
"--without-bash-malloc",
|
"--without-bash-malloc",
|
||||||
@@ -19,4 +19,4 @@ configure, build, install = autotools(configure_args = [
|
|||||||
"--enable-readline",
|
"--enable-readline",
|
||||||
"--enable-history",
|
"--enable-history",
|
||||||
"--enable-job-control",
|
"--enable-job-control",
|
||||||
])
|
], configure_env = {"CFLAGS": "-std=gnu17"})
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
version = "1.0.8"
|
|
||||||
revision = 1
|
|
||||||
metadata = meta(
|
|
||||||
description = "Block-sorting file compressor",
|
|
||||||
license = "bzip2-1.0.6",
|
|
||||||
website = "https://sourceware.org/bzip2/",
|
|
||||||
)
|
|
||||||
source = tarball_source(
|
|
||||||
url = f"https://sourceware.org/pub/bzip2/bzip2-{version}.tar.gz",
|
|
||||||
sha256 = "?",
|
|
||||||
strip_components = 1,
|
|
||||||
)
|
|
||||||
host_deps = ["binutils", "gcc"]
|
|
||||||
|
|
||||||
# bzip2 ships only a plain Makefile, no configure script.
|
|
||||||
|
|
||||||
def build(ctx):
|
|
||||||
# Copy sources into the build dir so the in-tree Makefile can write here.
|
|
||||||
ctx.run(["cp", "-rp", ctx.source_dir / ".", ctx.build_dir])
|
|
||||||
jobs = "-j" + str(ctx.jobs)
|
|
||||||
common = [
|
|
||||||
"CC=" + options.target_triple + "-gcc",
|
|
||||||
"AR=" + options.target_triple + "-ar",
|
|
||||||
"RANLIB=" + options.target_triple + "-ranlib",
|
|
||||||
"CFLAGS=" + options.cflags + " -D_FILE_OFFSET_BITS=64",
|
|
||||||
]
|
|
||||||
ctx.run(["make", jobs, "-f", "Makefile-libbz2_so"] + common)
|
|
||||||
ctx.run(["make", jobs, "libbz2.a", "bzip2", "bzip2recover"] + common)
|
|
||||||
|
|
||||||
def install(ctx, pkg):
|
|
||||||
ctx.run(["make", "install", "PREFIX=" + pkg.dest_dir + ctx.prefix])
|
|
||||||
# Install the shared library that the auxiliary Makefile produced.
|
|
||||||
libdir = pkg.dest_dir + ctx.prefix / "lib"
|
|
||||||
bindir = pkg.dest_dir + ctx.prefix / "bin"
|
|
||||||
ctx.run(["mkdir", "-p", libdir, bindir])
|
|
||||||
ctx.run(["cp", "-a", "libbz2.so." + version, libdir])
|
|
||||||
ctx.run(["ln", "-sf", "libbz2.so." + version, libdir + "/libbz2.so.1.0"])
|
|
||||||
ctx.run(["ln", "-sf", "libbz2.so." + version, libdir + "/libbz2.so.1"])
|
|
||||||
ctx.run(["ln", "-sf", "libbz2.so." + version, libdir + "/libbz2.so"])
|
|
||||||
@@ -7,7 +7,7 @@ metadata = meta(
|
|||||||
)
|
)
|
||||||
source = tarball_source(
|
source = tarball_source(
|
||||||
url = f"https://ftp.gnu.org/gnu/coreutils/coreutils-{version}.tar.xz",
|
url = f"https://ftp.gnu.org/gnu/coreutils/coreutils-{version}.tar.xz",
|
||||||
sha256 = "?",
|
sha256 = "7a0124327b398fd9eb1a6abde583389821422c744ffa10734b24f557610d3283",
|
||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
|||||||
+6
-17
@@ -16,12 +16,9 @@ deps = ["linux-headers"]
|
|||||||
build_if = options.libc == "glibc"
|
build_if = options.libc == "glibc"
|
||||||
|
|
||||||
def configure(ctx):
|
def configure(ctx):
|
||||||
ctx.run([
|
autotools_configure(ctx, [
|
||||||
ctx.source_dir / "configure",
|
|
||||||
"--host=" + options.target_triple,
|
|
||||||
"--build=" + options.target_triple,
|
"--build=" + options.target_triple,
|
||||||
"--prefix=/usr",
|
"--with-headers=" + ctx.sysroot / options.prefix / "include",
|
||||||
"--with-headers=" + ctx.sysroot / "usr/include",
|
|
||||||
"--enable-kernel=5.4",
|
"--enable-kernel=5.4",
|
||||||
"--enable-bind-now",
|
"--enable-bind-now",
|
||||||
"--enable-stack-protector=strong",
|
"--enable-stack-protector=strong",
|
||||||
@@ -31,17 +28,9 @@ def configure(ctx):
|
|||||||
"--disable-nscd",
|
"--disable-nscd",
|
||||||
"--without-selinux",
|
"--without-selinux",
|
||||||
"--without-gd",
|
"--without-gd",
|
||||||
|
"libc_cv_slibdir=/lib",
|
||||||
|
"libc_cv_rtlddir=/lib",
|
||||||
"libc_cv_forced_unwind=yes",
|
"libc_cv_forced_unwind=yes",
|
||||||
"libc_cv_ssp=no",
|
])
|
||||||
"libc_cv_ssp_strong=yes",
|
|
||||||
], env = {
|
|
||||||
"CC": options.target_triple + "-gcc",
|
|
||||||
"CXX": options.target_triple + "-g++",
|
|
||||||
"CFLAGS": options.cflags,
|
|
||||||
})
|
|
||||||
|
|
||||||
def build(ctx):
|
_, build, install = autotools()
|
||||||
ctx.run(["make", "-j" + str(ctx.jobs)])
|
|
||||||
|
|
||||||
def install(ctx, pkg):
|
|
||||||
ctx.run(["make", "install", "DESTDIR=" + pkg.dest_dir])
|
|
||||||
|
|||||||
+34
-6
@@ -15,9 +15,37 @@ deps = [options.libc]
|
|||||||
|
|
||||||
build_if = options.target_arch in ["x86_64", "aarch64", "riscv64", "loongarch64"]
|
build_if = options.target_arch in ["x86_64", "aarch64", "riscv64", "loongarch64"]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_env = {
|
arch_configure_args = {
|
||||||
"TOOLCHAIN_FOR_TARGET": options.target_triple + "-",
|
"x86_64": ["--enable-uefi-x86-64", "--enable-uefi-ia32", "--enable-bios", "--enable-bios-cd"],
|
||||||
"LD_FOR_TARGET": options.target_triple + "-" + "ld",
|
"aarch64": ["--enable-uefi-aarch64"],
|
||||||
"OBJCOPY_FOR_TARGET": options.target_triple + "-" + "objcopy",
|
"riscv64": ["--enable-uefi-riscv64"],
|
||||||
"OBJDUMP_FOR_TARGET": options.target_triple + "-" + "objdump",
|
"loongarch64": ["--enable-uefi-loongarch64"],
|
||||||
})
|
}
|
||||||
|
|
||||||
|
configure, build, install = autotools(
|
||||||
|
configure_args = ["--enable-uefi-cd"] + arch_configure_args.get(options.target_arch),
|
||||||
|
configure_env = {"TOOLCHAIN_FOR_TARGET": options.target_triple + "-"},
|
||||||
|
)
|
||||||
|
|
||||||
|
subpackages = [
|
||||||
|
subpackage(
|
||||||
|
"limine-uefi",
|
||||||
|
meta(description = "UEFI files"),
|
||||||
|
[
|
||||||
|
"usr/share/limine/BOOT*.EFI",
|
||||||
|
"usr/share/limine/limine-uefi-*.bin",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
if options.target_arch == "x86_64":
|
||||||
|
subpackages += [
|
||||||
|
subpackage(
|
||||||
|
"limine-bios",
|
||||||
|
meta(description = "BIOS files"),
|
||||||
|
[
|
||||||
|
"usr/share/limine/limine-bios*",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ def build(ctx):
|
|||||||
ctx.run(["find", ctx.build_dir / "usr/include", "-type", "f", "!", "-name", "*.h", "-delete"])
|
ctx.run(["find", ctx.build_dir / "usr/include", "-type", "f", "!", "-name", "*.h", "-delete"])
|
||||||
|
|
||||||
def install(ctx, pkg):
|
def install(ctx, pkg):
|
||||||
ctx.run(["mkdir", "-p", pkg.dest_dir + ctx.prefix])
|
ctx.run(["mkdir", "-p", pkg.dest_dir / options.prefix])
|
||||||
ctx.run(["cp", "-rp", ctx.build_dir / "usr/include", pkg.dest_dir / ctx.prefix])
|
ctx.run(["cp", "-rp", ctx.build_dir / "usr/include", pkg.dest_dir / options.prefix])
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# TODO
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ source = tarball_source(
|
|||||||
|
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
|
||||||
def _make_args(ctx, *args):
|
def make_args(ctx, *args):
|
||||||
# Translate arch name
|
# Translate arch name
|
||||||
if options.target_arch == "aarch64":
|
if options.target_arch == "aarch64":
|
||||||
linux_arch = "arm64"
|
linux_arch = "arm64"
|
||||||
@@ -22,8 +22,6 @@ def _make_args(ctx, *args):
|
|||||||
|
|
||||||
result = [
|
result = [
|
||||||
"make",
|
"make",
|
||||||
"-C", ctx.source_dir,
|
|
||||||
"O=" + ctx.build_dir,
|
|
||||||
"ARCH=" + linux_arch,
|
"ARCH=" + linux_arch,
|
||||||
"CROSS_COMPILE=" + options.target_triple + "-",
|
"CROSS_COMPILE=" + options.target_triple + "-",
|
||||||
"-j" + str(ctx.jobs),
|
"-j" + str(ctx.jobs),
|
||||||
@@ -32,10 +30,12 @@ def _make_args(ctx, *args):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def configure(ctx):
|
def configure(ctx):
|
||||||
ctx.run(_make_args(ctx, "defconfig"))
|
ctx.run(["cp", "-rp", ctx.source_dir / ".", ctx.build_dir])
|
||||||
|
ctx.run(["cp", ctx.files / "config." + options.target_arch, ctx.build_dir / ".config"])
|
||||||
|
ctx.run(make_args(ctx, "olddefconfig"))
|
||||||
|
|
||||||
def build(ctx):
|
def build(ctx):
|
||||||
ctx.run(_make_args(ctx, "bzImage"))
|
ctx.run(make_args(ctx))
|
||||||
|
|
||||||
def install(ctx, pkg):
|
def install(ctx, pkg):
|
||||||
ctx.install(
|
ctx.install(
|
||||||
|
|||||||
+2
-1
@@ -7,10 +7,11 @@ metadata = meta(
|
|||||||
)
|
)
|
||||||
source = tarball_source(
|
source = tarball_source(
|
||||||
url = f"https://ftp.gnu.org/gnu/make/make-{version}.tar.gz",
|
url = f"https://ftp.gnu.org/gnu/make/make-{version}.tar.gz",
|
||||||
sha256 = "?",
|
sha256 = "dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3",
|
||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
deps = [options.libc]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_args = [
|
configure, build, install = autotools(configure_args = [
|
||||||
"--without-guile",
|
"--without-guile",
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ def configure(ctx):
|
|||||||
ctx.run([
|
ctx.run([
|
||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--target=" + options.target_triple,
|
"--target=" + options.target_triple,
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=" + options.prefix,
|
||||||
"--syslibdir=/lib",
|
"--syslibdir=/lib",
|
||||||
], env = {
|
], env = {
|
||||||
"CC": options.target_triple + "-gcc",
|
"CC": options.target_triple + "-gcc",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ source = tarball_source(
|
|||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
deps = [options.libc]
|
deps = [options.libc]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_args = [
|
configure, build, _ = autotools(configure_args = [
|
||||||
"--with-shared",
|
"--with-shared",
|
||||||
"--without-debug",
|
"--without-debug",
|
||||||
"--without-ada",
|
"--without-ada",
|
||||||
@@ -31,3 +31,8 @@ configure, build, install = autotools(configure_args = [
|
|||||||
"cf_cv_builtin_bool": "1",
|
"cf_cv_builtin_bool": "1",
|
||||||
"ac_cv_header_stdbool_h": "yes",
|
"ac_cv_header_stdbool_h": "yes",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def install(ctx, pkg):
|
||||||
|
autotools_install(ctx, pkg, extra_args = [
|
||||||
|
"DESTDIR=" + pkg.dest_dir
|
||||||
|
])
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def configure(ctx):
|
|||||||
ctx.run([
|
ctx.run([
|
||||||
ctx.source_dir / "Configure",
|
ctx.source_dir / "Configure",
|
||||||
ossl_target,
|
ossl_target,
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=" + options.prefix,
|
||||||
"--openssldir=/etc/ssl",
|
"--openssldir=/etc/ssl",
|
||||||
"--libdir=lib",
|
"--libdir=lib",
|
||||||
"shared",
|
"shared",
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ source = tarball_source(
|
|||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
deps = [options.libc]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_args = [
|
configure, build, install = autotools(configure_args = [
|
||||||
"--with-system-libdir=/usr/lib",
|
"--with-system-libdir=" + options.libdir,
|
||||||
"--with-system-includedir=/usr/include",
|
"--with-system-includedir=" + options.includedir,
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ source = tarball_source(
|
|||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
deps = ["ncurses"]
|
deps = [options.libc, "ncurses"]
|
||||||
|
|
||||||
configure, build, install = autotools(
|
configure, build, _ = autotools(
|
||||||
configure_args = ["--with-curses"],
|
configure_args = ["--with-curses"],
|
||||||
configure_env = {
|
configure_env = {
|
||||||
# Force linking against the system curses; otherwise readline's
|
# Force linking against the system curses; otherwise readline's
|
||||||
@@ -21,3 +21,9 @@ configure, build, install = autotools(
|
|||||||
"bash_cv_termcap_lib": "ncursesw",
|
"bash_cv_termcap_lib": "ncursesw",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Readline overwrites DESTDIR
|
||||||
|
def install(ctx, pkg):
|
||||||
|
autotools_install(ctx, pkg, extra_args = [
|
||||||
|
"DESTDIR=" + pkg.dest_dir
|
||||||
|
])
|
||||||
|
|||||||
+2
-1
@@ -7,10 +7,11 @@ metadata = meta(
|
|||||||
)
|
)
|
||||||
source = tarball_source(
|
source = tarball_source(
|
||||||
url = f"https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.xz",
|
url = f"https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.xz",
|
||||||
sha256 = "?",
|
sha256 = "db0590629b6f0fa36e74aea5f9731dc6f8df068ce7b7bafa45301832a5eebc3a",
|
||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
deps = [options.libc]
|
||||||
|
|
||||||
configure, build, install = autotools(configure_args = [
|
configure, build, install = autotools(configure_args = [
|
||||||
"--disable-doc",
|
"--disable-doc",
|
||||||
|
|||||||
+6
-5
@@ -1,4 +1,4 @@
|
|||||||
version = "1.3.1"
|
version = "1.3.2"
|
||||||
revision = 1
|
revision = 1
|
||||||
metadata = meta(
|
metadata = meta(
|
||||||
description = "Lossless data-compression library",
|
description = "Lossless data-compression library",
|
||||||
@@ -7,19 +7,20 @@ metadata = meta(
|
|||||||
)
|
)
|
||||||
source = tarball_source(
|
source = tarball_source(
|
||||||
url = f"https://zlib.net/zlib-{version}.tar.xz",
|
url = f"https://zlib.net/zlib-{version}.tar.xz",
|
||||||
sha256 = "?",
|
sha256 = "d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3",
|
||||||
strip_components = 1,
|
strip_components = 1,
|
||||||
)
|
)
|
||||||
host_deps = ["binutils", "gcc"]
|
host_deps = ["binutils", "gcc"]
|
||||||
|
deps = [options.libc]
|
||||||
|
|
||||||
def configure(ctx):
|
def configure(ctx):
|
||||||
# zlib ships its own ./configure that does not understand the usual
|
# zlib ships its own ./configure that does not understand the usual
|
||||||
# autoconf flags (no --host, --build, etc.), so it is invoked directly.
|
# autoconf flags (no --host, --build, etc.), so it is invoked directly.
|
||||||
ctx.run([
|
ctx.run([
|
||||||
ctx.source_dir / "configure",
|
ctx.source_dir / "configure",
|
||||||
"--prefix=" + ctx.prefix,
|
"--prefix=" + options.prefix,
|
||||||
"--libdir=" + ctx.prefix / "lib",
|
"--libdir=" + options.libdir,
|
||||||
"--sharedlibdir=" + ctx.prefix / "lib",
|
"--sharedlibdir=" + options.libdir,
|
||||||
], env = {
|
], env = {
|
||||||
"CC": options.target_triple + "-gcc",
|
"CC": options.target_triple + "-gcc",
|
||||||
"AR": options.target_triple + "-ar",
|
"AR": options.target_triple + "-ar",
|
||||||
|
|||||||
+172
-29
@@ -26,6 +26,36 @@ use crate::{
|
|||||||
recipe::{GitSource, OutputPackage, Recipe, RecipeKind, RecipeSet, Source},
|
recipe::{GitSource, OutputPackage, Recipe, RecipeKind, RecipeSet, Source},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SPLIT_SUBPACKAGE_SCRIPT: &str = r#"
|
||||||
|
base=$1
|
||||||
|
dest=$2
|
||||||
|
package=$3
|
||||||
|
shift 3
|
||||||
|
|
||||||
|
mkdir -p "$dest"
|
||||||
|
for pattern do
|
||||||
|
matches=$(mktemp)
|
||||||
|
find "$base" -depth -path "$base/$pattern" -print > "$matches"
|
||||||
|
if ! [ -s "$matches" ]; then
|
||||||
|
echo "subpackage $package: glob '$pattern' matched no files under $base" >&2
|
||||||
|
rm -f "$matches"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r path; do
|
||||||
|
[ "$path" != "$base" ] || continue
|
||||||
|
rel=${path#"$base"/}
|
||||||
|
target=$dest/$rel
|
||||||
|
mkdir -p "$(dirname "$target")"
|
||||||
|
echo "$rel"
|
||||||
|
mv "$path" "$target"
|
||||||
|
done < "$matches"
|
||||||
|
rm -f "$matches"
|
||||||
|
done
|
||||||
|
|
||||||
|
find "$base" -depth -type d -empty -delete
|
||||||
|
"#;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
@@ -52,7 +82,7 @@ impl Builder {
|
|||||||
let requested = filter_skipped(recipes, requested);
|
let requested = filter_skipped(recipes, requested);
|
||||||
let plan = TaskPlanner::new(&self.root, &self.config.arch, recipes)
|
let plan = TaskPlanner::new(&self.root, &self.config.arch, recipes)
|
||||||
.build_plan(&requested, rebuild)?;
|
.build_plan(&requested, rebuild)?;
|
||||||
self.print_plan(&plan);
|
self.print_plan(recipes, &plan)?;
|
||||||
if dry_run {
|
if dry_run {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -68,7 +98,7 @@ impl Builder {
|
|||||||
let requested = filter_skipped(recipes, requested);
|
let requested = filter_skipped(recipes, requested);
|
||||||
let plan =
|
let plan =
|
||||||
TaskPlanner::new(&self.root, &self.config.arch, recipes).fetch_plan(&requested)?;
|
TaskPlanner::new(&self.root, &self.config.arch, recipes).fetch_plan(&requested)?;
|
||||||
self.print_plan(&plan);
|
self.print_plan(recipes, &plan)?;
|
||||||
if dry_run {
|
if dry_run {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -128,6 +158,11 @@ impl Builder {
|
|||||||
let recipe = recipes.recipe(key)?;
|
let recipe = recipes.recipe(key)?;
|
||||||
self.task_prepare_sources(layout, recipe)
|
self.task_prepare_sources(layout, recipe)
|
||||||
}
|
}
|
||||||
|
TaskId::PrepareRecipe(key) => {
|
||||||
|
let recipe = recipes.recipe(key)?;
|
||||||
|
let active = active.expect("recipe prepare task requires an active container");
|
||||||
|
self.task_prepare_recipe(layout, recipe, active)
|
||||||
|
}
|
||||||
TaskId::ConfigureRecipe(key) => {
|
TaskId::ConfigureRecipe(key) => {
|
||||||
let recipe = recipes.recipe(key)?;
|
let recipe = recipes.recipe(key)?;
|
||||||
let active = active.expect("configure task requires an active container");
|
let active = active.expect("configure task requires an active container");
|
||||||
@@ -206,7 +241,7 @@ impl Builder {
|
|||||||
let mut tmp = tempfile::NamedTempFile::new_in(&cache_dir)
|
let mut tmp = tempfile::NamedTempFile::new_in(&cache_dir)
|
||||||
.with_context(|| format!("creating temp file in {}", cache_dir.display()))?;
|
.with_context(|| format!("creating temp file in {}", cache_dir.display()))?;
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
let mut buf = [0u8; 64 * 1024];
|
let mut buf: [u8; 65536] = [0u8; 64 * 1024];
|
||||||
loop {
|
loop {
|
||||||
let n = std::io::Read::read(&mut response, &mut buf)
|
let n = std::io::Read::read(&mut response, &mut buf)
|
||||||
.with_context(|| format!("reading response body for {url}"))?;
|
.with_context(|| format!("reading response body for {url}"))?;
|
||||||
@@ -235,12 +270,6 @@ impl Builder {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if unknown {
|
|
||||||
bail!(
|
|
||||||
"{}: source sha256 is unknown; update the recipe with sha256 = \"{hash}\"",
|
|
||||||
recipe.key()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +447,22 @@ impl Builder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn task_prepare_recipe(
|
||||||
|
&self,
|
||||||
|
layout: &Layout<'_>,
|
||||||
|
recipe: &Recipe,
|
||||||
|
active: &ActiveContainer,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let Some(func) = recipe.phases().prepare() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
log::step("prepare", &format!("{} (recipe phase)", recipe.key()));
|
||||||
|
let ctx = phase_context_for(recipe);
|
||||||
|
self.invoke_with_runtime(active, &[PhaseArg::Ctx(ctx)], func)?;
|
||||||
|
self.write_recipe_stamp(layout, recipe, "prepare")
|
||||||
|
}
|
||||||
|
|
||||||
fn task_configure(
|
fn task_configure(
|
||||||
&self,
|
&self,
|
||||||
layout: &Layout<'_>,
|
layout: &Layout<'_>,
|
||||||
@@ -452,6 +497,10 @@ impl Builder {
|
|||||||
active: &ActiveContainer,
|
active: &ActiveContainer,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
log::step("install", &output.key());
|
log::step("install", &output.key());
|
||||||
|
if !output.is_base() {
|
||||||
|
return self.task_split_subpackage(layout, recipe, output, active);
|
||||||
|
}
|
||||||
|
|
||||||
let dest = format!("/dest/{}", output.name());
|
let dest = format!("/dest/{}", output.name());
|
||||||
active.container.borrow().exec(
|
active.container.borrow().exec(
|
||||||
&["mkdir".to_owned(), "-p".to_owned(), dest.clone()],
|
&["mkdir".to_owned(), "-p".to_owned(), dest.clone()],
|
||||||
@@ -468,6 +517,36 @@ impl Builder {
|
|||||||
self.write_output_stamp(layout, recipe, output, "install")
|
self.write_output_stamp(layout, recipe, output, "install")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn task_split_subpackage(
|
||||||
|
&self,
|
||||||
|
layout: &Layout<'_>,
|
||||||
|
recipe: &Recipe,
|
||||||
|
output: &OutputPackage,
|
||||||
|
active: &ActiveContainer,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let base = recipe
|
||||||
|
.base_output()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("recipe `{}` has no base output", recipe.key()))?;
|
||||||
|
let base_dest = format!("/dest/{}", base.name());
|
||||||
|
let dest = format!("/dest/{}", output.name());
|
||||||
|
let mut argv: Vec<String> = vec![
|
||||||
|
"sh".to_owned(),
|
||||||
|
"-eu".to_owned(),
|
||||||
|
"-c".to_owned(),
|
||||||
|
SPLIT_SUBPACKAGE_SCRIPT.to_owned(),
|
||||||
|
"split-subpackage".to_owned(),
|
||||||
|
base_dest,
|
||||||
|
dest,
|
||||||
|
output.name().to_owned(),
|
||||||
|
];
|
||||||
|
argv.extend(output.file_globs().iter().cloned());
|
||||||
|
active
|
||||||
|
.container
|
||||||
|
.borrow()
|
||||||
|
.exec(&argv, &base_env(&active.base_path), "/")?;
|
||||||
|
self.write_output_stamp(layout, recipe, output, "install")
|
||||||
|
}
|
||||||
|
|
||||||
fn task_install_host(
|
fn task_install_host(
|
||||||
&self,
|
&self,
|
||||||
layout: &Layout<'_>,
|
layout: &Layout<'_>,
|
||||||
@@ -594,6 +673,10 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let env = base_env(&active.base_path);
|
let env = base_env(&active.base_path);
|
||||||
|
log::step(
|
||||||
|
"sysroot",
|
||||||
|
&format!("{} from {} target apk(s)", recipe.key(), deps.len()),
|
||||||
|
);
|
||||||
for dep in deps {
|
for dep in deps {
|
||||||
let output = recipes.output(dep)?;
|
let output = recipes.output(dep)?;
|
||||||
let owning = recipes.recipe(output.recipe())?;
|
let owning = recipes.recipe(output.recipe())?;
|
||||||
@@ -616,6 +699,7 @@ impl Builder {
|
|||||||
"apk".to_owned(),
|
"apk".to_owned(),
|
||||||
"extract".to_owned(),
|
"extract".to_owned(),
|
||||||
"--allow-untrusted".to_owned(),
|
"--allow-untrusted".to_owned(),
|
||||||
|
"--force-overwrite".to_owned(),
|
||||||
"--destination".to_owned(),
|
"--destination".to_owned(),
|
||||||
"/sysroot".to_owned(),
|
"/sysroot".to_owned(),
|
||||||
format!("/pkgs/{file_name}"),
|
format!("/pkgs/{file_name}"),
|
||||||
@@ -693,10 +777,10 @@ impl Builder {
|
|||||||
let bare = dep_key.strip_prefix("host:").unwrap_or(dep_key);
|
let bare = dep_key.strip_prefix("host:").unwrap_or(dep_key);
|
||||||
mounts.push(Mount {
|
mounts.push(Mount {
|
||||||
host: install,
|
host: install,
|
||||||
container: format!("/tools/{bare}/usr/local"),
|
container: format!("/tools/{bare}"),
|
||||||
read_only: true,
|
read_only: true,
|
||||||
});
|
});
|
||||||
tools_bins.push(format!("/tools/{bare}/usr/local/bin"));
|
tools_bins.push(format!("/tools/{bare}/bin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = format!(
|
let name = format!(
|
||||||
@@ -837,23 +921,54 @@ impl Builder {
|
|||||||
Ok(hex::encode(hasher.finalize()))
|
Ok(hex::encode(hasher.finalize()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_plan(&self, plan: &TaskPlan) {
|
fn print_plan(&self, recipes: &RecipeSet, plan: &TaskPlan) -> anyhow::Result<()> {
|
||||||
if plan.is_empty() {
|
if plan.is_empty() {
|
||||||
log::skip("plan", "nothing to do");
|
log::skip("plan", "nothing to do");
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let task_count = plan.order().len();
|
||||||
|
let edge_count = plan.dependency_count();
|
||||||
log::step(
|
log::step(
|
||||||
"plan",
|
"plan",
|
||||||
&format!(
|
&format!(
|
||||||
"{} active task(s), {} edge(s)",
|
"{} {}, {} {}",
|
||||||
plan.order().len(),
|
task_count,
|
||||||
plan.dependency_count()
|
plural(task_count, "task", "tasks"),
|
||||||
|
edge_count,
|
||||||
|
plural(edge_count, "dependency edge", "dependency edges")
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
for task in plan.order() {
|
|
||||||
println!("{task}");
|
let step_width = task_count.to_string().len() * 2 + 1;
|
||||||
|
let action_width = plan
|
||||||
|
.order()
|
||||||
|
.iter()
|
||||||
|
.map(|task| plan_task_parts(task).0.len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
let mut current_recipe: Option<String> = None;
|
||||||
|
|
||||||
|
for (index, task) in plan.order().iter().enumerate() {
|
||||||
|
let recipe_slug = task_recipe_slug(task, recipes)?;
|
||||||
|
if current_recipe.as_deref() != Some(recipe_slug.as_str()) {
|
||||||
|
if current_recipe.is_some() {
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
println!(" {recipe_slug}");
|
||||||
|
current_recipe = Some(recipe_slug.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let step = format!("{}/{}", index + 1, task_count);
|
||||||
|
let (action, target) = plan_task_parts(task);
|
||||||
|
if target == recipe_slug {
|
||||||
|
println!(" {step:>step_width$} {action}");
|
||||||
|
} else {
|
||||||
|
println!(" {step:>step_width$} {action:<action_width$} {target}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abs_config_path(&self, path: &Path) -> PathBuf {
|
fn abs_config_path(&self, path: &Path) -> PathBuf {
|
||||||
@@ -874,14 +989,7 @@ struct ActiveContainer {
|
|||||||
fn filter_skipped(recipes: &RecipeSet, requested: &[String]) -> Vec<String> {
|
fn filter_skipped(recipes: &RecipeSet, requested: &[String]) -> Vec<String> {
|
||||||
requested
|
requested
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|key| {
|
.filter(|key| !recipes.is_skipped(key))
|
||||||
if recipes.is_skipped(key) {
|
|
||||||
log::skip("skip", &format!("{key} (build_if returned false)"));
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -894,9 +1002,27 @@ fn task_needs_container(task: &TaskId) -> bool {
|
|||||||
| TaskId::InstallPackageFiles(_)
|
| TaskId::InstallPackageFiles(_)
|
||||||
| TaskId::ProduceApk(_)
|
| TaskId::ProduceApk(_)
|
||||||
| TaskId::InstallHostRecipe(_)
|
| TaskId::InstallHostRecipe(_)
|
||||||
|
| TaskId::PrepareRecipe(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn plan_task_parts(task: &TaskId) -> (&'static str, &str) {
|
||||||
|
match task {
|
||||||
|
TaskId::FetchSources(recipe) => ("fetch sources", recipe),
|
||||||
|
TaskId::PrepareSources(recipe) => ("prepare sources", recipe),
|
||||||
|
TaskId::PrepareRecipe(recipe) => ("recipe prepare", recipe),
|
||||||
|
TaskId::ConfigureRecipe(recipe) => ("configure", recipe),
|
||||||
|
TaskId::BuildRecipe(recipe) => ("build", recipe),
|
||||||
|
TaskId::InstallPackageFiles(output) => ("install files", output),
|
||||||
|
TaskId::ProduceApk(output) => ("produce apk", output),
|
||||||
|
TaskId::InstallHostRecipe(recipe) => ("install host", recipe),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plural<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str {
|
||||||
|
if count == 1 { singular } else { plural }
|
||||||
|
}
|
||||||
|
|
||||||
fn prefix_for(kind: RecipeKind) -> &'static str {
|
fn prefix_for(kind: RecipeKind) -> &'static str {
|
||||||
match kind {
|
match kind {
|
||||||
RecipeKind::Package => "/usr",
|
RecipeKind::Package => "/usr",
|
||||||
@@ -937,13 +1063,30 @@ fn default_jobs() -> i32 {
|
|||||||
.unwrap_or(1)
|
.unwrap_or(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TERMINAL_ENV_VARS: &[&str] = &[
|
||||||
|
"TERM",
|
||||||
|
"COLORTERM",
|
||||||
|
"NO_COLOR",
|
||||||
|
"CLICOLOR",
|
||||||
|
"CLICOLOR_FORCE",
|
||||||
|
"FORCE_COLOR",
|
||||||
|
"TERM_PROGRAM",
|
||||||
|
"TERM_PROGRAM_VERSION",
|
||||||
|
];
|
||||||
|
|
||||||
fn bare_env() -> Vec<(String, String)> {
|
fn bare_env() -> Vec<(String, String)> {
|
||||||
vec![
|
let mut env = vec![
|
||||||
("PATH".to_owned(), String::new()),
|
("PATH".to_owned(), String::new()),
|
||||||
("HOME".to_owned(), "/tmp".to_owned()),
|
("HOME".to_owned(), "/tmp".to_owned()),
|
||||||
("TERM".to_owned(), "dumb".to_owned()),
|
|
||||||
("LC_ALL".to_owned(), "C".to_owned()),
|
("LC_ALL".to_owned(), "C".to_owned()),
|
||||||
]
|
];
|
||||||
|
env.extend(TERMINAL_ENV_VARS.iter().filter_map(|key| {
|
||||||
|
std::env::var(key)
|
||||||
|
.ok()
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.map(|value| ((*key).to_owned(), value))
|
||||||
|
}));
|
||||||
|
env
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_env(path: &str) -> Vec<(String, String)> {
|
fn base_env(path: &str) -> Vec<(String, String)> {
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{self, Read},
|
||||||
|
os::{fd::FromRawFd, unix::io::RawFd},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
|
ptr,
|
||||||
|
sync::{
|
||||||
|
Mutex,
|
||||||
|
atomic::{AtomicBool, AtomicI32, Ordering},
|
||||||
|
},
|
||||||
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, bail};
|
use anyhow::{Context, bail};
|
||||||
@@ -21,6 +30,41 @@ pub struct Container {
|
|||||||
stopped: bool,
|
stopped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct RegisteredContainer {
|
||||||
|
runtime: &'static str,
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ACTIVE_CONTAINER: Mutex<Option<RegisteredContainer>> = Mutex::new(None);
|
||||||
|
static SIGNAL_CLEANUP_INSTALLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
static SIGNAL_WRITE_FD: AtomicI32 = AtomicI32::new(-1);
|
||||||
|
|
||||||
|
pub fn install_signals() -> anyhow::Result<()> {
|
||||||
|
if SIGNAL_CLEANUP_INSTALLED
|
||||||
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fds = [0; 2];
|
||||||
|
if unsafe { libc::pipe(fds.as_mut_ptr()) } == -1 {
|
||||||
|
SIGNAL_CLEANUP_INSTALLED.store(false, Ordering::SeqCst);
|
||||||
|
return Err(io::Error::last_os_error()).context("creating signal cleanup pipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_close_on_exec(fds[0]);
|
||||||
|
set_close_on_exec(fds[1]);
|
||||||
|
SIGNAL_WRITE_FD.store(fds[1], Ordering::SeqCst);
|
||||||
|
|
||||||
|
thread::spawn(move || signal_cleanup_loop(fds[0]));
|
||||||
|
install_signal_handler(libc::SIGINT)?;
|
||||||
|
install_signal_handler(libc::SIGTERM)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
runtime: &ContainerRuntime,
|
runtime: &ContainerRuntime,
|
||||||
@@ -77,6 +121,8 @@ impl Container {
|
|||||||
bail!("`{runtime_str} run` returned an empty container id");
|
bail!("`{runtime_str} run` returned an empty container id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_container(runtime_str, &id);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
runtime: runtime_str,
|
runtime: runtime_str,
|
||||||
id,
|
id,
|
||||||
@@ -135,6 +181,112 @@ impl Drop for Container {
|
|||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status();
|
.status();
|
||||||
|
self.stopped = true;
|
||||||
|
}
|
||||||
|
unregister_container(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_container(runtime: &'static str, id: &str) {
|
||||||
|
if let Ok(mut active) = ACTIVE_CONTAINER.lock() {
|
||||||
|
*active = Some(RegisteredContainer {
|
||||||
|
runtime,
|
||||||
|
id: id.to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unregister_container(id: &str) {
|
||||||
|
if let Ok(mut active) = ACTIVE_CONTAINER.lock()
|
||||||
|
&& active.as_ref().is_some_and(|container| container.id == id)
|
||||||
|
{
|
||||||
|
*active = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_container() -> Option<RegisteredContainer> {
|
||||||
|
ACTIVE_CONTAINER
|
||||||
|
.lock()
|
||||||
|
.ok()
|
||||||
|
.and_then(|active| active.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal_cleanup_loop(read_fd: RawFd) {
|
||||||
|
let mut signals = unsafe { File::from_raw_fd(read_fd) };
|
||||||
|
let mut buffer = [0_u8; 16];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match signals.read(&mut buffer) {
|
||||||
|
Ok(0) => return,
|
||||||
|
Ok(len) => {
|
||||||
|
let signal = buffer[..len]
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find(|signal| *signal != 0)
|
||||||
|
.unwrap_or(libc::SIGINT as u8);
|
||||||
|
cleanup_after_signal(signal as i32);
|
||||||
|
}
|
||||||
|
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
|
||||||
|
Err(_) => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cleanup_after_signal(signal: i32) -> ! {
|
||||||
|
if let Some(container) = active_container() {
|
||||||
|
eprintln!(
|
||||||
|
"\nreceived signal {signal}; killing container {}",
|
||||||
|
container.id
|
||||||
|
);
|
||||||
|
kill_container_detached(container.runtime, &container.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::process::exit(128 + signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_container_detached(runtime: &str, id: &str) {
|
||||||
|
let _ = Command::new(runtime)
|
||||||
|
.arg("kill")
|
||||||
|
.arg(id)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_close_on_exec(fd: RawFd) {
|
||||||
|
unsafe {
|
||||||
|
let flags = libc::fcntl(fd, libc::F_GETFD);
|
||||||
|
if flags != -1 {
|
||||||
|
let _ = libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_signal_handler(signal: i32) -> anyhow::Result<()> {
|
||||||
|
let mut action = unsafe { std::mem::zeroed::<libc::sigaction>() };
|
||||||
|
action.sa_sigaction = handle_signal as *const () as usize;
|
||||||
|
action.sa_flags = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc::sigemptyset(&mut action.sa_mask);
|
||||||
|
if libc::sigaction(signal, &action, ptr::null_mut()) == -1 {
|
||||||
|
return Err(io::Error::last_os_error())
|
||||||
|
.with_context(|| format!("installing handler for signal {signal}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn handle_signal(signal: libc::c_int) {
|
||||||
|
let fd = SIGNAL_WRITE_FD.load(Ordering::Relaxed);
|
||||||
|
if fd < 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = signal as u8;
|
||||||
|
unsafe {
|
||||||
|
libc::write(fd, ptr::addr_of!(signal).cast(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+9
-11
@@ -3,7 +3,7 @@ use starlark::{
|
|||||||
environment::{FrozenModule, Globals, GlobalsBuilder, Module},
|
environment::{FrozenModule, Globals, GlobalsBuilder, Module},
|
||||||
eval::Evaluator,
|
eval::Evaluator,
|
||||||
syntax::{AstModule, Dialect},
|
syntax::{AstModule, Dialect},
|
||||||
values::list::ListRef,
|
values::list::{ListRef, UnpackList},
|
||||||
};
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
@@ -46,7 +46,7 @@ fn builder_globals(builder: &mut GlobalsBuilder) {
|
|||||||
url: String,
|
url: String,
|
||||||
sha256: String,
|
sha256: String,
|
||||||
strip_components: Option<u32>,
|
strip_components: Option<u32>,
|
||||||
patches: Option<starlark::values::list::UnpackList<String>>,
|
patches: Option<UnpackList<String>>,
|
||||||
) -> anyhow::Result<Source> {
|
) -> anyhow::Result<Source> {
|
||||||
Ok(Source::Tarball(TarballSource::new(
|
Ok(Source::Tarball(TarballSource::new(
|
||||||
url,
|
url,
|
||||||
@@ -59,7 +59,7 @@ fn builder_globals(builder: &mut GlobalsBuilder) {
|
|||||||
fn git_source(
|
fn git_source(
|
||||||
url: String,
|
url: String,
|
||||||
commit: String,
|
commit: String,
|
||||||
patches: Option<starlark::values::list::UnpackList<String>>,
|
patches: Option<UnpackList<String>>,
|
||||||
) -> anyhow::Result<Source> {
|
) -> anyhow::Result<Source> {
|
||||||
Ok(Source::Git(GitSource::new(
|
Ok(Source::Git(GitSource::new(
|
||||||
url,
|
url,
|
||||||
@@ -68,16 +68,14 @@ fn builder_globals(builder: &mut GlobalsBuilder) {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subpackage(name: String, metadata: Option<&Metadata>) -> anyhow::Result<Subpackage> {
|
fn subpackage(
|
||||||
let metadata = metadata
|
name: String,
|
||||||
.cloned()
|
metadata: &Metadata,
|
||||||
.unwrap_or_else(|| Metadata::new(None, None, None, None));
|
files: UnpackList<String>,
|
||||||
Ok(Subpackage::new(name, metadata))
|
) -> anyhow::Result<Subpackage> {
|
||||||
|
Ok(Subpackage::new(name, files.items, metadata.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap a string as a `path`. Paths support the `/` operator for
|
|
||||||
/// meson-style joining (`path("/usr") / "lib" / "pkgconfig"`), with
|
|
||||||
/// the same semantics as `pathlib.PurePosixPath`.
|
|
||||||
fn path(value: String) -> anyhow::Result<StarPath> {
|
fn path(value: String) -> anyhow::Result<StarPath> {
|
||||||
Ok(StarPath::new(value))
|
Ok(StarPath::new(value))
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-2
@@ -15,6 +15,7 @@ use crate::{
|
|||||||
pub enum TaskId {
|
pub enum TaskId {
|
||||||
FetchSources(String),
|
FetchSources(String),
|
||||||
PrepareSources(String),
|
PrepareSources(String),
|
||||||
|
PrepareRecipe(String),
|
||||||
ConfigureRecipe(String),
|
ConfigureRecipe(String),
|
||||||
BuildRecipe(String),
|
BuildRecipe(String),
|
||||||
InstallPackageFiles(String),
|
InstallPackageFiles(String),
|
||||||
@@ -27,6 +28,7 @@ impl fmt::Display for TaskId {
|
|||||||
match self {
|
match self {
|
||||||
Self::FetchSources(recipe) => write!(f, "fetch sources {recipe}"),
|
Self::FetchSources(recipe) => write!(f, "fetch sources {recipe}"),
|
||||||
Self::PrepareSources(recipe) => write!(f, "prepare sources {recipe}"),
|
Self::PrepareSources(recipe) => write!(f, "prepare sources {recipe}"),
|
||||||
|
Self::PrepareRecipe(recipe) => write!(f, "prepare recipe {recipe}"),
|
||||||
Self::ConfigureRecipe(recipe) => write!(f, "configure {recipe}"),
|
Self::ConfigureRecipe(recipe) => write!(f, "configure {recipe}"),
|
||||||
Self::BuildRecipe(recipe) => write!(f, "build {recipe}"),
|
Self::BuildRecipe(recipe) => write!(f, "build {recipe}"),
|
||||||
Self::InstallPackageFiles(output) => write!(f, "install package files {output}"),
|
Self::InstallPackageFiles(output) => write!(f, "install package files {output}"),
|
||||||
@@ -146,9 +148,13 @@ impl<'a> TaskPlanner<'a> {
|
|||||||
match task {
|
match task {
|
||||||
TaskId::FetchSources(_) => Ok(Vec::new()),
|
TaskId::FetchSources(_) => Ok(Vec::new()),
|
||||||
TaskId::PrepareSources(recipe) => Ok(vec![TaskId::FetchSources(recipe.clone())]),
|
TaskId::PrepareSources(recipe) => Ok(vec![TaskId::FetchSources(recipe.clone())]),
|
||||||
|
TaskId::PrepareRecipe(recipe) => Ok(vec![TaskId::PrepareSources(recipe.clone())]),
|
||||||
TaskId::ConfigureRecipe(recipe) => {
|
TaskId::ConfigureRecipe(recipe) => {
|
||||||
let recipe = self.recipes.recipe(recipe)?;
|
let recipe = self.recipes.recipe(recipe)?;
|
||||||
let mut deps = vec![TaskId::PrepareSources(recipe.key())];
|
let mut deps = vec![
|
||||||
|
TaskId::PrepareSources(recipe.key()),
|
||||||
|
TaskId::PrepareRecipe(recipe.key()),
|
||||||
|
];
|
||||||
deps.extend(
|
deps.extend(
|
||||||
recipe
|
recipe
|
||||||
.host_deps()
|
.host_deps()
|
||||||
@@ -167,12 +173,29 @@ impl<'a> TaskPlanner<'a> {
|
|||||||
TaskId::BuildRecipe(recipe) => Ok(vec![TaskId::ConfigureRecipe(recipe.clone())]),
|
TaskId::BuildRecipe(recipe) => Ok(vec![TaskId::ConfigureRecipe(recipe.clone())]),
|
||||||
TaskId::InstallPackageFiles(output) => {
|
TaskId::InstallPackageFiles(output) => {
|
||||||
let output = self.recipes.output(output)?;
|
let output = self.recipes.output(output)?;
|
||||||
|
let recipe = self.recipes.recipe(output.recipe())?;
|
||||||
|
if output.is_base() {
|
||||||
Ok(vec![TaskId::BuildRecipe(output.recipe().to_owned())])
|
Ok(vec![TaskId::BuildRecipe(output.recipe().to_owned())])
|
||||||
|
} else {
|
||||||
|
let base = recipe.base_output().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("recipe `{}` has no base output", recipe.key())
|
||||||
|
})?;
|
||||||
|
Ok(vec![TaskId::InstallPackageFiles(base.key())])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TaskId::ProduceApk(output) => {
|
TaskId::ProduceApk(output) => {
|
||||||
let output = self.recipes.output(output)?;
|
let output = self.recipes.output(output)?;
|
||||||
let recipe = self.recipes.recipe(output.recipe())?;
|
let recipe = self.recipes.recipe(output.recipe())?;
|
||||||
let mut deps = vec![TaskId::InstallPackageFiles(output.key())];
|
let mut deps = vec![TaskId::InstallPackageFiles(output.key())];
|
||||||
|
if output.is_base() {
|
||||||
|
deps.extend(
|
||||||
|
recipe
|
||||||
|
.outputs()
|
||||||
|
.iter()
|
||||||
|
.filter(|candidate| !candidate.is_base())
|
||||||
|
.map(|subpackage| TaskId::InstallPackageFiles(subpackage.key())),
|
||||||
|
);
|
||||||
|
}
|
||||||
deps.extend(
|
deps.extend(
|
||||||
recipe
|
recipe
|
||||||
.deps()
|
.deps()
|
||||||
@@ -195,6 +218,9 @@ impl<'a> TaskPlanner<'a> {
|
|||||||
TaskId::PrepareSources(recipe) => {
|
TaskId::PrepareSources(recipe) => {
|
||||||
self.prepare_sources_active(self.recipes.recipe(recipe)?)
|
self.prepare_sources_active(self.recipes.recipe(recipe)?)
|
||||||
}
|
}
|
||||||
|
TaskId::PrepareRecipe(recipe) => {
|
||||||
|
self.prepare_recipe_active(self.recipes.recipe(recipe)?)
|
||||||
|
}
|
||||||
TaskId::ConfigureRecipe(recipe) => {
|
TaskId::ConfigureRecipe(recipe) => {
|
||||||
self.recipe_task_active(self.recipes.recipe(recipe)?, "configure")
|
self.recipe_task_active(self.recipes.recipe(recipe)?, "configure")
|
||||||
}
|
}
|
||||||
@@ -204,8 +230,19 @@ impl<'a> TaskPlanner<'a> {
|
|||||||
TaskId::InstallPackageFiles(output) => {
|
TaskId::InstallPackageFiles(output) => {
|
||||||
let output = self.recipes.output(output)?;
|
let output = self.recipes.output(output)?;
|
||||||
let recipe = self.recipes.recipe(output.recipe())?;
|
let recipe = self.recipes.recipe(output.recipe())?;
|
||||||
Ok(self.output_task_active(recipe, output, "install")?
|
if output.is_base() {
|
||||||
|
return recipe.outputs().iter().try_fold(false, |active, output| {
|
||||||
|
Ok(active
|
||||||
|
|| self.output_task_active(recipe, output, "install")?
|
||||||
|| self.produce_apk_active(recipe, output)?)
|
|| self.produce_apk_active(recipe, output)?)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let base = recipe.base_output().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("recipe `{}` has no base output", recipe.key())
|
||||||
|
})?;
|
||||||
|
Ok(self.output_task_active(recipe, output, "install")?
|
||||||
|
|| self.produce_apk_active(recipe, output)?
|
||||||
|
|| self.produce_apk_active(recipe, base)?)
|
||||||
}
|
}
|
||||||
TaskId::ProduceApk(output) => {
|
TaskId::ProduceApk(output) => {
|
||||||
let output = self.recipes.output(output)?;
|
let output = self.recipes.output(output)?;
|
||||||
@@ -250,6 +287,16 @@ impl<'a> TaskPlanner<'a> {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_recipe_active(&self, recipe: &Recipe) -> anyhow::Result<bool> {
|
||||||
|
if recipe.phases().prepare().is_none() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
if self.prepare_sources_active(recipe)? {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
self.recipe_task_active(recipe, "prepare")
|
||||||
|
}
|
||||||
|
|
||||||
fn recipe_task_active(&self, recipe: &Recipe, kind: &str) -> anyhow::Result<bool> {
|
fn recipe_task_active(&self, recipe: &Recipe, kind: &str) -> anyhow::Result<bool> {
|
||||||
if self.is_recipe_forced(recipe) {
|
if self.is_recipe_forced(recipe) {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@@ -376,6 +423,7 @@ pub fn task_recipe_slug(task: &TaskId, recipes: &RecipeSet) -> anyhow::Result<St
|
|||||||
Ok(match task {
|
Ok(match task {
|
||||||
TaskId::FetchSources(recipe)
|
TaskId::FetchSources(recipe)
|
||||||
| TaskId::PrepareSources(recipe)
|
| TaskId::PrepareSources(recipe)
|
||||||
|
| TaskId::PrepareRecipe(recipe)
|
||||||
| TaskId::ConfigureRecipe(recipe)
|
| TaskId::ConfigureRecipe(recipe)
|
||||||
| TaskId::BuildRecipe(recipe)
|
| TaskId::BuildRecipe(recipe)
|
||||||
| TaskId::InstallHostRecipe(recipe) => recipe.clone(),
|
| TaskId::InstallHostRecipe(recipe) => recipe.clone(),
|
||||||
|
|||||||
+1
-4
@@ -35,10 +35,7 @@ impl<'a> Layout<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_install_dir(&self, recipe: &Recipe) -> PathBuf {
|
pub fn host_install_dir(&self, recipe: &Recipe) -> PathBuf {
|
||||||
self.root
|
self.root.join("build/host-pkgs").join(recipe.slug())
|
||||||
.join("build/host-pkgs")
|
|
||||||
.join(recipe.slug())
|
|
||||||
.join("usr/local")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_install_root(&self, recipe: &Recipe) -> PathBuf {
|
pub fn host_install_root(&self, recipe: &Recipe) -> PathBuf {
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ mod phase;
|
|||||||
mod recipe;
|
mod recipe;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
container::install_signals()?;
|
||||||
cli::run()
|
cli::run()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-6
@@ -73,9 +73,6 @@ impl Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn join_paths(base: &str, rhs: &str) -> String {
|
fn join_paths(base: &str, rhs: &str) -> String {
|
||||||
if rhs.starts_with('/') {
|
|
||||||
return rhs.to_owned();
|
|
||||||
}
|
|
||||||
let trimmed = base.trim_end_matches('/');
|
let trimmed = base.trim_end_matches('/');
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
format!("/{rhs}")
|
format!("/{rhs}")
|
||||||
@@ -247,7 +244,6 @@ impl<'v> StarlarkValue<'v> for PhaseContext {
|
|||||||
Some(match attr {
|
Some(match attr {
|
||||||
"source_dir" => heap.alloc(self.source_dir.clone()),
|
"source_dir" => heap.alloc(self.source_dir.clone()),
|
||||||
"build_dir" => heap.alloc(Path::new(self.build_dir.clone())),
|
"build_dir" => heap.alloc(Path::new(self.build_dir.clone())),
|
||||||
"prefix" => heap.alloc(Path::new(self.prefix.clone())),
|
|
||||||
"sysroot" => heap.alloc(Path::new(self.sysroot.clone())),
|
"sysroot" => heap.alloc(Path::new(self.sysroot.clone())),
|
||||||
"files" => heap.alloc(Path::new(self.files.as_ref()?.clone())),
|
"files" => heap.alloc(Path::new(self.files.as_ref()?.clone())),
|
||||||
"jobs" => heap.alloc(self.jobs),
|
"jobs" => heap.alloc(self.jobs),
|
||||||
@@ -257,7 +253,7 @@ impl<'v> StarlarkValue<'v> for PhaseContext {
|
|||||||
|
|
||||||
fn has_attr(&self, attr: &str, _heap: &'v Heap) -> bool {
|
fn has_attr(&self, attr: &str, _heap: &'v Heap) -> bool {
|
||||||
match attr {
|
match attr {
|
||||||
"source_dir" | "build_dir" | "prefix" | "sysroot" | "jobs" | "run" => true,
|
"source_dir" | "build_dir" | "sysroot" | "jobs" | "run" => true,
|
||||||
"files" => self.files.is_some(),
|
"files" => self.files.is_some(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@@ -267,7 +263,6 @@ impl<'v> StarlarkValue<'v> for PhaseContext {
|
|||||||
let mut attrs = vec![
|
let mut attrs = vec![
|
||||||
"source_dir".to_owned(),
|
"source_dir".to_owned(),
|
||||||
"build_dir".to_owned(),
|
"build_dir".to_owned(),
|
||||||
"prefix".to_owned(),
|
|
||||||
"sysroot".to_owned(),
|
"sysroot".to_owned(),
|
||||||
"jobs".to_owned(),
|
"jobs".to_owned(),
|
||||||
"run".to_owned(),
|
"run".to_owned(),
|
||||||
|
|||||||
@@ -51,4 +51,21 @@ impl Metadata {
|
|||||||
pub fn website(&self) -> Option<&str> {
|
pub fn website(&self) -> Option<&str> {
|
||||||
self.website.as_deref()
|
self.website.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inherit(&self, from: &Self) -> Self {
|
||||||
|
Self {
|
||||||
|
maintainer: self
|
||||||
|
.maintainer
|
||||||
|
.as_ref()
|
||||||
|
.or(from.maintainer.as_ref())
|
||||||
|
.cloned(),
|
||||||
|
description: self
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.or(from.description.as_ref())
|
||||||
|
.cloned(),
|
||||||
|
license: self.license.as_ref().or(from.license.as_ref()).cloned(),
|
||||||
|
website: self.website.as_ref().or(from.website.as_ref()).cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-56
@@ -13,14 +13,12 @@ use starlark::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
eval::{self, ExtractError},
|
eval::{self, ExtractError},
|
||||||
log,
|
|
||||||
options::Options,
|
options::Options,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,24 +98,6 @@ pub struct Recipe {
|
|||||||
phases: RecipePhases,
|
phases: RecipePhases,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Recipe {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Recipe")
|
|
||||||
.field("path", &self.path)
|
|
||||||
.field("kind", &self.kind)
|
|
||||||
.field("version", &self.version)
|
|
||||||
.field("revision", &self.revision)
|
|
||||||
.field("sources", &self.sources)
|
|
||||||
.field("outputs", &self.outputs)
|
|
||||||
.field("host_deps", &self.host_deps)
|
|
||||||
.field("build_deps", &self.build_deps)
|
|
||||||
.field("deps", &self.deps)
|
|
||||||
.field("run_deps", &self.run_deps)
|
|
||||||
.field("phases", &self.phases)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Recipe {
|
impl Recipe {
|
||||||
pub fn load(
|
pub fn load(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
@@ -129,7 +109,7 @@ impl Recipe {
|
|||||||
let module = eval::eval_file(path, Some(options), lib)
|
let module = eval::eval_file(path, Some(options), lib)
|
||||||
.with_context(|| format!("evaluating recipe {}", path.display()))?;
|
.with_context(|| format!("evaluating recipe {}", path.display()))?;
|
||||||
|
|
||||||
if !evaluate_build_if(&module)? {
|
if !Self::eval_build_if(&module)? {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +156,7 @@ impl Recipe {
|
|||||||
let key_str = key
|
let key_str = key
|
||||||
.unpack_str()
|
.unpack_str()
|
||||||
.ok_or_else(|| anyhow::anyhow!("field `sources`: keys must be strings"))?;
|
.ok_or_else(|| anyhow::anyhow!("field `sources`: keys must be strings"))?;
|
||||||
if !is_valid_source_name(key_str) {
|
if !Self::is_valid_source_name(key_str) {
|
||||||
bail!(
|
bail!(
|
||||||
"field `sources`: invalid source name `{key_str}` (allowed: letters, digits, `-`, `_`; must start with a letter or digit)"
|
"field `sources`: invalid source name `{key_str}` (allowed: letters, digits, `-`, `_`; must start with a letter or digit)"
|
||||||
);
|
);
|
||||||
@@ -192,10 +172,10 @@ impl Recipe {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let host_deps = optional_string_list(&module, "host_deps")?;
|
let host_deps = Self::optional_string_list(&module, "host_deps")?;
|
||||||
let build_deps = optional_string_list(&module, "build_deps")?;
|
let build_deps = Self::optional_string_list(&module, "build_deps")?;
|
||||||
let deps = optional_string_list(&module, "deps")?;
|
let deps = Self::optional_string_list(&module, "deps")?;
|
||||||
let run_deps = optional_string_list(&module, "run_deps")?;
|
let run_deps = Self::optional_string_list(&module, "run_deps")?;
|
||||||
|
|
||||||
let recipe_key = kind.key(name);
|
let recipe_key = kind.key(name);
|
||||||
let outputs = match kind {
|
let outputs = match kind {
|
||||||
@@ -204,6 +184,8 @@ impl Recipe {
|
|||||||
recipe: recipe_key.clone(),
|
recipe: recipe_key.clone(),
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
metadata: metadata.clone(),
|
metadata: metadata.clone(),
|
||||||
|
file_globs: Vec::new(),
|
||||||
|
base: true,
|
||||||
}];
|
}];
|
||||||
if let Some(value) = module.get("subpackages") {
|
if let Some(value) = module.get("subpackages") {
|
||||||
let list = ListRef::from_value(value)
|
let list = ListRef::from_value(value)
|
||||||
@@ -214,10 +196,13 @@ impl Recipe {
|
|||||||
"field `subpackages`: each entry must be a subpackage value"
|
"field `subpackages`: each entry must be a subpackage value"
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
Self::check_subpackage_file_globs(sub.name(), sub.file_globs())?;
|
||||||
outputs.push(OutputPackage {
|
outputs.push(OutputPackage {
|
||||||
recipe: recipe_key.clone(),
|
recipe: recipe_key.clone(),
|
||||||
name: sub.name().to_owned(),
|
name: sub.name().to_owned(),
|
||||||
metadata: sub.metadata().clone(),
|
metadata: sub.metadata().inherit(&metadata),
|
||||||
|
file_globs: sub.file_globs().to_vec(),
|
||||||
|
base: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,6 +291,10 @@ impl Recipe {
|
|||||||
&self.outputs
|
&self.outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn base_output(&self) -> Option<&OutputPackage> {
|
||||||
|
self.outputs.iter().find(|output| output.is_base())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn host_deps(&self) -> &[String] {
|
pub fn host_deps(&self) -> &[String] {
|
||||||
&self.host_deps
|
&self.host_deps
|
||||||
}
|
}
|
||||||
@@ -321,21 +310,17 @@ impl Recipe {
|
|||||||
pub fn run_deps(&self) -> &[String] {
|
pub fn run_deps(&self) -> &[String] {
|
||||||
&self.run_deps
|
&self.run_deps
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn optional_string_list(module: &Module, key: &str) -> anyhow::Result<Vec<String>> {
|
fn optional_string_list(module: &Module, key: &str) -> anyhow::Result<Vec<String>> {
|
||||||
match eval::extract_string_list(module, key) {
|
match eval::extract_string_list(module, key) {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(ExtractError::NotFound) => Ok(Vec::new()),
|
Err(ExtractError::NotFound) => Ok(Vec::new()),
|
||||||
Err(e) => Err(anyhow::anyhow!("field `{key}`: {e}")),
|
Err(e) => Err(anyhow::anyhow!("field `{key}`: {e}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the recipe's optional `build_if` gate. It may be either a bool
|
/// Evaluates "build_if" functions.
|
||||||
/// (or any expression evaluating to one) or a zero-argument callable that
|
fn eval_build_if(module: &Module) -> anyhow::Result<bool> {
|
||||||
/// returns a bool. Recipes can reach configuration via the `options` global.
|
|
||||||
/// Recipes without a `build_if` are unconditionally built.
|
|
||||||
fn evaluate_build_if(module: &Module) -> anyhow::Result<bool> {
|
|
||||||
let Some(value) = module.get("build_if") else {
|
let Some(value) = module.get("build_if") else {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
};
|
};
|
||||||
@@ -356,9 +341,9 @@ fn evaluate_build_if(module: &Module) -> anyhow::Result<bool> {
|
|||||||
result
|
result
|
||||||
.unpack_bool()
|
.unpack_bool()
|
||||||
.ok_or_else(|| anyhow::anyhow!("field `build_if`: must return a bool"))
|
.ok_or_else(|| anyhow::anyhow!("field `build_if`: must return a bool"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_source_name(name: &str) -> bool {
|
fn is_valid_source_name(name: &str) -> bool {
|
||||||
let mut chars = name.chars();
|
let mut chars = name.chars();
|
||||||
let Some(first) = chars.next() else {
|
let Some(first) = chars.next() else {
|
||||||
return false;
|
return false;
|
||||||
@@ -367,33 +352,52 @@ fn is_valid_source_name(name: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
chars.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
chars.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_subpackage_file_globs(name: &str, file_globs: &[String]) -> anyhow::Result<()> {
|
||||||
|
if file_globs.is_empty() {
|
||||||
|
bail!("subpackage '{name}' must declare at least one file glob");
|
||||||
|
}
|
||||||
|
|
||||||
|
for glob in file_globs {
|
||||||
|
if glob.is_empty() {
|
||||||
|
bail!("subpackage '{name}' has an empty file glob");
|
||||||
|
}
|
||||||
|
if glob.starts_with('/') {
|
||||||
|
bail!("subpackage '{name}' glob '{glob}' must be relative to the package dest dir");
|
||||||
|
}
|
||||||
|
if Path::new(glob)
|
||||||
|
.components()
|
||||||
|
.any(|component| matches!(component, std::path::Component::ParentDir))
|
||||||
|
{
|
||||||
|
bail!("subpackage '{name}' glob '{glob}' must not contain '..'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct RecipePhases {
|
pub struct RecipePhases {
|
||||||
|
prepare: Option<OwnedFrozenValue>,
|
||||||
configure: Option<OwnedFrozenValue>,
|
configure: Option<OwnedFrozenValue>,
|
||||||
build: OwnedFrozenValue,
|
build: OwnedFrozenValue,
|
||||||
install: OwnedFrozenValue,
|
install: OwnedFrozenValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for RecipePhases {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("RecipePhases")
|
|
||||||
.field("configure", &self.configure.is_some())
|
|
||||||
.field("build", &true)
|
|
||||||
.field("install", &true)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecipePhases {
|
impl RecipePhases {
|
||||||
fn load(module: &FrozenModule) -> anyhow::Result<Self> {
|
fn load(module: &FrozenModule) -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
prepare: optional_phase_function(module, "prepare")?,
|
||||||
configure: optional_phase_function(module, "configure")?,
|
configure: optional_phase_function(module, "configure")?,
|
||||||
build: required_phase_function(module, "build")?,
|
build: required_phase_function(module, "build")?,
|
||||||
install: required_phase_function(module, "install")?,
|
install: required_phase_function(module, "install")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prepare(&self) -> Option<&OwnedFrozenValue> {
|
||||||
|
self.prepare.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure(&self) -> Option<&OwnedFrozenValue> {
|
pub fn configure(&self) -> Option<&OwnedFrozenValue> {
|
||||||
self.configure.as_ref()
|
self.configure.as_ref()
|
||||||
}
|
}
|
||||||
@@ -440,7 +444,7 @@ fn validate_phase_function(name: &str, value: &OwnedFrozenValue) -> anyhow::Resu
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct OutputPackage {
|
pub struct OutputPackage {
|
||||||
/// Canonical key of the owning recipe.
|
/// Canonical key of the owning recipe.
|
||||||
recipe: String,
|
recipe: String,
|
||||||
@@ -448,6 +452,10 @@ pub struct OutputPackage {
|
|||||||
name: String,
|
name: String,
|
||||||
/// Metadata attached to the output package.
|
/// Metadata attached to the output package.
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
|
/// File globs claimed from the base package dest dir.
|
||||||
|
file_globs: Vec<String>,
|
||||||
|
/// Whether this is the recipe's base output.
|
||||||
|
base: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputPackage {
|
impl OutputPackage {
|
||||||
@@ -466,9 +474,16 @@ impl OutputPackage {
|
|||||||
pub fn metadata(&self) -> &Metadata {
|
pub fn metadata(&self) -> &Metadata {
|
||||||
&self.metadata
|
&self.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_globs(&self) -> &[String] {
|
||||||
|
&self.file_globs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_base(&self) -> bool {
|
||||||
|
self.base
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RecipeSet {
|
pub struct RecipeSet {
|
||||||
recipes: HashMap<String, Recipe>,
|
recipes: HashMap<String, Recipe>,
|
||||||
outputs: HashMap<String, OutputPackage>,
|
outputs: HashMap<String, OutputPackage>,
|
||||||
@@ -500,7 +515,6 @@ impl RecipeSet {
|
|||||||
let loaded = Recipe::load(&path, &name, kind, options, lib)
|
let loaded = Recipe::load(&path, &name, kind, options, lib)
|
||||||
.with_context(|| format!("loading recipe `{name}`"))?;
|
.with_context(|| format!("loading recipe `{name}`"))?;
|
||||||
let Some(recipe) = loaded else {
|
let Some(recipe) = loaded else {
|
||||||
log::skip("load", &format!("{key} (build_if returned false)"));
|
|
||||||
skipped.insert(key);
|
skipped.insert(key);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -545,11 +559,6 @@ impl RecipeSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find all recipe `.star` files under `dir`, returning a map of recipe name
|
|
||||||
/// to its `.star` file. Intermediate directories act as categories and are
|
|
||||||
/// not themselves recipes. Within any subtree, a recipe takes either form:
|
|
||||||
/// - `.../<name>.star` — name is the file stem
|
|
||||||
/// - `.../<name>/recipe.star` — name is the parent directory
|
|
||||||
fn discover_recipes(dir: &Path) -> anyhow::Result<HashMap<String, PathBuf>> {
|
fn discover_recipes(dir: &Path) -> anyhow::Result<HashMap<String, PathBuf>> {
|
||||||
let mut recipes: HashMap<String, PathBuf> = HashMap::new();
|
let mut recipes: HashMap<String, PathBuf> = HashMap::new();
|
||||||
|
|
||||||
|
|||||||
@@ -7,18 +7,27 @@ use crate::recipe::Metadata;
|
|||||||
#[derive(Debug, Clone, Allocative, ProvidesStaticType, NoSerialize)]
|
#[derive(Debug, Clone, Allocative, ProvidesStaticType, NoSerialize)]
|
||||||
pub struct Subpackage {
|
pub struct Subpackage {
|
||||||
name: String,
|
name: String,
|
||||||
|
file_globs: Vec<String>,
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subpackage {
|
impl Subpackage {
|
||||||
pub fn new(name: String, metadata: Metadata) -> Self {
|
pub fn new(name: String, file_globs: Vec<String>, metadata: Metadata) -> Self {
|
||||||
Self { name, metadata }
|
Self {
|
||||||
|
name,
|
||||||
|
file_globs,
|
||||||
|
metadata,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_globs(&self) -> &[String] {
|
||||||
|
&self.file_globs
|
||||||
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> &Metadata {
|
pub fn metadata(&self) -> &Metadata {
|
||||||
&self.metadata
|
&self.metadata
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user