Feat: big movement for ASIO

This commit is contained in:
2026-03-20 13:03:32 +01:00
parent 5c5488a9f0
commit b6daa81304
28 changed files with 236 additions and 1338 deletions

View File

@@ -3,7 +3,7 @@
# requires-python = ">=3.11"
# dependencies = ["rich>=13.0", "questionary>=2.0"]
# ///
"""Cagire release builder — replaces build-all.sh, make-dmg.sh, make-appimage.sh."""
"""Cagire release builder."""
from __future__ import annotations
@@ -150,6 +150,8 @@ def get_version(root: Path) -> str:
def builder_for(p: Platform) -> str:
if p.os == "windows" and p.cross:
return "cargo-xwin"
return "cross" if p.cross else "cargo"
@@ -195,12 +197,31 @@ def run_cmd(
# Build functions
# ---------------------------------------------------------------------------
def _macos_env(p: Platform) -> dict[str, str] | None:
if p.os == "macos":
return {"MACOSX_DEPLOYMENT_TARGET": "12.0"}
def _find_llvm_prefix() -> str | None:
"""Find Homebrew LLVM installation path."""
try:
result = subprocess.run(
["brew", "--prefix", "llvm"], capture_output=True, text=True,
)
if result.returncode == 0:
return result.stdout.strip()
except FileNotFoundError:
pass
return None
def _cross_env(p: Platform) -> dict[str, str] | None:
env = {}
if p.os == "macos":
env["MACOSX_DEPLOYMENT_TARGET"] = "12.0"
if p.os == "windows" and p.cross:
env["VCINSTALLDIR"] = "/dummy"
llvm_prefix = _find_llvm_prefix()
if llvm_prefix:
env["LIBCLANG_PATH"] = f"{llvm_prefix}/lib"
return env or None
def _platform_features(p: Platform) -> list[str]:
if p.os == "windows":
return ["--features", "asio"]
@@ -211,7 +232,7 @@ def build_binary(root: Path, p: Platform, log: list[str], extra_args: list[str]
features = _platform_features(p) if platform_features else []
cmd = [builder_for(p), "build", "--release", *target_flags(p), *features, *(extra_args or [])]
log.append(f" Building: {' '.join(extra_args or ['default'])}")
run_cmd(cmd, log, env=_macos_env(p), cwd=root)
run_cmd(cmd, log, env=_cross_env(p), cwd=root)
def bundle_plugins(root: Path, p: Platform, log: list[str]) -> None:
@@ -224,7 +245,7 @@ def bundle_plugins(root: Path, p: Platform, log: list[str]) -> None:
def _bundle_plugins_native(root: Path, p: Platform, log: list[str]) -> None:
log.append(" Bundling plugins (native xtask)")
cmd = ["cargo", "xtask", "bundle", PLUGIN_NAME, "--release", *target_flags(p)]
run_cmd(cmd, log, env=_macos_env(p), cwd=root)
run_cmd(cmd, log, env=_cross_env(p), cwd=root)
def _bundle_plugins_cross(root: Path, p: Platform, log: list[str]) -> None:
@@ -267,7 +288,7 @@ def bundle_desktop_app(root: Path, p: Platform, log: list[str]) -> None:
return
log.append(" Bundling desktop .app")
cmd = ["cargo", "bundle", "--release", "--features", "desktop", "--bin", "cagire-desktop", *target_flags(p)]
run_cmd(cmd, log, env=_macos_env(p), cwd=root)
run_cmd(cmd, log, env=_cross_env(p), cwd=root)
# ---------------------------------------------------------------------------
@@ -444,36 +465,6 @@ def make_appimage(root: Path, binary: Path, arch: str, output_dir: Path, log: li
return _make_appimage_docker(root, binary, arch, output_dir, log)
# ---------------------------------------------------------------------------
# Packaging: NSIS
# ---------------------------------------------------------------------------
def make_nsis(root: Path, rd: Path, version: str, output_dir: Path, config: BuildConfig, log: list[str]) -> str | None:
if not shutil.which("makensis"):
log.append(" makensis not found, skipping NSIS installer")
return None
log.append(" Building NSIS installer")
abs_root = str(root.resolve())
cmd = [
"makensis",
f"-DVERSION={version}",
f"-DICON={abs_root}/assets/Cagire.ico",
f"-DOUTDIR={abs_root}/{OUT}",
]
if config.cli:
cmd.append(f"-DCLI_EXE={abs_root}/{rd.relative_to(root)}/cagire.exe")
if config.desktop:
cmd.append(f"-DDESKTOP_EXE={abs_root}/{rd.relative_to(root)}/cagire-desktop.exe")
cmd.append(str(root / "nsis" / "cagire.nsi"))
run_cmd(cmd, log)
installer = f"cagire-{version}-windows-x86_64-setup.exe"
log.append(f" Installer -> {output_dir / installer}")
return str(output_dir / installer)
# ---------------------------------------------------------------------------
# Artifact copying & packaging dispatch
# ---------------------------------------------------------------------------
@@ -483,7 +474,6 @@ def copy_artifacts(root: Path, p: Platform, config: BuildConfig, log: list[str])
rd = release_dir(root, p)
out = root / OUT
sx = suffix_for(p)
version = get_version(root)
artifacts: list[str] = []
if config.cli:
@@ -515,11 +505,6 @@ def copy_artifacts(root: Path, p: Platform, config: BuildConfig, log: list[str])
if dmg:
artifacts.append(dmg)
if p.os == "windows":
nsis = make_nsis(root, rd, version, out, config, log)
if nsis:
artifacts.append(nsis)
if p.os == "linux":
if config.cli:
ai = make_appimage(root, rd / "cagire", p.arch, out, log)
@@ -725,9 +710,10 @@ def run_builds(
for p in platforms:
_update_phase(p.alias, "waiting", 0)
# Split into native (share cargo lock) and cross (independent Docker builds)
native_platforms = [p for p in platforms if not p.cross]
cross_platforms = [p for p in platforms if p.cross]
# Docker-isolated cross builds (Linux only — each in its own container)
docker_cross = [p for p in platforms if p.cross and p.os != "windows"]
# Local builds share cargo lock — run sequentially
local_builds = [p for p in platforms if not p.cross or p.os == "windows"]
results: list[PlatformResult] = []
completed: dict[str, PlatformResult] = {}
@@ -740,19 +726,19 @@ def run_builds(
return _build_display(platforms, config, completed, start_times, log_max_lines)
with Live(make_display(), console=console, refresh_per_second=4) as live:
# Native builds run sequentially in one thread (they contend on cargo lock).
# Cross builds run in parallel (each in its own Docker container).
with ThreadPoolExecutor(max_workers=max(len(cross_platforms) + 1, 1)) as pool:
# Local builds run sequentially (they contend on cargo lock).
# Docker cross builds run in parallel (each in its own container).
with ThreadPoolExecutor(max_workers=max(len(docker_cross) + 1, 1)) as pool:
futures = {}
if native_platforms:
if local_builds:
f = pool.submit(
_build_native_sequential, root, native_platforms, config,
_build_native_sequential, root, local_builds, config,
completed, start_times,
)
futures[f] = "native"
for p in cross_platforms:
for p in docker_cross:
f = pool.submit(build_platform, root, p, config)
futures[f] = "cross"
@@ -928,20 +914,22 @@ def check_git_clean(root: Path) -> tuple[str, bool]:
def check_prerequisites(platforms: list[Platform], config: BuildConfig) -> None:
"""Verify required tools are available, fail fast if not."""
need_cross = any(p.cross for p in platforms)
need_xwin = any(p.os == "windows" and p.cross for p in platforms)
need_cross = any(p.cross and p.os != "windows" for p in platforms)
need_docker = any(p.cross and p.os == "linux" for p in platforms)
need_bundle = config.desktop and any(not p.cross and p.os == "macos" for p in platforms)
need_nsis = any(p.os == "windows" for p in platforms)
checks: list[tuple[str, bool]] = [("cargo", True)]
if need_xwin:
checks.append(("cargo-xwin", True))
checks.append(("clang", True))
checks.append(("cmake", True))
if need_cross:
checks.append(("cross", True))
if need_docker:
checks.append(("docker", True))
if need_bundle:
checks.append(("cargo-bundle", True))
if need_nsis:
checks.append(("makensis", False))
console.print("[bold]Prerequisites:[/]")
missing_critical: list[str] = []