ww: canonicalize directory package identities
This commit is contained in:
@@ -24,6 +24,7 @@ import syntax;
|
||||
// (os.PATH_MAX: i32 = 4096) — the duplicate u64 def was dropped to close
|
||||
// the cgen #127 mod-mangle attribution bug consumer per rule-7.
|
||||
def CMD_MAX: u64 = 8192u64;
|
||||
def SEP_LOCAL_IMPORT_PREFIX: str = "__wwlocal";
|
||||
|
||||
let selfpath: *u8;
|
||||
|
||||
@@ -164,6 +165,42 @@ fn owncstr(s: str) *u8 = {
|
||||
return b.ptr;
|
||||
};
|
||||
|
||||
fn reservedimport(s: str) bool = {
|
||||
let prefix: str = SEP_LOCAL_IMPORT_PREFIX;
|
||||
if (s.len < prefix.len) { return false; };
|
||||
let i: i32 = 0;
|
||||
for (i < prefix.len) {
|
||||
if (s[i] != prefix[i]) { return false; };
|
||||
i += 1;
|
||||
};
|
||||
return s.len == prefix.len || s[prefix.len] == '.': u8;
|
||||
};
|
||||
|
||||
fn reservedimportpath(p: *u8) bool = {
|
||||
return reservedimport(pathstr(p));
|
||||
};
|
||||
|
||||
// The driver is single-threaded while constructing a graph. Saving and
|
||||
// restoring cwd gives WWstage the same symlink-resolved absolute directory
|
||||
// spelling that Cstage obtains from realpath, without adding a library API.
|
||||
fn canonicaldir(path: str) *u8 = {
|
||||
let before: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||||
before.len = os.PATH_MAX;
|
||||
let bn: i64 = os.getcwd(before.ptr, before.len: u64);
|
||||
if (bn <= 1i64 || bn > before.len: i64) { return nil; };
|
||||
if (os.chdir(path) != 0) { return nil; };
|
||||
let after: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||||
after.len = os.PATH_MAX;
|
||||
let an: i64 = os.getcwd(after.ptr, after.len: u64);
|
||||
let restored: i32 = os.chdir(pathstr(before.ptr));
|
||||
if (restored != 0) {
|
||||
cerr("ww: cannot restore current directory\n");
|
||||
return nil;
|
||||
};
|
||||
if (an <= 1i64 || an > after.len: i64) { return nil; };
|
||||
return arenadupcstr(after.ptr, (an - 1i64): u64);
|
||||
};
|
||||
|
||||
fn envpath(name: str) *u8 = {
|
||||
match (os.getenv(name)) {
|
||||
case let p: str => {
|
||||
@@ -185,7 +222,7 @@ fn toolpath(selfdir: *u8, envvar: str, name: str) *u8 = {
|
||||
// package.ww roots stay on runsingletest, which is the recursion boundary when
|
||||
// wwtest builds its already-aggregated package binaries.
|
||||
fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
||||
targetindex: i32, resolved: *u8, adddot: bool) i32 = {
|
||||
targetindex: i32, resolved: *u8, rootidentity: *u8, adddot: bool) i32 = {
|
||||
let prog: *u8 = joinpathlit(selfdir, "wwtest");
|
||||
match (os.getenv("WW_WWTEST")) {
|
||||
case let p: str => {
|
||||
@@ -194,7 +231,7 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
||||
case void => void;
|
||||
};
|
||||
|
||||
let cap: i32 = argc - start + 6;
|
||||
let cap: i32 = argc - start + 8;
|
||||
let execargv: []*u8 = alloc([], cap: u64)!;
|
||||
execargv.len = cap;
|
||||
let n: i32 = 0;
|
||||
@@ -202,6 +239,10 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
||||
execargv[n] = "package".ptr; n += 1;
|
||||
execargv[n] = "--ww-driver".ptr; n += 1;
|
||||
execargv[n] = selfpath; n += 1;
|
||||
if (rootidentity != nil) {
|
||||
execargv[n] = "--ww-root-identity".ptr; n += 1;
|
||||
execargv[n] = rootidentity; n += 1;
|
||||
};
|
||||
let dotted: bool = false;
|
||||
let i: i32 = start;
|
||||
for (i < argc) {
|
||||
@@ -761,6 +802,7 @@ type seppkg = struct {
|
||||
path: *u8, // compiler/import identity derived from importbase
|
||||
importbase: *u8, // canonical ordinary directory import identity
|
||||
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
|
||||
name: *u8, // validated declared name; directory packages only
|
||||
testpackage: *u8,
|
||||
@@ -801,6 +843,7 @@ type sepgraph = struct {
|
||||
type sepproduct = struct {
|
||||
dir: *u8,
|
||||
out: *u8,
|
||||
identity: *u8,
|
||||
testpackage: *u8,
|
||||
status: *u8,
|
||||
artifact: *u8,
|
||||
@@ -843,6 +886,19 @@ fn sepdiagdiridentities(entry: *u8, a: *u8, b: *u8) void = {
|
||||
cerr(" and "); cerr(pathstr(second)); cerr("\n");
|
||||
};
|
||||
|
||||
fn sepimportbasevalid(base: *u8) bool = {
|
||||
let total: u64 = cstrlen(base);
|
||||
let pos: u64 = 0u64;
|
||||
for (pos < total) {
|
||||
let end: u64 = pos;
|
||||
for (end < total && base[end] != '.': u8) { end += 1u64; };
|
||||
if (!sepimportcomponent(base + pos, end - pos)) { return false; };
|
||||
if (end == total) { return true; };
|
||||
pos = end + 1u64;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bind a provisional directory action to its canonical ordinary import
|
||||
// identity. The compiler path is derived from that base and the semantic
|
||||
// variant; root/product/artifact state never participates.
|
||||
@@ -851,6 +907,10 @@ fn sepbindimportbase(g: *sepgraph, pi: i32, base: *u8) i32 = {
|
||||
cerr("ww: package path is too long (limit 255 bytes)\n");
|
||||
return -1;
|
||||
};
|
||||
if (!reservedimportpath(base) && !sepimportbasevalid(base)) {
|
||||
cerr("ww: invalid package path "); cerr(pathstr(base)); cerr("\n");
|
||||
return -1;
|
||||
};
|
||||
let p: *seppkg = &g.pkg[pi];
|
||||
if (p.importbase != nil) {
|
||||
if (cstreq(p.importbase, base)) { return 0; };
|
||||
@@ -862,8 +922,7 @@ fn sepbindimportbase(g: *sepgraph, pi: i32, base: *u8) i32 = {
|
||||
let i: i32 = 0;
|
||||
for (i < g.n) {
|
||||
if (i != pi && g.pkg[i].isdir != 0 && !g.pkg[i].generatedmain) {
|
||||
let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry),
|
||||
pathstr(p.entry));
|
||||
let samelocation: bool = cstreq(g.pkg[i].canon, p.canon);
|
||||
let supportalias: bool = p.role == SEP_ROLE_TEST_SUPPORT
|
||||
|| g.pkg[i].role == SEP_ROLE_TEST_SUPPORT;
|
||||
if (!supportalias && !samelocation
|
||||
@@ -910,14 +969,33 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
|
||||
cerr("ww: package path is too long (limit 255 bytes)\n");
|
||||
return -1;
|
||||
};
|
||||
let canon: *u8 = nil;
|
||||
if (isdir != 0) {
|
||||
canon = canonicaldir(pathstr(entry));
|
||||
if (canon == nil) {
|
||||
cerr("ww: cannot canonicalize package ");
|
||||
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) {
|
||||
incoming = sepvariantpath(variant, path);
|
||||
};
|
||||
let i: i32 = 0;
|
||||
for (i < g.n) {
|
||||
let samelocation: bool = os.samefile(pathstr(g.pkg[i].entry),
|
||||
pathstr(entry));
|
||||
let samelocation: bool = false;
|
||||
if (isdir != 0 && g.pkg[i].isdir != 0 && canon != nil
|
||||
&& g.pkg[i].canon != nil) {
|
||||
samelocation = cstreq(g.pkg[i].canon, canon);
|
||||
} else {
|
||||
samelocation = os.samefile(pathstr(g.pkg[i].entry),
|
||||
pathstr(entry));
|
||||
};
|
||||
if (isdir != 0 && g.pkg[i].isdir != 0
|
||||
&& !g.pkg[i].generatedmain) {
|
||||
let supportalias: bool = role == SEP_ROLE_TEST_SUPPORT
|
||||
@@ -987,6 +1065,7 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
|
||||
g.pkg[g.n].path = arenadupcstr(path, plen);
|
||||
g.pkg[g.n].importbase = nil;
|
||||
g.pkg[g.n].entry = arenadupcstr(entry, elen);
|
||||
g.pkg[g.n].canon = canon;
|
||||
g.pkg[g.n].artifact = nil;
|
||||
g.pkg[g.n].name = nil;
|
||||
g.pkg[g.n].testpackage = nil;
|
||||
@@ -1025,7 +1104,7 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
|
||||
if (g.pkg[i].isdir != 0 && !g.pkg[i].generatedmain
|
||||
&& g.pkg[i].role != SEP_ROLE_TEST_SUPPORT
|
||||
&& role != SEP_ROLE_TEST_SUPPORT
|
||||
&& os.samefile(pathstr(g.pkg[i].entry), pathstr(entry))
|
||||
&& cstreq(g.pkg[i].canon, canon)
|
||||
&& g.pkg[i].importbase != nil) {
|
||||
inherited = g.pkg[i].importbase;
|
||||
break;
|
||||
@@ -1197,10 +1276,6 @@ fn sepexternalname(pkg: *seppkg, path: *u8, n: u64,
|
||||
&& pkg.testpackage[leafn + 4u64] == 't';
|
||||
};
|
||||
|
||||
fn sepexternalproduction(pkg: *seppkg, path: *u8, n: u64) bool = {
|
||||
return sepexternalname(pkg, path, n, false);
|
||||
};
|
||||
|
||||
fn sepbindadd(bindings: *[]sepbind, kind: u8, name: str,
|
||||
target: *u8) void = {
|
||||
let i: i32 = 0;
|
||||
@@ -1357,17 +1432,26 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
previous = u.usepath;
|
||||
let idp: *u8 = u.usepath.ptr;
|
||||
let idn: u64 = u.usepath.len: u64;
|
||||
if (reservedimport(u.usepath)) {
|
||||
cerrpos(u.file, u.line, u.col);
|
||||
cerr(": error: package path ");
|
||||
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 = sepexternalproduction(
|
||||
&g.pkg[pi], idp, idn);
|
||||
let externalproduction: bool = false;
|
||||
let ipath: *u8 = nil;
|
||||
if (externalproduction) {
|
||||
ipath = g.pkg[pi].entry;
|
||||
} else {
|
||||
if (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||
&& g.pkg[pi].importbase != nil
|
||||
&& cstrlen(g.pkg[pi].importbase) == idn
|
||||
&& bytecmp(g.pkg[pi].importbase, idn, idp, idn) == 0) {
|
||||
ipath = g.pkg[pi].canon;
|
||||
};
|
||||
if (ipath == nil) {
|
||||
ipath = locateimport(searchpath, idp, idn);
|
||||
};
|
||||
if (ipath != nil) {
|
||||
@@ -1382,7 +1466,7 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
||||
cerr(": error: self-import: package '");
|
||||
if (g.pkg[pi].path[0u64] != 0u8) {
|
||||
cerr(pathstr(g.pkg[pi].path));
|
||||
} else { cerr(pathstr(g.pkg[pi].name)); };
|
||||
} else { cerr(pathstr(g.pkg[pi].canon)); };
|
||||
cerr("' cannot import itself\n");
|
||||
return -1;
|
||||
};
|
||||
@@ -1447,8 +1531,11 @@ fn sepdepcmp(g: *sepgraph, a: i32, b: i32) i32 = {
|
||||
if (g.pkg[a].variant > g.pkg[b].variant) { return 1; };
|
||||
if (g.pkg[a].role < g.pkg[b].role) { return -1; };
|
||||
if (g.pkg[a].role > g.pkg[b].role) { return 1; };
|
||||
return strings.compare(pathstr(g.pkg[a].entry),
|
||||
pathstr(g.pkg[b].entry)): i32;
|
||||
if (g.pkg[a].canon != nil && g.pkg[b].canon != nil) {
|
||||
return strings.compare(pathstr(g.pkg[a].canon),
|
||||
pathstr(g.pkg[b].canon)): i32;
|
||||
};
|
||||
return strings.compare(pathstr(g.pkg[a].entry), pathstr(g.pkg[b].entry)): i32;
|
||||
};
|
||||
|
||||
fn generatedmainkind(variant: i32) str = {
|
||||
@@ -1516,6 +1603,9 @@ fn sepaddgeneratedmain(g: *sepgraph, product: *sepproduct, ordinal: i32,
|
||||
p.path = mainpath;
|
||||
p.importbase = nil;
|
||||
p.entry = g.pkg[variant].entry;
|
||||
let canonkind: *u8 = appendlit(g.pkg[variant].canon, "#");
|
||||
canonkind = appendlit(canonkind, kind);
|
||||
p.canon = appendlit(canonkind, "-test-main");
|
||||
let variantartifact: *u8 = g.pkg[variant].artifact;
|
||||
if (variantartifact == nil) { variantartifact = g.pkg[variant].path; };
|
||||
p.artifact = appendlit(variantartifact, "-main");
|
||||
@@ -1649,7 +1739,7 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
||||
cerr("ww: package ");
|
||||
if (g.pkg[pi].path[0u64] != 0u8) {
|
||||
cerr(pathstr(g.pkg[pi].path));
|
||||
} else { cerr(pathstr(g.pkg[pi].name)); };
|
||||
} else { cerr(pathstr(g.pkg[pi].canon)); };
|
||||
cerr(" resolves imports differently in ");
|
||||
cerr(pathstr(g.context[g.pkg[pi].emitcontext].root));
|
||||
cerr(" and "); cerr(pathstr(g.context[context].root)); cerr("\n");
|
||||
@@ -1684,12 +1774,186 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Finalize provisional directory identities after source discovery and before
|
||||
// generated-main construction or compilation. Source-import bindings win;
|
||||
// otherwise a literal manifest-free root falls back to its validated package
|
||||
// name (external p_test roots bind the ordinary base p).
|
||||
fn sepimportcomponent(s: *u8, n: u64) bool = {
|
||||
if (n == 0u64) { return false; };
|
||||
let first: u8 = s[0u64];
|
||||
if (!((first >= 'a': u8 && first <= 'z': u8)
|
||||
|| (first >= 'A': u8 && first <= 'Z': u8)
|
||||
|| first == '_': u8)) { return false; };
|
||||
let i: u64 = 1u64;
|
||||
for (i < n) {
|
||||
let c: u8 = s[i];
|
||||
if (!((c >= 'a': u8 && c <= 'z': u8)
|
||||
|| (c >= 'A': u8 && c <= 'Z': u8)
|
||||
|| (c >= '0': u8 && c <= '9': u8)
|
||||
|| c == '_': u8)) { return false; };
|
||||
i += 1u64;
|
||||
};
|
||||
return syntax.kwlookup(s, n: i32) == syntax.tkind.TK_NONE;
|
||||
};
|
||||
|
||||
fn sepimportpathfromrelative(rel: *u8, out: *u8, outsz: u64) i32 = {
|
||||
let off: u64 = 0u64;
|
||||
let p: u64 = 0u64;
|
||||
let total: u64 = cstrlen(rel);
|
||||
for (p < total) {
|
||||
let q: u64 = p;
|
||||
for (q < total && rel[q] != '/': u8) { q += 1u64; };
|
||||
let n: u64 = q - p;
|
||||
if (!sepimportcomponent(rel + p, n)) { return 0; };
|
||||
let more: bool = q < total;
|
||||
let separator: u64 = 0u64;
|
||||
if (more) { separator = 1u64; };
|
||||
if (off + n + separator + 1u64 > outsz) {
|
||||
return -1;
|
||||
};
|
||||
bytecpy(out + off, rel + p, n);
|
||||
off += n;
|
||||
if (more) { out[off] = '.': u8; off += 1u64; };
|
||||
p = q + 1u64;
|
||||
};
|
||||
out[off] = 0u8;
|
||||
if (off == 0u64) { return 0; };
|
||||
return 1;
|
||||
};
|
||||
|
||||
// A reverse candidate is authoritative only when the ordinary ordered lookup
|
||||
// selects this exact canonical directory. Later or nested roots therefore
|
||||
// cannot manufacture an alias hidden by an earlier source root.
|
||||
fn sepreverseimportbase(g: *sepgraph, p: *seppkg, context: i32,
|
||||
out: *u8, outsz: u64) i32 = {
|
||||
let searchpath: *u8 = g.context[context].searchpath;
|
||||
let total: u64 = cstrlen(searchpath);
|
||||
let pos: u64 = 0u64;
|
||||
for (pos < total) {
|
||||
let end: u64 = pos;
|
||||
for (end < total && searchpath[end] != ':': u8) { end += 1u64; };
|
||||
if (end > pos) {
|
||||
let root: str;
|
||||
root.ptr = searchpath + pos;
|
||||
root.len = (end - pos): i32;
|
||||
let canonroot: *u8 = canonicaldir(root);
|
||||
if (canonroot != nil) {
|
||||
let rn: u64 = cstrlen(canonroot);
|
||||
let dn: u64 = cstrlen(p.canon);
|
||||
let rel: *u8 = nil;
|
||||
if (rn == 1u64 && canonroot[0u64] == '/': u8
|
||||
&& dn > 1u64 && p.canon[0u64] == '/': u8) {
|
||||
rel = p.canon + 1u64;
|
||||
} else { if (dn > rn + 1u64
|
||||
&& bytecmp(p.canon, rn, canonroot, rn) == 0
|
||||
&& p.canon[rn] == '/': u8) {
|
||||
rel = p.canon + rn + 1u64;
|
||||
}; };
|
||||
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 && reservedimportpath(out)) {
|
||||
converted = 0;
|
||||
};
|
||||
if (converted > 0) {
|
||||
let selected: *u8 = locateimport(searchpath, rel,
|
||||
cstrlen(rel));
|
||||
if (selected != nil) {
|
||||
let selectedcanon: *u8 = canonicaldir(pathstr(selected));
|
||||
if (selectedcanon != nil
|
||||
&& cstreq(selectedcanon, p.canon)) {
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
pos = end + 1u64;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn sepordinarydeclaredname(p: *seppkg) *u8 = {
|
||||
if (p.name == nil || p.name[0u64] == 0u8) { return nil; };
|
||||
let n: u64 = cstrlen(p.name);
|
||||
if (p.variant == SEP_VARIANT_EXTERNAL) {
|
||||
if (n <= 5u64 || !cstrendswithlit(p.name, "_test")) {
|
||||
cerr("ww: package-test selector does not name an external package\n");
|
||||
return nil;
|
||||
};
|
||||
n -= 5u64;
|
||||
};
|
||||
return arenadupcstr(p.name, n);
|
||||
};
|
||||
|
||||
// The reserved local namespace is reversible, so filesystem identity never
|
||||
// depends on a hash, request order, output name, or another selected package.
|
||||
fn seplocalimportbase(p: *seppkg) *u8 = {
|
||||
let leaf: *u8 = sepordinarydeclaredname(p);
|
||||
if (leaf == nil) { return nil; };
|
||||
let need: u64 = SEP_LOCAL_IMPORT_PREFIX.len: u64 + 2u64
|
||||
+ cstrlen(p.canon) * 4u64 + 1u64 + cstrlen(leaf) + 1u64;
|
||||
let out: []u8 = alloc([], need)!;
|
||||
out.len = need: i32;
|
||||
let off: u64 = strinto(out.ptr, 0u64, SEP_LOCAL_IMPORT_PREFIX);
|
||||
off = byteinto(out.ptr, off, '.': u8);
|
||||
off = byteinto(out.ptr, off, 'p': u8);
|
||||
let hex: str = "0123456789abcdef";
|
||||
let i: u64 = 0u64;
|
||||
for (p.canon[i] != 0u8) {
|
||||
let c: u8 = p.canon[i];
|
||||
if ((c >= 'a': u8 && c <= 'z': u8)
|
||||
|| (c >= 'A': u8 && c <= 'Z': u8)
|
||||
|| (c >= '0': u8 && c <= '9': u8)) {
|
||||
off = byteinto(out.ptr, off, c);
|
||||
} else { if (c == '_': u8 || c == '/': u8) {
|
||||
off = byteinto(out.ptr, off, '_': u8);
|
||||
let escaped: u8 = 's': u8;
|
||||
if (c == '_': u8) { escaped = 'u': u8; };
|
||||
off = byteinto(out.ptr, off, escaped);
|
||||
} else {
|
||||
off = byteinto(out.ptr, off, '_': u8);
|
||||
off = byteinto(out.ptr, off, 'x': u8);
|
||||
let high: i32 = (c / 16u8): i32;
|
||||
let low: i32 = (c % 16u8): i32;
|
||||
off = byteinto(out.ptr, off, hex[high]);
|
||||
off = byteinto(out.ptr, off, hex[low]);
|
||||
}; };
|
||||
i += 1u64;
|
||||
};
|
||||
off = byteinto(out.ptr, off, '.': u8);
|
||||
off = cstrinto(out.ptr, off, leaf);
|
||||
cstrseal(out.ptr, off);
|
||||
return out.ptr;
|
||||
};
|
||||
|
||||
// Finalization verifies every reached context before generated-main creation.
|
||||
// Source bindings and explicit lookup identities remain authoritative; a
|
||||
// literal root either round-trips through an active root or receives the
|
||||
// reserved reversible local identity.
|
||||
fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
|
||||
let pi: i32 = 0;
|
||||
for (pi < g.n) {
|
||||
let p: *seppkg = &g.pkg[pi];
|
||||
if (p.isdir != 0 && !p.generatedmain && !p.failed && p.loaded
|
||||
&& p.role != SEP_ROLE_TEST_SUPPORT && p.importbase == nil) {
|
||||
let ci: i32 = 0;
|
||||
for (ci < g.ncontext) {
|
||||
if (p.contextstate[ci] == 2u8) {
|
||||
let candidate: [256]u8;
|
||||
let found: i32 = sepreverseimportbase(g, p, ci,
|
||||
&candidate[0], 256u64);
|
||||
if (found < 0) { return -1; };
|
||||
if (found > 0 && sepbindimportbase(g, pi,
|
||||
&candidate[0]) < 0) { return -1; };
|
||||
};
|
||||
ci += 1;
|
||||
};
|
||||
};
|
||||
pi += 1;
|
||||
};
|
||||
pi = 0;
|
||||
for (pi < g.n) {
|
||||
let p: *seppkg = &g.pkg[pi];
|
||||
if (p.isdir != 0 && !p.generatedmain && !p.failed && p.loaded
|
||||
@@ -1701,7 +1965,7 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
|
||||
&& !g.pkg[i].generatedmain
|
||||
&& g.pkg[i].role != SEP_ROLE_TEST_SUPPORT
|
||||
&& p.role != SEP_ROLE_TEST_SUPPORT
|
||||
&& os.samefile(pathstr(g.pkg[i].entry), pathstr(p.entry))
|
||||
&& cstreq(g.pkg[i].canon, p.canon)
|
||||
&& g.pkg[i].importbase != nil) {
|
||||
base = g.pkg[i].importbase;
|
||||
break;
|
||||
@@ -1709,26 +1973,11 @@ fn sepfinalizedirectoryidentities(g: *sepgraph) i32 = {
|
||||
i += 1;
|
||||
};
|
||||
if (base == nil) {
|
||||
if (p.name == nil || p.name[0u64] == 0u8) {
|
||||
cerr("ww: package directory "); cerr(pathstr(p.entry));
|
||||
cerr(" has no canonical import identity\n");
|
||||
base = seplocalimportbase(p);
|
||||
if (base == nil || cstrlen(base) >= 256u64) {
|
||||
cerr("ww: local package identity is too long (limit 255 bytes)\n");
|
||||
return -1;
|
||||
};
|
||||
if (p.variant == SEP_VARIANT_EXTERNAL) {
|
||||
let n: u64 = cstrlen(p.name);
|
||||
if (n <= 5u64 || !cstrendswithlit(p.name, "_test")) {
|
||||
cerr("ww: package-test selector does not name an external package\n");
|
||||
return -1;
|
||||
};
|
||||
let fallback: []u8 = alloc([], n - 5u64 + 1u64)!;
|
||||
fallback.len = (n - 5u64 + 1u64): i32;
|
||||
let k: u64 = 0u64;
|
||||
for (k < n - 5u64) { fallback[k] = p.name[k]; k += 1u64; };
|
||||
fallback[n - 5u64] = 0u8;
|
||||
base = fallback.ptr;
|
||||
} else {
|
||||
base = p.name;
|
||||
};
|
||||
};
|
||||
if (sepbindimportbase(g, pi, base) < 0) { return -1; };
|
||||
};
|
||||
@@ -2133,14 +2382,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 8 mode test asm 1\n";
|
||||
return "ww workdir fmt 9 mode test asm 1\n";
|
||||
};
|
||||
return "ww workdir fmt 8 mode test asm 0\n";
|
||||
return "ww workdir fmt 9 mode test asm 0\n";
|
||||
};
|
||||
if (emitasm != 0) {
|
||||
return "ww workdir fmt 7 mode build asm 1\n";
|
||||
return "ww workdir fmt 8 mode build asm 1\n";
|
||||
};
|
||||
return "ww workdir fmt 7 mode build asm 0\n";
|
||||
return "ww workdir fmt 8 mode build asm 0\n";
|
||||
};
|
||||
|
||||
fn stampmatches(path: *u8, want: str) bool = {
|
||||
@@ -2244,8 +2493,8 @@ fn cerrpath(head: str, path: *u8, tail: str) void = {
|
||||
};
|
||||
|
||||
fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
rootidentity: *u8, out: *u8,
|
||||
objstem: *u8, incs: *u8, lf: *lflags, packageonly: i32, istest: i32,
|
||||
out: *u8, objstem: *u8, incs: *u8, lf: *lflags,
|
||||
packageonly: i32, istest: i32,
|
||||
products: *sepproduct, nproducts: i32, emitasm: i32,
|
||||
workdir: *u8, scratchout: **u8,
|
||||
graphout: **sepgraph) i32 = {
|
||||
@@ -2418,8 +2667,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
identityfailed = false,
|
||||
})!;
|
||||
if (graphout != nil) { *graphout = g; };
|
||||
let rootpath: *u8 = "\0".ptr;
|
||||
if (packageonly != 0 && rootidentity != nil) { rootpath = rootidentity; };
|
||||
let supportfor: []i32 = alloc([], nproducts: u64)!;
|
||||
supportfor.len = nproducts;
|
||||
let producti: i32 = 0;
|
||||
@@ -2439,6 +2686,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
if (products[producti].variant == SEP_VARIANT_PRODUCTION) {
|
||||
selector = nil;
|
||||
};
|
||||
let rootpath: *u8 = "\0".ptr;
|
||||
if (products[producti].identity != nil) {
|
||||
rootpath = products[producti].identity;
|
||||
};
|
||||
products[producti].root = sepfindoraddvariant(g, rootpath, entry,
|
||||
entryisdir, products[producti].variant,
|
||||
selector,
|
||||
@@ -3044,6 +3295,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let product: sepproduct;
|
||||
product.dir = src;
|
||||
product.out = out;
|
||||
product.identity = rootidentity;
|
||||
product.testpackage = testpackage;
|
||||
product.status = nil;
|
||||
product.artifact = nil;
|
||||
@@ -3053,8 +3305,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
product.variant = rootvariant;
|
||||
product.root = -1;
|
||||
product.variantroot = -1;
|
||||
let r: i32 = buildonesepimpl(selfdir, src, entryisdir, rootidentity,
|
||||
out, objstem,
|
||||
let r: i32 = buildonesepimpl(selfdir, src, entryisdir, out, objstem,
|
||||
incs, lf, packageonly, istest, &product, 1,
|
||||
emitasm, workdir, &scratch, &g);
|
||||
sepgraphfree(g);
|
||||
@@ -3084,14 +3335,19 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
|
||||
// Build every selected directory/variant root inside one command-owned package
|
||||
// universe. The first output owns the shared cold sepwork tree.
|
||||
fn buildpackagetests(selfdir: *u8, src: *u8, incs: *u8, workdir: *u8,
|
||||
products: *sepproduct, nproducts: i32) i32 = {
|
||||
fn buildpackagetests(selfdir: *u8, src: *u8, rootidentity: *u8,
|
||||
incs: *u8, workdir: *u8, products: *sepproduct, nproducts: i32) i32 = {
|
||||
let scratch: *u8 = nil;
|
||||
let g: *sepgraph = nil;
|
||||
let lf: lflags;
|
||||
lf.libdirs = nil; lf.nlibdirs = 0;
|
||||
lf.libs = nil; lf.nlibs = 0;
|
||||
let r: i32 = buildonesepimpl(selfdir, src, 1, nil,
|
||||
let i: i32 = 0;
|
||||
for (i < nproducts) {
|
||||
products[i].identity = rootidentity;
|
||||
i += 1;
|
||||
};
|
||||
let r: i32 = buildonesepimpl(selfdir, src, 1,
|
||||
products[0].out, products[0].out, incs, &lf, 0, 1,
|
||||
products, nproducts, 0, workdir, &scratch, &g);
|
||||
sepgraphfree(g);
|
||||
@@ -3182,6 +3438,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||
*isdir = foundisdir;
|
||||
return arenadupcstr(name, nlen);
|
||||
};
|
||||
if (reservedimportpath(name)) { return nil; };
|
||||
|
||||
let search: *u8 = buildsearchpath(selfdir, incs);
|
||||
return locatemodule(search, name, nlen, isdir);
|
||||
@@ -3394,7 +3651,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.libs = libs.ptr;
|
||||
lf.nlibs = nlibs;
|
||||
let rootidentity: *u8 = nil;
|
||||
if (packageonly != 0 && !requestedliteral) { rootidentity = src; };
|
||||
if (!requestedliteral && isdir != 0) { rootidentity = src; };
|
||||
return buildonesep(selfdir, resolved, isdir, rootidentity,
|
||||
out, objstem, incs.ptr, &lf,
|
||||
packageonly, 0i32, SEP_VARIANT_PRODUCTION, nil,
|
||||
@@ -3541,6 +3798,12 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let dot: [2]u8 = ['.': u8, 0u8];
|
||||
src = &dot[0];
|
||||
};
|
||||
let requestedliteral: bool = false;
|
||||
let requestedstat: os.filestat;
|
||||
match (os.stat(&requestedstat, pathstr(src))) {
|
||||
case void => requestedliteral = true;
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||
if (resolved == nil) {
|
||||
@@ -3562,7 +3825,10 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.libs = libs.ptr;
|
||||
lf.nlibs = nlibs;
|
||||
// The freshly acquired directory owns both main and main.sepwork.
|
||||
if (buildonesep(selfdir, resolved, isdir, nil, outp, outp, incs.ptr, &lf,
|
||||
let rootidentity: *u8 = nil;
|
||||
if (!requestedliteral && isdir != 0) { rootidentity = src; };
|
||||
if (buildonesep(selfdir, resolved, isdir, rootidentity,
|
||||
outp, outp, incs.ptr, &lf,
|
||||
0i32, 0i32, SEP_VARIANT_PRODUCTION, nil, 0i32, 0i32, nil) != 0) {
|
||||
let cleanrc: i32 = os.remove(pathstr(outp));
|
||||
if (cleanrc != 0 && cleanrc != -2i32) {
|
||||
@@ -3740,6 +4006,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let emitasm: i32 = 0;
|
||||
let outstem: *u8 = nil;
|
||||
let workdir: *u8 = nil;
|
||||
let requestidentity: *u8 = nil;
|
||||
let products: []sepproduct = alloc([], SEP_MAXPRODUCT: u64)!;
|
||||
let packageopts: bool = false;
|
||||
let afterdash: bool = false;
|
||||
@@ -3751,6 +4018,17 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
if (cstreqlit(p, "--")) {
|
||||
packageopts = true; afterdash = true; i += 1; continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-root-identity")) {
|
||||
if (i + 1 >= argc || requestidentity != nil
|
||||
|| argv[i + 1][0u64] == 0u8
|
||||
|| reservedimportpath(argv[i + 1])) {
|
||||
cerr("ww test: invalid --ww-root-identity\n");
|
||||
return 2;
|
||||
};
|
||||
requestidentity = argv[i + 1];
|
||||
i += 2;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-package-test")) {
|
||||
if (i + 5 >= argc || products.len >= SEP_MAXPRODUCT) {
|
||||
cerr("ww test: --ww-package-test needs kind, package, directory, output, and status\n");
|
||||
@@ -3784,6 +4062,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let product: sepproduct;
|
||||
product.dir = dir;
|
||||
product.out = output;
|
||||
product.identity = nil;
|
||||
product.testpackage = name;
|
||||
product.status = status;
|
||||
product.artifact = nil;
|
||||
@@ -3943,7 +4222,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
// -w forwards: the coordinator keys one persistent driver workdir
|
||||
// for the complete selected test request.
|
||||
return execpackagetests(selfdir, argv, argc, start,
|
||||
targetindex, nil, false);
|
||||
targetindex, nil, nil, false);
|
||||
};
|
||||
|
||||
let resolved: *u8 = target;
|
||||
@@ -3958,6 +4237,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
let requestedliteral: bool = found;
|
||||
if (!found) {
|
||||
resolved = resolvemodule(selfdir, target, incs.ptr, &isdir);
|
||||
if (resolved == nil) {
|
||||
@@ -3966,6 +4246,10 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
let rootidentity: *u8 = requestidentity;
|
||||
if (!requestedliteral && rootidentity == nil && isdir != 0) {
|
||||
rootidentity = target;
|
||||
};
|
||||
if (isdir == 0) {
|
||||
if (products.len != 0) {
|
||||
cerr("ww test: package-test variant needs one directory\n");
|
||||
@@ -3995,13 +4279,14 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
cerr("ww test: package-test products need -c\n");
|
||||
return 2;
|
||||
};
|
||||
return buildpackagetests(selfdir, resolved, incs.ptr, workdir,
|
||||
return buildpackagetests(selfdir, resolved, rootidentity,
|
||||
incs.ptr, workdir,
|
||||
products.ptr, products.len);
|
||||
};
|
||||
let replacement: *u8 = nil;
|
||||
if (resolved != target) { replacement = resolved; };
|
||||
return execpackagetests(selfdir, argv, argc, start, targetindex,
|
||||
replacement, targetindex < 0);
|
||||
replacement, rootidentity, targetindex < 0);
|
||||
};
|
||||
|
||||
export fn main(argc: i32, argv: **u8) i32 = {
|
||||
|
||||
Reference in New Issue
Block a user