Initial commit

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
sisungo
2026-06-08 16:43:26 +08:00
commit 3d480b48f3
13 changed files with 446 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
/packages/*/target
__pycache__
/.vscode
/.idea
/.zed
+9
View File
@@ -0,0 +1,9 @@
from typing import Callable, TypeAlias
from . import autostrip
PostBuild: TypeAlias = Callable[[], None]
post_build: dict[str, PostBuild] = {
"autostrip": autostrip.autostrip,
}
+2
View File
@@ -0,0 +1,2 @@
def autostrip():
pass
View File
+19
View File
@@ -0,0 +1,19 @@
import os
from . import packie
all: dict[str, dict[str, str]] = {
"aarch64-semios-linux": {},
}
_target: str = packie.host_arch()
def get_target():
return _target
def set_target(arch: str):
global _target
_target = arch
for key, val in all[_target]:
os.environ[key] = val
+62
View File
@@ -0,0 +1,62 @@
import os
import subprocess
from . import make as libmake
from . import pkgcomposer
def configure(rem: list[str] = []):
subprocess.run(["./configure"] + rem, check=True)
def make(rem: list[str] = []):
subprocess.run(["make", f"-j{os.cpu_count()}"] + rem, check=True)
def make_install(rem: list[str] | None = None, chdir: bool = True):
default_rem = ["DESTDIR=" + os.getcwd() + "/../cpp_install"]
subprocess.run(["make", "install"] + (rem or default_rem), check=True)
if rem is None and chdir:
os.chdir("../cpp_install")
def _do_auto_pack(pc: pkgcomposer.PkgComposer, srcdir: str, dstdir: str, filter):
pc.makedir(srcdir)
with os.scandir(srcdir) as entries:
for ent in entries:
if filter(ent):
if ent.is_dir(follow_symlinks=False):
pc.makedir(f"{dstdir}/{ent.name}")
else:
pc.addfile(ent.path, f"{dstdir}/{ent.name}")
def _autopack_filter_lib(ent) -> bool:
if ent.name.endswith(".o") or ent.name.endswith(".a"):
return False
return True
def _autopack_filter_bin(ent) -> bool:
return True
def _autopack_filter_dev(ent):
if ".so" in ent.name:
return False
return True
def _auto_pack(profiles: list[str], pc: pkgcomposer.PkgComposer):
if "lib" in profiles:
_do_auto_pack(pc, "lib", "lib", _autopack_filter_lib)
if "bin" in profiles:
_do_auto_pack(pc, "bin", "bin", _autopack_filter_bin)
_do_auto_pack(pc, "sbin", "bin", _autopack_filter_bin)
if "dev" in profiles:
_do_auto_pack(pc, "lib", "lib", _autopack_filter_dev)
_do_auto_pack(pc, "include", "include", _autopack_filter_dev)
def use_auto_pack(profiles: list[str]):
libmake.on_pack(lambda pc: _auto_pack(profiles, pc))
+61
View File
@@ -0,0 +1,61 @@
import typing
from . import pkgcomposer, sources
_key_global = "@global"
_key_version = "version"
_key_description = "description"
_key_on_pack = "on_pack"
_key_arch_any = "arch_any"
_current_pkgname: str = _key_global
_pkginfo: dict[str, dict[str, typing.Any]] = {
_key_global: {},
}
_supported_arch: typing.Callable[[str], bool] = lambda x: True
_on_build: typing.Callable[[], None] | None = None
_blocked_hooks: list[str] = []
def package(s: str):
global _current_pkgname
_current_pkgname = s
_pkginfo[s] = _pkginfo[_key_global].copy()
def version(s: str):
_pkginfo[_current_pkgname][_key_version] = s
def description(s: str):
_pkginfo[_current_pkgname][_key_description] = s
def arch_any():
_pkginfo[_current_pkgname][_key_arch_any] = True
def supported_arch(s: list[str] | typing.Callable[[str], bool]):
global _supported_arch
if isinstance(s, list):
_supported_arch = lambda arch: arch in s
else:
_supported_arch = s
def on_build(func: typing.Callable[[], None]):
global _on_build
_on_build = func
def on_pack(func: typing.Callable[[pkgcomposer.PkgComposer], None]):
_pkginfo[_current_pkgname][_key_on_pack] = func
def block_hook(hook: str):
_blocked_hooks.append(hook)
def source_url(url: str):
sources.source_url(url)
+11
View File
@@ -0,0 +1,11 @@
import subprocess
import os
PACKIE_EXEC = os.getenv("PACKIE", "packie")
def host_arch() -> str:
result = subprocess.run(
[PACKIE_EXEC, "print", "profile.host_arch"],
check=True, capture_output=True,
)
return result.stdout.decode().strip()
+39
View File
@@ -0,0 +1,39 @@
import json
import os
import shutil
import sys
if sys.version_info >= (3, 14):
_archive_format = "zstdtar"
_archive_suffix = ".tar.zst"
else:
_archive_format = "gztar"
_archive_suffix = ".tar.gz"
class PkgComposer:
def __init__(self, pkgfile: str):
self.pkgfile = pkgfile
def setworkdir(self, workdir: str):
self.workdir = workdir
shutil.rmtree(workdir, ignore_errors=True)
os.mkdir(workdir)
os.mkdir(f"{workdir}/bundle")
def write_manifest(self, manifest: dict):
with open(self.workdir + "/PkgManifest.json", "w") as f:
json.dump(manifest, f, indent=4)
def compose(self):
shutil.make_archive(self.pkgfile, _archive_format, self.workdir)
shutil.move(self.pkgfile + _archive_suffix, self.pkgfile)
def makedir(self, name: str):
os.mkdir(f"{self.workdir}/bundle/{name}")
def addfile(self, src: str, dst: str):
shutil.copy2(src, f"{self.workdir}/bundle/{dst}", follow_symlinks=False)
def add_dir(self, src: str, dst: str, merge: bool = False):
shutil.copytree(src, f"{self.workdir}/bundle/{dst}", dirs_exist_ok=merge)
+111
View File
@@ -0,0 +1,111 @@
import os
import sys
import tarfile
import urllib.parse
import urllib.request
def filename(url):
parsed = urllib.parse.urlparse(url)
path = parsed.path
filename = os.path.basename(path)
if not filename:
raise ValueError("No filename found in URL")
return filename
def download(url, output):
last_percent = -1
def report_progress(percent):
nonlocal last_percent
if percent != last_percent:
if last_percent >= 0:
sys.stdout.write("\b" * len(str(last_percent)))
sys.stdout.write(str(percent))
sys.stdout.flush()
last_percent = percent
try:
req = urllib.request.Request(url, method="GET")
req.add_header(
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
)
with urllib.request.urlopen(req) as response:
total_size = response.length
if total_size is None or total_size <= 0:
print("Downloading...")
with open(output, "wb") as f:
while True:
chunk = response.read(8192)
if not chunk:
break
f.write(chunk)
print("Download completed: " + output)
return output
sys.stdout.write("downloading... 0%")
sys.stdout.flush()
with open(output, "wb") as f:
downloaded = 0
block_size = 8192
while True:
chunk = response.read(block_size)
if not chunk:
break
f.write(chunk)
downloaded += len(chunk)
percent = int(downloaded * 100 / total_size)
report_progress(percent)
if last_percent != 100:
report_progress(100)
sys.stdout.write("\n")
print("Download completed: " + output)
return output
except Exception as e:
if os.path.exists(output):
os.remove(output)
raise Exception("Download failed: " + str(e))
def tar_common_root(tar: tarfile.TarFile) -> str | None:
root = None
for member in tar.getmembers():
this_root = member.path.split("/")[0]
if root is None:
root = this_root
if this_root != root:
return None
return root
def sources_dir_name() -> str:
with open("./target/sources.tag") as file:
return file.read()
def source_url(url: str):
if os.path.exists("./target/sources.tag"):
return
download_path = f"./target/{filename(url)}"
download(url, download_path)
with tarfile.open(download_path, "r") as tar:
common_root = tar_common_root(tar)
extract_dir = "./target"
sources_dir = f"./target/{common_root}"
if common_root in ["", ".", "None"]:
extract_dir = "./target/sources"
sources_dir = "./target/sources"
tar.extractall(extract_dir)
with open("./target/sources.tag", "wt+") as file:
file.write(sources_dir)
+21
View File
@@ -0,0 +1,21 @@
from lib.cpp import *
from lib.make import *
source_url("https://gitea.semilabs.org/semios/semi-libc/archive/v1.2.5.tar.gz")
version("1.2.5")
def build():
configure(["--prefix=/"])
make()
make_install()
on_build(build)
package("semi-libc")
use_auto_pack(["lib"])
package("semi-libc-dev")
use_auto_pack(["dev"])
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
cd $(dirname $0)
python3 x.py $@
+102
View File
@@ -0,0 +1,102 @@
import argparse
import importlib
import os
import shutil
import sys
import hooks
import lib.arch
import lib.make
import lib.pkgcomposer
import lib.sources
os.chdir(os.path.dirname(__file__))
sys.path.append(os.path.abspath("lib"))
argparser = argparse.ArgumentParser(
description="SemiOS source package repository helper"
)
argparser.add_argument(
"--target", type=str, help="Target architecture to build the package for"
)
argparser.add_argument("--build", type=str, help="Package to build")
argparser.add_argument("--clean", type=str, help="Package to clean")
args = argparser.parse_args()
if args.target:
lib.arch.set_target(args.target)
else:
lib.arch.set_target(lib.arch.get_target())
def run_build_package(package: str):
try:
os.chdir(f"packages/{package}")
os.makedirs("target", exist_ok=True)
except Exception:
print(f"No such package {package}")
exit(1)
package_dir = os.getcwd()
importlib.import_module(f"packages.{package}.pkgbuild")
# Check architecture support
if not lib.make._supported_arch(lib.arch.get_target()):
print(f"Architecture {lib.arch.get_target()} is not supported for this package")
exit(1)
# Build the package
if lib.make._on_build:
os.chdir(lib.sources.sources_dir_name())
lib.make._on_build()
else:
print("on_build(): Not defined")
# Run `post_build` hooks
for n in hooks.post_build.keys():
if n in lib.make._blocked_hooks:
continue
hooks.post_build[n]()
# Pack packages
for pkgname in lib.make._pkginfo.keys():
if pkgname == lib.make._key_global:
continue
pkginfo = lib.make._pkginfo[pkgname]
pkgver = pkginfo[lib.make._key_version]
if pkginfo.get(lib.make._key_arch_any):
pkgarch = "any"
else:
pkgarch = lib.arch.get_target()
pkgfilename = f"{pkgname}={pkgver}@{pkgarch}"
composer = lib.pkgcomposer.PkgComposer(
f"{package_dir}/target/{pkgfilename}.pkg"
)
composer.setworkdir(f"{package_dir}/target/{pkgfilename}")
pkginfo[lib.make._key_on_pack](composer)
manifest = {
"name": pkgname,
"version": pkgver,
"arch": pkgarch,
}
composer.write_manifest(manifest)
composer.compose()
def run_clean_package(package: str):
try:
shutil.rmtree(f"packages/{package}/target")
except Exception:
pass
if args.build:
run_build_package(args.build)
if args.clean:
run_clean_package(args.clean)