Skip to content

Host-native SCRIPTC_CC=zigcc builds use system clang/ar/libz instead of the zig toolchain #252

Description

@euxaristia

Summary

On a host-native zig build — SCRIPTC_CC=zigcc with no SCRIPTC_TARGET — the vendored-prerequisite and zlib paths ignore the selected driver and hardcode a system clang, a system ar, and a system -lz. Everything else (zlib/lre objects, the program TU, the final link) uses zig cc.

On Linux/macOS this silently mixes toolchains but usually links. On Windows it fails outright: none of ar, a system libz, or an ABI-compatible clang exists there.

Adding SCRIPTC_TARGET=<host triple> avoids all of it, because every one of these sites is already correct on the cross path. So this is about the host-native zigcc configuration, which is accepted but cannot work.

Environment

@scriptc/compiler 0.0.35, Windows 11 x64, zig 0.16.0, Bun 1.4.0-canary. The only clang on PATH targets x86_64-unknown-windows-msvc; zig cc defaults to x86_64-unknown-windows-gnu.

Reproduction

// t.ts
const res = await fetch("https://example.com");
console.log(res.status);
$ SCRIPTC_CC=zigcc scriptc run t.ts
error: Executable not found in $PATH: "ar"
    cmd: "ar rcs ...\scriptc-vendor-mbedtls-plain-native-win32-x64-<hash>\libmbedtls.a ..."
   code: "ENOENT"

Supplying an ar and a matching clang exposes the next two in order:

error: unable to find dynamic system library 'z' using strategy 'paths_first'. searched paths: none
scr_fetch.c:95:10: fatal error: 'zlib.h' file not found

With SCRIPTC_TARGET=x86_64-windows-gnu added and nothing else changed, the same project builds, links, and runs.

Affected sites

Lines from the published dist/backend/cc.js of 0.0.35.

Line Code Effect
579 ...(driver.target === null ? ["clang", "ar"] : []) cache identity records tools the build won't use
742–743 driver.target === null ? ["clang"] : driver.argv / driver.target === null ? ["ar"] : [...] quickjs built + archived by system clang/ar
967–968 driver.target !== null ? driver.argv : ["clang"] / same for ar mbedTLS built + archived by system clang/ar
3713, 3827, 3831 ... && driver.target !== null gating zlibObjects and -I vendorZlibDir() vendored zlib headers/objects skipped
3959, 4138, 4158 ... && driver.target === null ? ["-lz"] : [] system libz linked instead

Each uses driver.target === null ("is this a host build?") as a proxy for "a POSIX toolchain and system libraries are present." Those are independent, and the proxy only holds when the host driver is also the bare-clang one.

Why these look like bugs, not intent

ensureLreObjects (797) and ensureZlibObjects (858) solve the same problem correctly — compiling with driver.argv[0] and keying the cache flavor on the driver (... driver.argv[0] === "clang" ? "" : "-zigcc" ...), under this comment:

The flavor keys the DRIVER as well as the target: a zig-cc-built object set must never be handed to a clang link (or vice versa) off a shared cache directory.

Sites 742 and 967 do exactly what that forbids. Two sites already use the right dispatch:

// cc.js:1163 and cc.js:4456
const arArgv = driver.argv[0] === "zig" ? [driver.argv[0], "ar"] : ["ar"];

Suggested fix

Dispatch on the driver instead of driver.target:

function driverUsesZig(driver)    { return driver.argv[0] === "zig"; }
function vendorArArgv(driver)     { return driverUsesZig(driver) ? [driver.argv[0], "ar"] : ["ar"]; }
function usesVendoredZlib(driver) { return driver.target !== null || driverUsesZig(driver); }

compileArgv becomes driver.argv at both vendor sites, arArgv becomes vendorArArgv(driver), and the zlib gates become usesVendoredZlib(driver) / its negation. The -I vendorZlibDir() spread is already keyed on zlibObjects.length > 0, so the header path follows from the one gate change.

I applied exactly this against 0.0.35: a project that previously failed then built and ran clean under plain SCRIPTC_CC=zigcc with no PATH shims, with results identical to the SCRIPTC_TARGET=x86_64-windows-gnu path.

If host-native zigcc isn't a configuration you intend to support, rejecting it in resolveCc with a message pointing at SCRIPTC_TARGET would be far cheaper and would save the next person the three-error trail.

Secondary

For the -lz and zlib.h failures opts.systemLibraries is empty, so compileC reports "This is a scriptc bug (generated C should always compile) unless zig cc itself is missing/broken." zig cc is fine — the missing input is a system library scriptc chose to add.

Related

#25 is the default-clang path failing on Windows over ssize_t (MSVC-target clang lacks the POSIX declarations) — different and already understood. This one is about the zigcc driver being selected and then not used consistently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions