From 21885af02e4c7b66f296a1432a9ffb0184c05d36 Mon Sep 17 00:00:00 2001 From: sisungo Date: Mon, 6 Jul 2026 20:17:30 +0800 Subject: [PATCH] feat: add support of atom builds Signed-off-by: sisungo --- lib/atombuild.py | 129 +++++++++++++++++++++++++++++++++ lib/make.py | 22 +++++- lib/packie.py | 24 +++++- packages/bzip2/pkgbuild.py | 8 +- packages/mksh/pkgbuild.py | 11 ++- packages/ncurses/pkgbuild.py | 3 + packages/semi-libc/pkgbuild.py | 4 + packages/sqlite/pkgbuild.py | 6 ++ packages/xz/pkgbuild.py | 6 ++ x.py | 48 +++++++++++- 10 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 lib/atombuild.py diff --git a/lib/atombuild.py b/lib/atombuild.py new file mode 100644 index 0000000..1ade61e --- /dev/null +++ b/lib/atombuild.py @@ -0,0 +1,129 @@ +import os +import shutil +import subprocess + +from . import packie + +BASE_REQUIREMENTS = [ + "mksh", + "coreutils", + "python", + "packie", + "sqlite", + "libgcc-compat", + "aws-lc-libssl", + "aws-lc-tools", + "libcxx", + "semi-libc", + "ca-certificates", + "certutil", + "libz-ng", + "zstd", +] + + +def _prepare_build_env_base_files(root: str): + os.makedirs(root, exist_ok=True) + os.mkdir(f"{root}/var") + os.mkdir(f"{root}/var/tmp") + os.mkdir(f"{root}/var/run") + os.mkdir(f"{root}/tmp") + os.mkdir(f"{root}/run") + os.makedirs(f"{root}/var/db/packie") + shutil.copytree("/var/config/packie/repos.d", f"{root}/var/config/packie/repos.d") + os.mkdir(f"{root}/dev") + os.mkdir(f"{root}/sys") + os.mkdir(f"{root}/proc") + os.makedirs(f"{root}/home/root") + + +def prepare_build_env(root: str, extreq: list[str] = []): + # Skip if the environment is already initialized + if os.path.exists(f"{root}/.INIT"): + return + try: + shutil.rmtree(root) + except: + pass + + # Prepare basic filesystem hierarchy + _prepare_build_env_base_files(root) + + # Install packages + packie.chroot_update(root) + packie.chroot_install(root, BASE_REQUIREMENTS + extreq) + + # Refresh certificates + enter_build_env(root, ["certutil", "refresh"]) + + # Copy DNS config + os.makedirs(f"{root}/etc", exist_ok=True) + shutil.copy2("/etc/resolv.conf", f"{root}/etc/resolv.conf") + + # Create initialized tag + with open(f"{root}/.INIT", "wt+") as file: + file.write("OK") + + +def enter_build_env(root: str, cmd: list[str], chdir: str = "/"): + reserve_env = ["https_proxy", "http_proxy", "all_proxy"] + reserve_env_args = [] + for i in reserve_env: + if os.environ.get(i): + reserve_env_args += ["--setenv", i, os.environ[i]] + subprocess.run( + [ + "bwrap", + "--bind", + root, + "/", + "--unshare-pid", + "--unshare-user", + "--uid", + "0", + "--gid", + "0", + "--dev", + "/dev", + "--bind", + "/sys", + "/sys", + "--proc", + "/proc", + "--clearenv", + "--setenv", + "HOME", + "/home/root", + "--setenv", + "PATH", + "/bin", + "--setenv", + "SEMIOS_PKGBUILD_IN_ATOMIC", + "1", + "--chdir", + chdir, + ] + + reserve_env_args + + cmd + ) + + +def copy_pkgbuild_tree(repo_root: str, pkgname: str, build_env_root: str): + build_dir = f"{build_env_root}/Atom" + if os.path.exists(build_dir): + return + os.mkdir(build_dir) + shutil.copytree(f"{repo_root}/hooks", f"{build_dir}/hooks") + shutil.copytree(f"{repo_root}/lib", f"{build_dir}/lib") + shutil.copy2(f"{repo_root}/x", f"{build_dir}/x") + shutil.copy2(f"{repo_root}/x.py", f"{build_dir}/x.py") + os.makedirs(f"{build_dir}/packages/{pkgname}") + for i in os.listdir(f"{repo_root}/packages/{pkgname}"): + srcpath = f"{repo_root}/packages/{pkgname}/{i}" + dstpath = f"{build_dir}/packages/{pkgname}/{i}" + if i in ["target", "__pycache__"]: + continue + if os.path.isdir(srcpath): + shutil.copytree(srcpath, dstpath) + elif os.path.isfile(srcpath): + shutil.copy2(srcpath, dstpath) diff --git a/lib/make.py b/lib/make.py index a045423..0088e8b 100644 --- a/lib/make.py +++ b/lib/make.py @@ -24,9 +24,25 @@ _pkginfo: dict[str, dict[str, typing.Any]] = { _supported_arch: typing.Callable[[str], bool] = lambda x: True _on_build: typing.Callable[[], None] | None = None +_in_atombuild = False +_builddep: list[str] = [] + _blocked_hooks: list[str] = [] +def builddep(s: str): + global _builddep + if s == "shortcut/c": + _builddep += ["semi-libc-dev", "clang", "llvm", "lld"] + elif s == "shortcut/c++": + builddep("shortcut/c") + _builddep += ["libcxx-dev"] + elif s == "shortcut/autotools": + _builddep += ["gnu-make", "gnu-sed", "awk", "grep"] + else: + _builddep += [s] + + def package(s: str): global _current_pkgname _current_pkgname = s @@ -103,8 +119,10 @@ def block_hook(hook: str): def source_url(url: str): - sources.source_url(url) + if not _in_atombuild: + sources.source_url(url) def source_git(url: str, branch: str | None = None): - sources.source_git(url, branch) + if not _in_atombuild: + sources.source_git(url, branch) diff --git a/lib/packie.py b/lib/packie.py index 5ed847f..35c012a 100644 --- a/lib/packie.py +++ b/lib/packie.py @@ -1,11 +1,31 @@ -import subprocess import os +import subprocess PACKIE_EXEC = os.getenv("PACKIE", "packie") + def host_arch() -> str: result = subprocess.run( [PACKIE_EXEC, "print", "profile.host_arch"], - check=True, capture_output=True, + check=True, + capture_output=True, ) return result.stdout.decode().strip() + + +def chroot_update(root: str): + subprocess.run([PACKIE_EXEC, "--chroot", root, "update"], check=True) + + +def chroot_install(root: str, requirements: list[str]): + subprocess.run( + [PACKIE_EXEC, "--chroot", root, "install", "--yes"] + requirements, check=True + ) + + +def update(): + subprocess.run([PACKIE_EXEC, "update"], check=True) + + +def install(requirements: list[str]): + subprocess.run([PACKIE_EXEC, "install", "--yes"] + requirements, check=True) diff --git a/packages/bzip2/pkgbuild.py b/packages/bzip2/pkgbuild.py index cc18c8c..1f61628 100644 --- a/packages/bzip2/pkgbuild.py +++ b/packages/bzip2/pkgbuild.py @@ -6,13 +6,17 @@ from lib.make import * upstream_version = "1.0.8" +builddep("shortcut/c") +builddep("shortcut/autotools") + version(f"{upstream_version}") source_url(f"https://sourceware.org/pub/bzip2/bzip2-{upstream_version}.tar.gz") def build(): - cpp.make(["-f", "Makefile-libbz2_so"]) - cpp.make_install(["PREFIX=../cpp_install"], chdir=False) + makevars = [f"CC={os.environ.get('CC', 'cc')}"] + cpp.make(["-f", "Makefile-libbz2_so"] + makevars) + cpp.make_install(["PREFIX=../cpp_install"] + makevars, chdir=False) shutil.copy2(f"libbz2.so.{upstream_version}", "../cpp_install/lib/libbz2.so") os.chdir("../cpp_install") diff --git a/packages/mksh/pkgbuild.py b/packages/mksh/pkgbuild.py index b6e6e68..8a54259 100644 --- a/packages/mksh/pkgbuild.py +++ b/packages/mksh/pkgbuild.py @@ -1,13 +1,18 @@ -import os +import subprocess from lib.make import * version("1.59.2") -source_git("https://github.com/mirabilos/mksh-cvs2git.git", "mksh-R59c") +source_url( + "https://github.com/mirabilos/mksh-cvs2git/archive/refs/tags/mksh-R59c.tar.gz" +) + +builddep("shortcut/c") +builddep("sed") def build(): - os.system("sh ./Build.sh") + subprocess.run(["sh", "./Build.sh"], check=True) on_build(build) diff --git a/packages/ncurses/pkgbuild.py b/packages/ncurses/pkgbuild.py index 7ac48ea..7a6fa8b 100644 --- a/packages/ncurses/pkgbuild.py +++ b/packages/ncurses/pkgbuild.py @@ -6,6 +6,9 @@ from lib.make import * version("6.6") source_url("https://invisible-island.net/archives/ncurses/ncurses-6.6.tar.gz") +builddep("shortcut/c") +builddep("shortcut/autotools") + def build(): cpp.configure( diff --git a/packages/semi-libc/pkgbuild.py b/packages/semi-libc/pkgbuild.py index 1a0da63..aebdc6d 100644 --- a/packages/semi-libc/pkgbuild.py +++ b/packages/semi-libc/pkgbuild.py @@ -5,6 +5,10 @@ version("1.2.6+1") source_url("https://gitea.semilabs.org/semios/semi-libc/archive/v1.2.6-1.tar.gz") +builddep("shortcut/c") +builddep("shortcut/autotools") + + def build(): cpp.configure(["--prefix=/"]) cpp.make() diff --git a/packages/sqlite/pkgbuild.py b/packages/sqlite/pkgbuild.py index 8d8d8f3..621be52 100644 --- a/packages/sqlite/pkgbuild.py +++ b/packages/sqlite/pkgbuild.py @@ -1,9 +1,15 @@ +import os + import lib.cpp as cpp from lib.make import * version("3.53.2") source_url("https://sqlite.org/2026/sqlite-autoconf-3530200.tar.gz") +builddep("shortcut/autotools") +builddep("shortcut/c") +builddep("linenoise-dev") + def build(): cpp.configure( diff --git a/packages/xz/pkgbuild.py b/packages/xz/pkgbuild.py index c368342..a98fae2 100644 --- a/packages/xz/pkgbuild.py +++ b/packages/xz/pkgbuild.py @@ -1,3 +1,5 @@ +import os + from lib import cpp from lib.make import * @@ -6,8 +8,12 @@ source_url( "https://github.com/tukaani-project/xz/releases/download/v5.8.3/xz-5.8.3.tar.gz" ) +builddep("shortcut/c") +builddep("shortcut/autotools") + def build(): + os.environ["FGREP"] = "grep -F" cpp.configure( [ "--prefix=/", diff --git a/x.py b/x.py index 4460a4f..f1ebe41 100644 --- a/x.py +++ b/x.py @@ -7,7 +7,9 @@ import typing import hooks import lib.arch +import lib.atombuild import lib.make +import lib.packie import lib.pkgcomposer import lib.sources @@ -20,6 +22,7 @@ argparser = argparse.ArgumentParser( argparser.add_argument( "--target", type=str, help="Target architecture to build the package for" ) +argparser.add_argument("--atombuild", action="store_true", help="Enable Atom Build") subparsers = argparser.add_subparsers(dest="subcommand") parser_build = subparsers.add_parser("build") parser_build.add_argument("BUILD_PKGNAME") @@ -64,6 +67,9 @@ def run_build_package(package: str): print(f"Architecture {lib.arch.get_target()} is not supported for this package") exit(1) + if os.environ.get("SEMIOS_PKGBUILD_IN_ATOMIC", "0") == "1": + lib.packie.install(lib.make._builddep) + # Run `pre_build` hooks run_hooks(hooks.pre_build) @@ -151,8 +157,48 @@ def run_list(item): print(i) +def run_build_atom(pkgname: str): + # Declare necessary paths + repo_root = os.getcwd() + package_dir = f"{repo_root}/packages/{pkgname}" + target_dir = f"{package_dir}/target" + build_env_root = f"{target_dir}/atombuild_root" + + # Read `pkgbuild.py` of that package + lib.make._in_atombuild = True + try: + os.chdir(package_dir) + os.makedirs(target_dir, exist_ok=True) + except Exception: + print(f"No such package {pkgname}") + exit(1) + importlib.import_module(f"packages.{pkgname}.pkgbuild") + os.chdir(repo_root) + + # Initialize build environment + lib.atombuild.prepare_build_env(build_env_root, lib.make._builddep) + lib.atombuild.copy_pkgbuild_tree(repo_root, pkgname, build_env_root) + lib.atombuild.enter_build_env( + build_env_root, + ["./x", "--target", lib.arch.get_target(), "build", pkgname], + chdir="/Atom", + ) + + # Copy artifacts + atom_target_dir = f"{build_env_root}/Atom/packages/{pkgname}/target" + try: + for i in os.listdir(atom_target_dir): + if i.endswith(".pkg"): + shutil.copy2(f"{atom_target_dir}/{i}", f"{target_dir}/{i}") + except Exception: + pass + + if args.subcommand == "build": - run_build_package(args.BUILD_PKGNAME) + if args.atombuild: + run_build_atom(args.BUILD_PKGNAME) + else: + run_build_package(args.BUILD_PKGNAME) elif args.subcommand == "clean": run_clean_package(args.CLEAN_PKGNAME) elif args.subcommand == "list":