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.
Summary
On a host-native zig build —
SCRIPTC_CC=zigccwith noSCRIPTC_TARGET— the vendored-prerequisite and zlib paths ignore the selected driver and hardcode a systemclang, a systemar, and a system-lz. Everything else (zlib/lre objects, the program TU, the final link) useszig cc.On Linux/macOS this silently mixes toolchains but usually links. On Windows it fails outright: none of
ar, a systemlibz, or an ABI-compatibleclangexists 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-nativezigccconfiguration, which is accepted but cannot work.Environment
@scriptc/compiler0.0.35, Windows 11 x64, zig 0.16.0, Bun 1.4.0-canary. The onlyclangon PATH targetsx86_64-unknown-windows-msvc;zig ccdefaults tox86_64-unknown-windows-gnu.Reproduction
Supplying an
arand a matchingclangexposes the next two in order:With
SCRIPTC_TARGET=x86_64-windows-gnuadded and nothing else changed, the same project builds, links, and runs.Affected sites
Lines from the published
dist/backend/cc.jsof 0.0.35....(driver.target === null ? ["clang", "ar"] : [])driver.target === null ? ["clang"] : driver.argv/driver.target === null ? ["ar"] : [...]driver.target !== null ? driver.argv : ["clang"]/ same forar... && driver.target !== nullgatingzlibObjectsand-I vendorZlibDir()... && driver.target === null ? ["-lz"] : []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) andensureZlibObjects(858) solve the same problem correctly — compiling withdriver.argv[0]and keying the cache flavor on the driver (... driver.argv[0] === "clang" ? "" : "-zigcc" ...), under this comment:Sites 742 and 967 do exactly what that forbids. Two sites already use the right dispatch:
Suggested fix
Dispatch on the driver instead of
driver.target:compileArgvbecomesdriver.argvat both vendor sites,arArgvbecomesvendorArArgv(driver), and the zlib gates becomeusesVendoredZlib(driver)/ its negation. The-I vendorZlibDir()spread is already keyed onzlibObjects.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=zigccwith no PATH shims, with results identical to theSCRIPTC_TARGET=x86_64-windows-gnupath.If host-native
zigccisn't a configuration you intend to support, rejecting it inresolveCcwith a message pointing atSCRIPTC_TARGETwould be far cheaper and would save the next person the three-error trail.Secondary
For the
-lzandzlib.hfailuresopts.systemLibrariesis empty, socompileCreports "This is a scriptc bug (generated C should always compile) unless zig cc itself is missing/broken."zig ccis fine — the missing input is a system library scriptc chose to add.Related
#25 is the default-
clangpath failing on Windows overssize_t(MSVC-target clang lacks the POSIX declarations) — different and already understood. This one is about thezigccdriver being selected and then not used consistently.