build: separate package identity from storage paths

This commit is contained in:
2026-08-13 05:37:56 +09:00
parent 5f0f544157
commit c3df0afeb0
11 changed files with 1167 additions and 505 deletions

View File

@@ -60,6 +60,25 @@ fn slurp(path: *u8) (*u8, u64) = {
return buf.ptr, nz;
};
fn exportownermatches(buf: *u8, n: u64, path: *u8) bool = {
let prefix: str = "//ww:module ";
let pn: u64 = cstrlen(path);
let poff: u64 = prefix.len: u64;
let need: u64 = poff + pn + 1u64;
if (n < need) { return false; };
let i: u64 = 0u64;
for (i < poff) {
if (buf[i] != prefix.ptr[i]) { return false; };
i += 1u64;
};
i = 0u64;
for (i < pn) {
if (buf[poff + i] != path[i]) { return false; };
i += 1u64;
};
return buf[need - 1u64] == 10u8;
};
export fn main(argc: i32, argv: **u8) i32 = {
let src: *u8 = nil;
let out: *u8 = nil;
@@ -204,6 +223,17 @@ export fn main(argc: i32, argv: **u8) i32 = {
os.write(2, nl.ptr, nl.len: u64);
return 1;
};
if (!exportownermatches(ibuf, ilen, importpaths[importi])) {
let pre: str = "w6c: import ";
let mid: str = ": export owner mismatch in ";
let nl: str = "\n";
os.write(2, pre.ptr, pre.len: u64);
os.write(2, importpaths[importi], cstrlen(importpaths[importi]));
os.write(2, mid.ptr, mid.len: u64);
os.write(2, importfiles[importi], cstrlen(importfiles[importi]));
os.write(2, nl.ptr, nl.len: u64);
return 1;
};
let il: lex;
lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen);
let ips: parser;

View File

@@ -1017,6 +1017,9 @@ fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
pkg = file.nmod;
};
};
if (file.nmod.len > 0) {
wputs(fd, "//ww:module "); wputs(fd, file.nmod); wputs(fd, "\n");
};
wputs(fd, "package ");
wputs(fd, pkg);
wputs(fd, ";\n");

View File

@@ -13,6 +13,8 @@
package main;
import crypto.sha256;
import hash;
import os;
import os.exec;
import rt;
@@ -137,8 +139,9 @@ fn selfdirinto(dst: *u8, dstsz: u64, argv0: *u8) void = {
};
fn joinpath(dir: *u8, name: *u8) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
let need: u64 = cstrlen(dir) + 1u64 + cstrlen(name) + 1u64;
let buf: []u8 = alloc([], need)!;
buf.len = need: i32;
let off: u64 = cstrinto(buf.ptr, 0u64, dir);
off = byteinto(buf.ptr, off, 47u8);
off = cstrinto(buf.ptr, off, name);
@@ -147,8 +150,9 @@ fn joinpath(dir: *u8, name: *u8) *u8 = {
};
fn joinpathlit(dir: *u8, name: str) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
let need: u64 = cstrlen(dir) + 1u64 + name.len: u64 + 1u64;
let buf: []u8 = alloc([], need)!;
buf.len = need: i32;
let off: u64 = cstrinto(buf.ptr, 0u64, dir);
off = byteinto(buf.ptr, off, 47u8);
off = strinto(buf.ptr, off, name);
@@ -495,7 +499,6 @@ def SEP_ROLE_GENERATED_MAIN: i32 = 2;
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
def SEP_MAXPRODUCT: i32 = 256;
def SEP_MAXCONTEXT: i32 = 257;
def SEP_ARTIFACT_MAX: i32 = 1024;
// Classify a selected directory entry: 1 production, 2 test, 0 skipped,
// -1 @test outside *_test.ww, -2 non-regular source.
@@ -558,11 +561,6 @@ fn dirpackagename(path: *u8) *u8 = {
cerr(": error: invalid or missing package clause\n");
return nil;
};
if (imports.nmod.len >= 256) {
cerrpos(imports.file, imports.line, imports.col);
cerr(": error: package name is too long\n");
return nil;
};
return arenadupcstr(imports.nmod.ptr, imports.nmod.len: u64);
};
@@ -759,8 +757,9 @@ fn makestem(stem: *u8, src: *u8) void = {
};
fn appendlit(stem: *u8, suffix: str) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
let need: u64 = cstrlen(stem) + suffix.len: u64 + 1u64;
let buf: []u8 = alloc([], need)!;
buf.len = need: i32;
let off: u64 = cstrinto(buf.ptr, 0u64, stem);
off = strinto(buf.ptr, off, suffix);
cstrseal(buf.ptr, off);
@@ -804,6 +803,8 @@ type seppkg = struct {
entry: *u8, // resolved package dir (or file, file root), NUL-term
canon: *u8, // canonical location; never package identity
artifact: *u8, // stable non-importable variant artifact key
storage: *u8, // internal storage basename; never package identity
storagehashed: bool,
name: *u8, // validated declared name; directory packages only
testpackage: *u8,
sources: **u8, // owned, byte-sorted selected paths; dirs only
@@ -930,10 +931,7 @@ fn sepcommandcompilermarker(g: *sepgraph, pi: i32) bool = {
// identity. The compiler path is derived from that base and the semantic
// variant; root/product/artifact state never participates.
fn sepbindimportbase(g: *sepgraph, pi: i32, base: *u8) i32 = {
if (base == nil || base[0u64] == 0u8 || cstrlen(base) >= 256u64) {
cerr("ww: package path is too long (limit 255 bytes)\n");
return -1;
};
if (base == nil || base[0u64] == 0u8) { return -1; };
if (!reservedimportpath(base) && !sepimportbasevalid(base)) {
cerr("ww: invalid package path "); cerr(pathstr(base)); cerr("\n");
return -1;
@@ -992,10 +990,6 @@ fn sepbindimportbase(g: *sepgraph, pi: i32, base: *u8) i32 = {
fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
isdir: i32, variant: i32, testpackage: *u8, role: i32,
artifact: *u8, root: bool) i32 = {
if (cstrlen(path) >= 256u64) {
cerr("ww: package path is too long (limit 255 bytes)\n");
return -1;
};
let canon: *u8 = nil;
if (isdir != 0) {
canon = canonicaldir(pathstr(entry));
@@ -1004,10 +998,6 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
cerr(pathstr(entry)); cerr("\n");
return -1;
};
if (cstrlen(canon) >= 1024u64) {
cerr("ww: canonical package path is too long\n");
return -1;
};
};
let incoming: *u8 = nil;
if (isdir != 0 && path[0u64] != 0u8) {
@@ -1094,6 +1084,8 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
g.pkg[g.n].entry = arenadupcstr(entry, elen);
g.pkg[g.n].canon = canon;
g.pkg[g.n].artifact = nil;
g.pkg[g.n].storage = nil;
g.pkg[g.n].storagehashed = false;
g.pkg[g.n].name = nil;
g.pkg[g.n].testpackage = nil;
if (testpackage != nil) {
@@ -1186,17 +1178,12 @@ fn sepgraphfree(g: *sepgraph) void = {
fn sepcontextfor(g: *sepgraph, root: *u8, incs: *u8,
toolsrcdir: *u8) i32 = {
let cap: u64 = (os.PATH_MAX: u64) * 2u64;
let need: u64 = cstrlen(root) + 1u64 + cstrlen(toolsrcdir) + 1u64;
if (incs != nil && incs[0u64] != 0u8) {
need += cstrlen(incs) + 1u64;
};
if (need > cap) {
cerr("ww: package import search path is too long\n");
return -1;
};
let search: []u8 = alloc([], cap)!;
search.len = cap: i32;
let search: []u8 = alloc([], need)!;
search.len = need: i32;
let off: u64 = cstrinto(search.ptr, 0u64, root);
off = byteinto(search.ptr, off, 58u8);
if (incs != nil && incs[0u64] != 0u8) {
@@ -1221,59 +1208,142 @@ fn sepcontextfor(g: *sepgraph, root: *u8, incs: *u8,
return result;
};
// Build one artifact path. Test roots use distinct names even though both
// compiler units reset to the bare executable namespace.
def SEP_NAME_MAX: u64 = 255u64;
fn sepstoragedigest(p: *seppkg) *u8 = {
let state: sha256.state = sha256.sha256();
let h: *hash.hash = (&state): *hash.hash;
hash.write(h, strings.toutf8("ww-package-storage-v2:"));
let tag: [4]u8;
tag[0] = ('0': i32 + p.variant): u8;
tag[1] = ':': u8;
tag[2] = ('0': i32 + p.role): u8;
tag[3] = ':': u8;
hash.write(h, tag[0:4]);
hash.write(h, strings.toutf8(pathstr(p.path)));
let zero: [1]u8;
zero[0] = 0u8;
hash.write(h, zero[0:1]);
hash.write(h, strings.toutf8(pathstr(p.canon)));
let digest: [32]u8;
hash.sum(h, digest[0:32]);
let need: u64 = "__wwpkg.v".len: u64 + 1u64 + ".r".len: u64
+ 1u64 + ".h".len: u64 + 64u64 + 1u64;
let out: []u8 = alloc([], need)!;
out.len = need: i32;
let off: u64 = strinto(out.ptr, 0u64, "__wwpkg.v");
off = byteinto(out.ptr, off, tag[0]);
off = strinto(out.ptr, off, ".r");
off = byteinto(out.ptr, off, tag[2]);
off = strinto(out.ptr, off, ".h");
let hex: str = "0123456789abcdef";
let i: i32 = 0;
for (i < 32) {
let high: i32 = (digest[i] / 16u8): i32;
let low: i32 = (digest[i] % 16u8): i32;
off = byteinto(out.ptr, off, hex[high]);
off = byteinto(out.ptr, off, hex[low]);
i += 1;
};
cstrseal(out.ptr, off);
return out.ptr;
};
fn seplegacyartifact(p: *seppkg) *u8 = {
if (p.artifact != nil && p.artifact[0u64] != 0u8) { return p.artifact; };
if (p.path != nil && p.path[0u64] != 0u8) { return p.path; };
return "__root\0".ptr;
};
fn sepvalidatestoragepath(p: *seppkg, scratch: *u8) i32 = {
let need: u64 = cstrlen(scratch) + 1u64 + cstrlen(p.storage)
+ ".unit.new".len: u64 + 1u64;
if (cstrlen(p.storage) + ".unit.new".len: u64 > SEP_NAME_MAX
|| need > os.PATH_MAX: u64) {
cerr("ww: package artifact path is too long\n");
return -1;
};
return 0;
};
fn sepassignstorage(p: *seppkg, scratch: *u8) i32 = {
let base: *u8 = seplegacyartifact(p);
let need: u64 = cstrlen(scratch) + 1u64 + cstrlen(base)
+ ".unit.new".len: u64 + 1u64;
if (cstrlen(base) + ".unit.new".len: u64 <= SEP_NAME_MAX
&& need <= os.PATH_MAX: u64) {
p.storage = arenadupcstr(base, cstrlen(base));
p.storagehashed = false;
} else {
p.storage = sepstoragedigest(p);
p.storagehashed = true;
};
return sepvalidatestoragepath(p, scratch);
};
fn sepfname(g: *sepgraph, pi: i32, scratch: *u8, suffix: str) *u8 = {
let buf: []u8 = alloc([], SEP_ARTIFACT_MAX: u64)!;
buf.len = SEP_ARTIFACT_MAX;
let need: u64 = cstrlen(scratch) + 1u64
+ cstrlen(g.pkg[pi].storage) + suffix.len: u64 + 1u64;
let buf: []u8 = alloc([], need)!;
buf.len = need: i32;
let off: u64 = cstrinto(buf.ptr, 0u64, scratch);
off = byteinto(buf.ptr, off, 47u8); // '/'
if (g.pkg[pi].artifact != nil) {
off = cstrinto(buf.ptr, off, g.pkg[pi].artifact);
} else { if (g.pkg[pi].path[0u64] != 0u8) {
off = cstrinto(buf.ptr, off, g.pkg[pi].path);
} else {
off = strinto(buf.ptr, off, "__root");
}; };
off = cstrinto(buf.ptr, off, g.pkg[pi].storage);
off = strinto(buf.ptr, off, suffix);
cstrseal(buf.ptr, off);
return buf.ptr;
};
// sepfname uses SEP_ARTIFACT_MAX storage. Validate the longest suffix once
// before opening files so two action identities can never alias by truncation.
fn sepvalidateartifactpaths(g: *sepgraph, scratch: *u8) i32 = {
let i: i32 = 0;
for (i < g.n) {
if (g.pkg[i].failed || !g.pkg[i].loaded) { i += 1; continue; };
let base: *u8 = g.pkg[i].artifact;
if (base == nil) {
base = g.pkg[i].path;
if (base[0u64] == 0u8) { base = "__root\0".ptr; };
};
let need: u64 = cstrlen(scratch) + 1u64 + cstrlen(base)
+ ".unit.new".len: u64 + 1u64;
if (need > SEP_ARTIFACT_MAX: u64) {
cerr("ww: package artifact path is too long\n");
return -1;
};
let j: i32 = i + 1;
for (j < g.n) {
if (g.pkg[j].failed || !g.pkg[j].loaded) { j += 1; continue; };
let other: *u8 = g.pkg[j].artifact;
if (other == nil) {
other = g.pkg[j].path;
if (other[0u64] == 0u8) { other = "__root\0".ptr; };
};
if (cstreq(base, other)) {
cerr("ww: package actions share artifact identity ");
cerr(pathstr(base)); cerr("\n");
return -1;
};
j += 1;
};
if (g.pkg[i].storage == nil
&& sepassignstorage(&g.pkg[i], scratch) < 0) { return -1; };
i += 1;
};
let changed: bool = true;
for (changed) {
changed = false;
i = 0;
for (i < g.n && !changed) {
if (g.pkg[i].failed || !g.pkg[i].loaded) { i += 1; continue; };
let j: i32 = i + 1;
for (j < g.n) {
if (g.pkg[j].failed || !g.pkg[j].loaded
|| !cstreq(g.pkg[i].storage, g.pkg[j].storage)) {
j += 1; continue;
};
if (!g.pkg[i].storagehashed || !g.pkg[j].storagehashed) {
if (!g.pkg[i].storagehashed) {
g.pkg[i].storage = sepstoragedigest(&g.pkg[i]);
g.pkg[i].storagehashed = true;
if (sepvalidatestoragepath(&g.pkg[i], scratch) < 0) {
return -1;
};
};
if (!g.pkg[j].storagehashed) {
g.pkg[j].storage = sepstoragedigest(&g.pkg[j]);
g.pkg[j].storagehashed = true;
if (sepvalidatestoragepath(&g.pkg[j], scratch) < 0) {
return -1;
};
};
changed = true;
j = g.n;
} else {
cerr("ww: package storage collision for ");
cerr(pathstr(g.pkg[i].path)); cerr(" in ");
cerr(pathstr(g.pkg[i].canon)); cerr(" and ");
cerr(pathstr(g.pkg[j].path)); cerr(" in ");
cerr(pathstr(g.pkg[j].canon)); cerr(" at ");
cerr(pathstr(g.pkg[i].storage)); cerr("\n");
return -1;
};
};
i += 1;
};
};
return 0;
};
@@ -1384,11 +1454,6 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
&& (ownedsource != 0 || g.pkg[pi].name == nil)) {
let declared: *u8 = imports.nmod.ptr;
let declaredn: u64 = imports.nmod.len: u64;
if (declaredn >= 256u64) {
cerrpos(imports.file, imports.line, imports.col);
cerr(": error: package name is too long\n");
return -1;
};
if (g.pkg[pi].name == nil) {
g.pkg[pi].name = arenadupcstr(declared, declaredn);
} else { if (bytecmp(g.pkg[pi].name, cstrlen(g.pkg[pi].name),
@@ -1465,11 +1530,6 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
cerr(u.usepath); cerr(" is reserved\n");
return -1;
};
if (idn >= 256u64) {
cerrpos(u.file, u.line, u.col);
cerr(": error: import path is too long (limit 255 bytes)\n");
return -1;
};
let externalproduction: bool = false;
let ipath: *u8 = nil;
if (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
@@ -1638,6 +1698,8 @@ fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32,
let variantartifact: *u8 = g.pkg[variant].artifact;
if (variantartifact == nil) { variantartifact = g.pkg[variant].path; };
p.artifact = appendlit(variantartifact, "-main");
p.storage = nil;
p.storagehashed = false;
p.name = arenadupcstr("main\0".ptr, 4u64);
p.testpackage = nil;
p.sources = nil;
@@ -1888,10 +1950,7 @@ fn sepreverseimportbase(g: *sepgraph, p: *seppkg, context: i32,
if (rel != nil) {
let converted: i32 = sepimportpathfromrelative(rel,
out, outsz);
if (converted < 0) {
cerr("ww: package path is too long (limit 255 bytes)\n");
return -1;
};
if (converted < 0) { return -1; };
if (converted > 0 && reservedimportpath(out)) {
converted = 0;
};
@@ -1981,12 +2040,14 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
let ci: i32 = 0;
for (ci < g.ncontext) {
if (p.contextstate[ci] == 2u8) {
let candidate: [256]u8;
let candidatesz: u64 = cstrlen(p.canon) + 1u64;
let candidate: []u8 = alloc([], candidatesz)!;
candidate.len = candidatesz: i32;
let found: i32 = sepreverseimportbase(g, p, ci,
&candidate[0], 256u64);
candidate.ptr, candidatesz);
if (found < 0) { return -1; };
if (found > 0 && sepbindimportbase(g, pi,
&candidate[0]) < 0) { return -1; };
candidate.ptr) < 0) { return -1; };
};
ci += 1;
};
@@ -2014,10 +2075,7 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
};
if (base == nil) {
base = seplocalimportbase(p);
if (base == nil || cstrlen(base) >= 256u64) {
cerr("ww: local package identity is too long (limit 255 bytes)\n");
return -1;
};
if (base == nil) { return -1; };
};
if (sepbindimportbase(g, pi, base) < 0) { return -1; };
};
@@ -2353,6 +2411,70 @@ fn filesizenonzero(path: *u8) bool = {
return ok;
};
fn sepvalidateunitowner(g: *sepgraph, pi: i32, scratch: *u8) i32 = {
let unit: *u8 = sepfname(g, pi, scratch, ".unit.ww");
let fi: os.filestat;
match (os.lstat(&fi, pathstr(unit))) {
case let e: os.oserror => {
if ((e: i64) == -2i64) { return 0; };
cerr("ww: package storage owner mismatch at ");
cerr(pathstr(g.pkg[pi].storage)); cerr(" for ");
if (g.pkg[pi].path[0u64] == 0u8) { cerr("(root)"); }
else { cerr(pathstr(g.pkg[pi].path)); };
cerr("\n");
return -1;
};
case void => void;
};
let typ: u32 = (fi.mode: u32) & 61440u32;
if (typ != os.mode.REG: u32) {
cerr("ww: package storage owner mismatch at ");
cerr(pathstr(g.pkg[pi].storage)); cerr(" for ");
if (g.pkg[pi].path[0u64] == 0u8) { cerr("(root)"); }
else { cerr(pathstr(g.pkg[pi].path)); };
cerr("\n");
return -1;
};
let buf: *u8;
let n: u64;
buf, n = slurp(unit);
let prefix: str = "//ww:module-reset";
let pathn: u64 = cstrlen(g.pkg[pi].path);
let want: u64 = prefix.len: u64 + 1u64;
if (pathn > 0u64) { want += 1u64 + pathn; };
let matches: bool = buf != nil && n >= want
&& bytecmp(buf, prefix.len: u64, prefix.ptr,
prefix.len: u64) == 0;
if (matches) {
let off: u64 = prefix.len: u64;
if (pathn == 0u64) {
matches = buf[off] == ('\n': u8);
} else {
matches = buf[off] == (' ': u8)
&& bytecmp(buf + off + 1u64, pathn,
g.pkg[pi].path, pathn) == 0
&& buf[off + 1u64 + pathn] == ('\n': u8);
};
};
if (matches) { return 1; };
cerr("ww: package storage owner mismatch at ");
cerr(pathstr(g.pkg[pi].storage)); cerr(" for ");
if (pathn == 0u64) { cerr("(root)"); }
else { cerr(pathstr(g.pkg[pi].path)); };
cerr("\n");
return -1;
};
fn sepvalidateworkdirowners(g: *sepgraph, scratch: *u8) i32 = {
let i: i32 = 0;
for (i < g.n) {
if (!g.pkg[i].failed && g.pkg[i].loaded
&& sepvalidateunitowner(g, i, scratch) < 0) { return -1; };
i += 1;
};
return 0;
};
// Byte equality of two files; absence or IO error is inequality.
fn fileequal(a: *u8, b: *u8) bool = {
let fa: i32 = os.open(pathstr(a), os.flag.RDONLY, 0i32);
@@ -2423,14 +2545,14 @@ fn copyfileatomic(src: *u8, dst: *u8) i32 = {
fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) {
if (emitasm != 0) {
return "ww workdir fmt 9 mode test asm 1\n";
return "ww workdir fmt 11 mode test asm 1\n";
};
return "ww workdir fmt 9 mode test asm 0\n";
return "ww workdir fmt 11 mode test asm 0\n";
};
if (emitasm != 0) {
return "ww workdir fmt 8 mode build asm 1\n";
return "ww workdir fmt 10 mode build asm 1\n";
};
return "ww workdir fmt 8 mode build asm 0\n";
return "ww workdir fmt 10 mode build asm 0\n";
};
fn stampmatches(path: *u8, want: str) bool = {
@@ -2595,20 +2717,18 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
};
};
let stem: []u8 = alloc([], (os.PATH_MAX: u64))!;
stem.len = os.PATH_MAX;
let stem: *u8 = nil;
if (entryisdir != 0) {
let dlen: u64 = cstrlen(srcd.ptr);
let bo: u64 = basenameoff(srcd.ptr, dlen);
let off: u64 = cstrinto(stem.ptr, 0u64, srcd.ptr);
stem[off] = 47u8; off += 1u64; // '/'
let i: u64 = bo;
for (i < dlen) { stem[off] = srcd[i]; off += 1u64; i += 1u64; };
cstrseal(stem.ptr, off);
stem = joinpath(srcd.ptr, srcd.ptr + bo);
} else {
makestem(stem.ptr, src);
let stembuf: []u8 = alloc([], cstrlen(src) + 1u64)!;
stembuf.len = (cstrlen(src) + 1u64): i32;
makestem(stembuf.ptr, src);
stem = stembuf.ptr;
};
let effstem: *u8 = stem.ptr;
let effstem: *u8 = stem;
if (objstem != nil) { effstem = objstem; };
let warm: bool = false;
if (workdir != nil) {
@@ -2675,7 +2795,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
if (!fileequal(toola, a6)) { staleall = true; };
};
};
if (staleall && invalidateworkdirunits(scratch) != 0) { return 1; };
};
let rtpaths: []*u8 = alloc([], 2u64)!;
@@ -2882,6 +3001,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
};
};
if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; };
if (warm && sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
if (warm && staleall && invalidateworkdirunits(scratch) != 0) { return 1; };
let rootpackage: bool = packageonly != 0;
if (rootpackage && !g.pkg[products[0].root].failed
&& cstreqlit(g.pkg[products[0].root].name, "main")) {
@@ -3534,8 +3655,14 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let workdir: *u8 = nil; // -w persistent package-artifact workdir
let emitasm: i32 = 0;
let packageonly: i32 = 0;
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
let inccap: u64 = 1u64;
let capi: i32 = start;
for (capi < argc) {
inccap += cstrlen(argv[capi]) + 1u64;
capi += 1;
};
let incs: []u8 = alloc([], inccap)!;
incs.len = inccap: i32;
let incoff: u64 = 0u64;
cstrseal(incs.ptr, 0u64);
@@ -3739,8 +3866,14 @@ fn makedrivertmp(buf: *u8, prefix: str) void = {
fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
let src: *u8 = nil;
let passstart: i32 = -1; // first argv idx to pass through to program
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
let inccap: u64 = 1u64;
let capi: i32 = start;
for (capi < argc) {
inccap += cstrlen(argv[capi]) + 1u64;
capi += 1;
};
let incs: []u8 = alloc([], inccap)!;
incs.len = inccap: i32;
let incoff: u64 = 0u64;
cstrseal(incs.ptr, 0u64);
@@ -4038,8 +4171,14 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
// to the test binary as argv[1] (single-file/module only; dir-mode
// rejects). cstage twin: do_test `pattern`.
let patarg: *u8 = nil;
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
let inccap: u64 = 1u64;
let capi: i32 = start;
for (capi < argc) {
inccap += cstrlen(argv[capi]) + 1u64;
capi += 1;
};
let incs: []u8 = alloc([], inccap)!;
incs.len = inccap: i32;
let incoff: u64 = 0u64;
cstrseal(incs.ptr, 0u64);
@@ -4093,7 +4232,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
if ((!cstreqlit(kind, "production")
&& !cstreqlit(kind, "same")
&& !cstreqlit(kind, "external"))
|| pn == 0u64 || pn >= 256u64
|| pn == 0u64
|| dir[0u64] == 0u8 || output[0u64] == 0u8
|| status[0u64] == 0u8
|| (cstreqlit(kind, "external")