From c546442b4f8f7de4a2253d3fe9426ce3b8f63f48 Mon Sep 17 00:00:00 2001 From: minhn4 Date: Fri, 25 Sep 2026 17:33:26 +0700 Subject: [PATCH] fix(sandbox): build the universal VM helper on Command Line Tools 27 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Release packaging (desktop prebuild → build-vzhelper.ts --universal) failed on Command Line Tools 27 / Swift 6.4: the helper's macOS 12 deployment target links Swift's back-compatibility libraries (libswiftCompatibility56, libswiftCompatibilityPacks), and this toolchain ships every one of them for arm64/arm64e only, so the x86_64 slice failed to link ("fat file missing arch 'x86_64'", undefined symbols). macOS releases had to be built by hand. When the toolchain's libswiftCompatibility56.a has no x86_64 slice, the Intel slice is now built with `-runtime-compatibility-version none`. The arm64 slice keeps the libraries, and a toolchain that still ships Intel copies keeps them for Intel too; the only cost is Intel Macs on macOS 12.0–12.2 running the helper without those runtime fixes. Also quiets the failed first attempt on the default build system before the `--build-system native` retry (#380), so a successful build no longer logs a wall of errors. Co-Authored-By: Claude Opus 5.5 --- .../codev-sandbox/script/build-vzhelper.ts | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/codev-sandbox/script/build-vzhelper.ts b/packages/codev-sandbox/script/build-vzhelper.ts index 273f9cedd..94b0af679 100644 --- a/packages/codev-sandbox/script/build-vzhelper.ts +++ b/packages/codev-sandbox/script/build-vzhelper.ts @@ -7,6 +7,7 @@ // build to xcbuild, which only full Xcode ships — Command Line Tools setups // error with "xcbuild executable ... does not exist". import { $ } from "bun" +import { dirname, join } from "node:path" import { fileURLToPath } from "node:url" const root = fileURLToPath(new URL("../native/vzhelper/", import.meta.url)) @@ -17,11 +18,9 @@ const slices = await (async () => { await swiftBuild([]) return [`${root}.build/release/codev-vmm`] } - const triples = ["arm64-apple-macosx", "x86_64-apple-macosx"] - for (const triple of triples) { - await swiftBuild(["--triple", triple]) - } - return triples.map((triple) => `${root}.build/${triple}/release/codev-vmm`) + await swiftBuild(["--triple", "arm64-apple-macosx"]) + await swiftBuild(["--triple", "x86_64-apple-macosx", ...(await intelCompatibilityFlags())]) + return ["arm64-apple-macosx", "x86_64-apple-macosx"].map((triple) => `${root}.build/${triple}/release/codev-vmm`) })() await $`mkdir -p ${root}dist` @@ -40,8 +39,28 @@ console.log(`built ${root}dist/codev-vmm${universal ? " (universal)" : ""}`) // first attempt in case a later Swift drops it. async function swiftBuild(extra: string[]) { const args = ["build", "-c", "release", "--package-path", root, ...extra] - const first = await $`swift ${args}`.nothrow() + // Quiet: on the toolchains this exists for, the default backend's failure + // is a wall of expected noise. The retry's own error still prints. + const first = await $`swift ${args}`.nothrow().quiet() if (first.exitCode === 0) return console.warn("swift build failed with the default build system; retrying with --build-system native") await $`swift ${args} --build-system native` } + +// A macOS 12 deployment target links Swift's back-compatibility libraries +// (libswiftCompatibility56 for macOS 12.0–12.2, libswiftCompatibilityPacks), +// and Command Line Tools 27 ships them for arm64/arm64e only — the x86_64 +// link fails with "fat file missing arch 'x86_64'" and undefined symbols. +// When the toolchain lacks the Intel copies, the Intel slice is built +// without them instead. The cost is confined to Intel Macs still on macOS +// 12.0–12.2, which run the helper without those runtime fixes; Apple Silicon +// keeps them, and a toolchain that still has Intel copies keeps them for +// Intel too. +async function intelCompatibilityFlags(): Promise { + const swift = (await $`xcrun --find swift`.text()).trim() + const library = join(dirname(dirname(swift)), "lib", "swift", "macosx", "libswiftCompatibility56.a") + const archs = await $`lipo -archs ${library}`.nothrow().quiet().text() + if (archs.split(/\s+/).includes("x86_64")) return [] + console.warn("this Swift toolchain has no x86_64 compatibility libraries; building the Intel slice without them") + return ["-Xswiftc", "-runtime-compatibility-version", "-Xswiftc", "none"] +}