cmd+selfhost+lib+test: directory-as-module enumeration in driver (#22)
Replace the cmd/ww + selfhost driver's file-walk import resolver with true directory enumeration. `import encoding.utf8;` now finds the lib/encoding/utf8/ directory and concatenates every *.ww file in it (excluding *test.ww and the driver's *.combined.ww artifacts) in byte-wise sorted order, instead of just finding the single lib/encoding/utf8/utf8.ww file. Mirrors Hare's hare/module/srcs.ha:183 _findsrcs minus tag handling. Lookup order in both stages: (1) <dir>/<dot-as-slash>/ as directory → enumerate. (2) <dir>/<dot-as-slash>.ww as file. The legacy <dir>/<name>/<name>.ww shape from #18's retained divergence is dropped per rule-9 Hare-fidelity — Hare has no foo/foo.ha fallback; a module IS the directory. Symmetric across cstage (cmd/ww/main.c via opendir+qsort+stat) and wwstage (selfhost/cmd/ww/main.ww via existing lib/os.getdents64 + os.stat — no new lib/os surface needed; the rundirtests() walker in main.ww from #18 was the model). Bootstrap ww2.s==ww3.s==ww4.s byte-identical post-change. Bundling justification (rule 11): strict-same-package validation is bundled because the failure mode is dir-enum's own (a non-dir-enum compilation unit cannot trigger mismatch across enumerated files). The natural enforcement site is the driver — the parser can't distinguish dir-enum concat from file-walk concat. Both stages peek each file's first `package <name>;` line in expand_dir / expanddir and exit(1) on mismatch with a precise error pointing at the offending file. Hare's hare/module/srcs.ha:131 has the same constraint via its README gate. Other half of #23 (strict missing-package error tightening — 63 inline-source test wrappers blocker) stays deferred per its filing. Parser side (cmd/wcc/parse.c parseuse + lib/ww/parse/decl.ww parseuse): n->str now carries only the LEAF identifier from a dotted import. With the driver translating the full dotted path to a directory walk, the checker only needs the package bareword (last component) for the N_USE → decl disambiguation walk in check.c's src_imports / decl_mod. Mirrors Hare's `use encoding::utf8;` → `utf8::name` semantics (ref/hare/hare/ast/import.ha:7). Migration: lib/ww/sym.ww drops `import typ; import ast;`; lib/ww/parse/parse.ww drops `import expr; import stmt; import decl;`; lib/ww/lex/lex.ww drops `import tok;` — all sibling imports auto-resolve via the new dir-enum when callers import the package directory. lib/strings/, lib/encoding/utf8/utf8test.ww migrate `import utf8;` → `import encoding.utf8;`. Makefile drops -I lib/encoding/utf8 stopgap from wwdump_ww + w6c_ww. Seven test wrappers (700_e2e, 966_strings_run, 970_fmt_run, 971_log_run, 972_fnmatch_run, 982_getopt_run, 990_selfhost) and 995_self_rebuild drop the -I lib/encoding/utf8 runtime stopgap. Tests: new 737_direnum C wrapper + test/wcc/data/direnum/ fixtures pin (a) cross-pkg multi-file dir-enum build at runtime (both stages must succeed) and (b) strict-same-package mismatch error (both stages must surface "differs from" + exit non-zero). 738_module_decl gains row 6 pinning the n_use->str leaf-only storage post-parser change. Retained workaround at selfhost/cmd/ww/main.ww expanddir loop: `names[i][k]` nested-deref-then-index split into `let nm: *u8 = names[i]; nm[k]` because wwstage cgen miscompiles the chained form (treats inner u8 element as 8B sizeof *u8 instead of 1B sizeof u8: extra MOVQ $8 + IMULQ on the inner index, MOVQ instead of MOVZBQ load). Inline rule-8 WHY comment cites task #24 (wwstage cgen chained-index inner element size on **T). Two-step form routes through the bare-pointer index path which both stages handle byte-identically. Class A wwstage cgen UNDER (chained-index inner element size on **T) surfaced first time the codebase exercises the **T[i][k] shape via enumeratedir() — corpus-coverage-blind landmine pattern, same family as the trio (#27/#28/#31) from STATUS-5. 112/112 ok. ww2 == ww3 == ww4 byte-id holds.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1089,18 +1089,31 @@ fn visitadd(c: *expctx, path: str) void = {
|
||||
c.visit = n;
|
||||
};
|
||||
|
||||
// Try <dir>/<name>.ww then <dir>/<name>/<name>.ww. Returns NUL-terminated
|
||||
// arena-resident path if found, else nil.
|
||||
//
|
||||
// Retained divergence from brief: directory-as-module enumeration is
|
||||
// NOT implemented here. The user's "module IS directory" mental model
|
||||
// is partially honored via the `package` keyword + file-walk + sibling
|
||||
// `import` chain. True dir enumeration (lib/foo/*.ww concatenated
|
||||
// atomically, no sibling-import boilerplate) is deferred to task #22
|
||||
// and needs a lib/os opendir/readdir wrapper around getdents64 first.
|
||||
// Rule 7 + rule 8 documentation.
|
||||
fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
// candidate 1: <dir>/<name>.ww
|
||||
// Translate dots in an `import` name to slashes for path lookup.
|
||||
// `encoding.utf8` → `encoding/utf8`. Mirrors Hare's hare(1)
|
||||
// use-path → fs-path mapping
|
||||
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
||||
// path::push per ident part).
|
||||
fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
||||
let buf: *u8 = amalloc(a, namelen + 1u64): *u8;
|
||||
let i: u64 = 0u64;
|
||||
for (i < namelen) {
|
||||
if (name[i] == 46u8) { buf[i] = 47u8; } // '.' -> '/'
|
||||
else { buf[i] = name[i]; };
|
||||
i += 1u64;
|
||||
};
|
||||
buf[namelen] = 0u8;
|
||||
return buf;
|
||||
};
|
||||
|
||||
// Try <dir>/<path>/ as a directory, then <dir>/<path>.ww as a file.
|
||||
// Sets *isdir on hit. Symmetric with cstage locate_import_in for
|
||||
// byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// form was dropped in task #22 — directory-as-module enumeration
|
||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||
// `foo/foo.ha` fallback; a module IS the directory).
|
||||
fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||
let buf: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
let off: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
@@ -1108,15 +1121,24 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
off += dirlen;
|
||||
buf[off] = 47u8; off += 1u64; // '/'
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf[off] = 46u8; off += 1u64; // '.'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
for (i < pflen) { buf[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf[off] = 0u8;
|
||||
if (os.access(pathstr(buf), 0i32) == 0) { return buf; };
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf));
|
||||
let isdirhit: bool = false;
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { isdirhit = true; };
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (isdirhit) {
|
||||
*isdir = 1;
|
||||
return buf;
|
||||
};
|
||||
|
||||
// candidate 2: <dir>/<name>/<name>.ww
|
||||
let buf2: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
off = 0u64;
|
||||
i = 0u64;
|
||||
@@ -1124,22 +1146,25 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
off += dirlen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf2[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf2[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf2[off] = 46u8; off += 1u64;
|
||||
buf2[off] = 119u8; off += 1u64;
|
||||
buf2[off] = 119u8; off += 1u64;
|
||||
for (i < pflen) { buf2[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf2[off] = 46u8; off += 1u64; // '.'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 0u8;
|
||||
if (os.access(pathstr(buf2), 0i32) == 0) { return buf2; };
|
||||
if (os.access(pathstr(buf2), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return buf2;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Walk a colon-separated dirlist, return first hit or nil.
|
||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||
// *isdir on hit.
|
||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
||||
isdir: *i32) *u8 = {
|
||||
let pathform: *u8 = importpathform(a, name, namelen);
|
||||
let pflen: u64 = cstrlen(pathform);
|
||||
let total: u64 = cstrlen(dirs);
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
@@ -1150,7 +1175,8 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(a, dirs + p, seglen, name, namelen);
|
||||
let hit: *u8 = locatein(a, dirs + p, seglen,
|
||||
pathform, pflen, isdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
@@ -1158,6 +1184,123 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Filter for dir enumeration: keep `*.ww` minus `*test.ww` and the
|
||||
// `*.combined.ww` driver-generated concat artifacts (the previous
|
||||
// build leaves them in the source tree; they parse-error when
|
||||
// re-included). Returns true to keep.
|
||||
fn dirfilekeep(name: *u8, nlen: u64) bool = {
|
||||
if (nlen <= 3u64) { return false; };
|
||||
if (name[nlen - 3u64] != 46u8) { return false; }; // '.'
|
||||
if (name[nlen - 2u64] != 119u8) { return false; }; // 'w'
|
||||
if (name[nlen - 1u64] != 119u8) { return false; }; // 'w'
|
||||
if (nlen >= 7u64) {
|
||||
if (name[nlen - 7u64] == 116u8) { // 't'
|
||||
if (name[nlen - 6u64] == 101u8) { // 'e'
|
||||
if (name[nlen - 5u64] == 115u8) { // 's'
|
||||
if (name[nlen - 4u64] == 116u8) { // 't'
|
||||
return false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (nlen >= 12u64) {
|
||||
// ".combined.ww"
|
||||
if (name[nlen - 12u64] == 46u8) { // '.'
|
||||
if (name[nlen - 11u64] == 99u8) { // 'c'
|
||||
if (name[nlen - 10u64] == 111u8) { // 'o'
|
||||
if (name[nlen - 9u64] == 109u8) { // 'm'
|
||||
if (name[nlen - 8u64] == 98u8) { // 'b'
|
||||
return false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Byte-wise memcmp returning < 0, 0, > 0. Rule-10 byte-id requires
|
||||
// cstage and wwstage sort the same way; memcmp is the
|
||||
// locale-independent total order (mirrors ref/hare/sort/cmp/cmp.ha
|
||||
// strs).
|
||||
fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
||||
let n: u64 = alen;
|
||||
if (blen < n) { n = blen; };
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
let av: i32 = (a[i]): i32;
|
||||
let bv: i32 = (b[i]): i32;
|
||||
if (av < bv) { return -1; };
|
||||
if (av > bv) { return 1; };
|
||||
i += 1u64;
|
||||
};
|
||||
if (alen < blen) { return -1; };
|
||||
if (alen > blen) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
||||
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
||||
// name a NUL-terminated arena copy.
|
||||
fn enumeratedir(a: *arena, dirpath: *u8) (**u8, i32) = {
|
||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil: **u8, 0; };
|
||||
let maxnames: i32 = 256;
|
||||
let names: **u8 = amalloc(a, (maxnames: u64) * 8u64): **u8;
|
||||
let nlens: *u64 = amalloc(a, (maxnames: u64) * 8u64): *u64;
|
||||
let n: i32 = 0;
|
||||
let buf: *u8 = os.alloc(8192u64): *u8;
|
||||
let r: i64 = os.getdents64(fd, buf, 8192u64);
|
||||
for (r > 0i64) {
|
||||
let off: u64 = 0u64;
|
||||
let ru: u64 = r: u64;
|
||||
for (off < ru) {
|
||||
let blo: u64 = (buf[off + 16u64]): u64;
|
||||
let bhi: u64 = (buf[off + 17u64]): u64;
|
||||
let reclen: u64 = blo + (bhi * 256u64);
|
||||
let nm: *u8 = buf + off + 19u64;
|
||||
let nl: u64 = cstrlen(nm);
|
||||
if (dirfilekeep(nm, nl)) {
|
||||
if (n < maxnames) {
|
||||
let cp: *u8 = amalloc(a, nl + 1u64): *u8;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
};
|
||||
};
|
||||
off += reclen;
|
||||
};
|
||||
r = os.getdents64(fd, buf, 8192u64);
|
||||
};
|
||||
os.close(fd);
|
||||
|
||||
// Insertion sort, byte-wise. n is small (≤16 in practice).
|
||||
let i: i32 = 1;
|
||||
for (i < n) {
|
||||
let j: i32 = i;
|
||||
for (j > 0) {
|
||||
if (bytecmp(names[j - 1], nlens[j - 1],
|
||||
names[j], nlens[j]) <= 0) { j = 0; }
|
||||
else {
|
||||
let t: *u8 = names[j];
|
||||
names[j] = names[j - 1];
|
||||
names[j - 1] = t;
|
||||
let tl: u64 = nlens[j];
|
||||
nlens[j] = nlens[j - 1];
|
||||
nlens[j - 1] = tl;
|
||||
j -= 1;
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return names, n;
|
||||
};
|
||||
|
||||
// ---- file slurp -------------------------------------------------------
|
||||
|
||||
fn slurp(pathcs: *u8) (*u8, u64) = {
|
||||
@@ -1227,9 +1370,9 @@ fn scanuse(src: *u8, len: u64) (*u8, u64) = {
|
||||
};
|
||||
|
||||
// expand — emit one file's bytes verbatim into the combined stream,
|
||||
// after recursive-expanding its top-of-file `use X;` imports. Each
|
||||
// source declares its own `module <name>;` (parser stamps decls);
|
||||
// the driver no longer injects a `// MODULE:` marker.
|
||||
// after recursive-expanding its top-of-file `import X;` imports.
|
||||
// Each source declares its own `package <name>;` (parser stamps
|
||||
// decls).
|
||||
fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
let plen: u64 = cstrlen(pathcs);
|
||||
let pathstr: str = astrndup(c.a, pathcs, plen);
|
||||
@@ -1244,7 +1387,7 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// Pass 1: scan top-of-file `use X;` lines, recursively expand.
|
||||
// Pass 1: scan top-of-file `import X;` lines, recursively expand.
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
@@ -1256,9 +1399,12 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn);
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn,
|
||||
&isdir);
|
||||
if (ipath != nil) {
|
||||
expand(c, ipath);
|
||||
if (isdir != 0) { expanddir(c, ipath); }
|
||||
else { expand(c, ipath); };
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
@@ -1268,6 +1414,141 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
os.writeall(c.out, "\n".ptr, 1u64);
|
||||
};
|
||||
|
||||
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
||||
// starts with `package <name>;` return the package name as a
|
||||
// borrowed-arena str, else nil. Same shape as cstage peek_package.
|
||||
fn peekpackage(a: *arena, pathcs: *u8) *u8 = {
|
||||
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil; };
|
||||
let buf: *u8 = os.alloc(2048u64): *u8;
|
||||
let n: i64 = os.read(fd, buf, 2048u64);
|
||||
os.close(fd);
|
||||
if (n <= 0i64) { return nil; };
|
||||
let nu: u64 = n: u64;
|
||||
let p: u64 = 0u64;
|
||||
for (p < nu) {
|
||||
let q: u64 = p;
|
||||
for (q < nu) {
|
||||
if (buf[q] == 10u8) { break; }; // '\n'
|
||||
q += 1u64;
|
||||
};
|
||||
let s: u64 = p;
|
||||
for (s < q) {
|
||||
if (buf[s] != 32u8) {
|
||||
if (buf[s] != 9u8) { break; };
|
||||
};
|
||||
s += 1u64;
|
||||
};
|
||||
if (s < q) {
|
||||
if (s + 1u64 < q) {
|
||||
if (buf[s] == 47u8) {
|
||||
if (buf[s + 1u64] == 47u8) {
|
||||
p = q + 1u64;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (s + 8u64 <= q) {
|
||||
if (buf[s] == 112u8) { // 'p'
|
||||
if (buf[s + 1u64] == 97u8) { // 'a'
|
||||
if (buf[s + 2u64] == 99u8) { // 'c'
|
||||
if (buf[s + 3u64] == 107u8) { // 'k'
|
||||
if (buf[s + 4u64] == 97u8) { // 'a'
|
||||
if (buf[s + 5u64] == 103u8) { // 'g'
|
||||
if (buf[s + 6u64] == 101u8) { // 'e'
|
||||
let sep: u8 = buf[s + 7u64];
|
||||
if (sep == 32u8) { }
|
||||
else { if (sep != 9u8) { return nil; }; };
|
||||
let t: u64 = s + 8u64;
|
||||
for (t < q) {
|
||||
if (buf[t] != 32u8) {
|
||||
if (buf[t] != 9u8) { break; };
|
||||
};
|
||||
t += 1u64;
|
||||
};
|
||||
let start: u64 = t;
|
||||
for (t < q) {
|
||||
let ch: u8 = buf[t];
|
||||
let isalpha: bool = false;
|
||||
if (ch >= 97u8) { if (ch <= 122u8) { isalpha = true; }; };
|
||||
if (ch >= 65u8) { if (ch <= 90u8) { isalpha = true; }; };
|
||||
if (ch >= 48u8) { if (ch <= 57u8) { isalpha = true; }; };
|
||||
if (ch == 95u8) { isalpha = true; };
|
||||
if (!isalpha) { break; };
|
||||
t += 1u64;
|
||||
};
|
||||
let plen: u64 = t - start;
|
||||
if (plen == 0u64) { return nil; };
|
||||
let r: *u8 = amalloc(a, plen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < plen) { r[k] = buf[start + k]; k += 1u64; };
|
||||
r[plen] = 0u8;
|
||||
return r;
|
||||
}; }; }; }; }; }; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
p = q + 1u64;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Strict-same-package error helper. Bundled here per task #22
|
||||
// brief — failure mode is dir-enum's own.
|
||||
fn strictpkgmismatch(file: *u8, pkg: *u8, dirpkg: *u8, dirpath: *u8) void = {
|
||||
os.write(2, "ww: ".ptr, 4u64);
|
||||
os.write(2, file, cstrlen(file));
|
||||
os.write(2, ": package ".ptr, 10u64);
|
||||
os.write(2, pkg, cstrlen(pkg));
|
||||
os.write(2, " differs from ".ptr, 14u64);
|
||||
os.write(2, dirpkg, cstrlen(dirpkg));
|
||||
os.write(2, " in same module dir ".ptr, 20u64);
|
||||
os.write(2, dirpath, cstrlen(dirpath));
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
os.exit(1);
|
||||
};
|
||||
|
||||
// expanddir — enumerate <dirpath>/*.ww (skip *test.ww and
|
||||
// *.combined.ww), byte-sort, recurse into each. Mirrors
|
||||
// ref/hare/hare/module/srcs.ha:183 `_findsrcs` minus tag handling.
|
||||
// The visited set keys on concrete file paths so multi-file modules
|
||||
// are pulled once. Strict-same-package: all enumerated files must
|
||||
// declare the same `package <name>;` (task #23 subset; failure
|
||||
// mode native to dir-enum).
|
||||
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(c.a, dirpath);
|
||||
let dlen: u64 = cstrlen(dirpath);
|
||||
let dirpkg: *u8 = nil;
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
// Two-step deref+index to avoid wwstage chained `names[i][k]`
|
||||
// cgen UNDER (task #24 — wwstage cgen chained-index inner
|
||||
// element size on **T). Wwstage treats inner element as 8B
|
||||
// (sizeof *u8) instead of 1B (sizeof u8); cstage handles
|
||||
// via typed-AST natively. Retire once the wwstage fix lands.
|
||||
let nm: *u8 = names[i];
|
||||
let nlen: u64 = cstrlen(nm);
|
||||
let fp: *u8 = amalloc(c.a, dlen + 1u64 + nlen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dirpath[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = nm[k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
let pkg: *u8 = peekpackage(c.a, fp);
|
||||
if (pkg != nil) {
|
||||
if (dirpkg == nil) { dirpkg = pkg; }
|
||||
else { if (!cstreq(dirpkg, pkg)) {
|
||||
strictpkgmismatch(fp, pkg, dirpkg, dirpath);
|
||||
}; };
|
||||
};
|
||||
expand(c, fp);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// ---- Build pipeline ---------------------------------------------------
|
||||
|
||||
// Strip the trailing ".ww" off `src` (a NUL-terminated path) into
|
||||
@@ -1308,19 +1589,21 @@ type lflags = struct {
|
||||
nlibs: i32,
|
||||
};
|
||||
|
||||
// buildone — compile `src` into the executable named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (w6c_ww/w6a_ww/w6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
// buildone — compile `src` (file or directory) into the executable
|
||||
// named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (w6c_ww/w6a_ww/w6l_ww)
|
||||
// src: NUL-terminated entry path (file or directory).
|
||||
// entryisdir: non-zero when src is a module directory.
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
//
|
||||
// The ww-side driver shells to the ww-side tools so a `ww_ww build`
|
||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
||||
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||
// pipelines to byte-identical output on a corpus.
|
||||
fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let a: *arena = newarena();
|
||||
|
||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||
@@ -1335,13 +1618,23 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(dotdotlib, off);
|
||||
};
|
||||
|
||||
// Compute the source-file's directory: bytes of `src` up to the
|
||||
// last '/'. If `src` has no '/', use ".". Hare's CWD-first
|
||||
// Compute the source directory. For a file entry: bytes of `src`
|
||||
// up to the last '/' (or "." when src has no '/'). For a dir
|
||||
// entry: the dir itself (less trailing slashes). Hare's CWD-first
|
||||
// convention assumes you're running from the module dir; our
|
||||
// wrappers don't cd, so dirname(src) stands in as the closest
|
||||
// analog. Source-dir wins ties over the system path (cc -I.).
|
||||
let srcd: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
{
|
||||
if (entryisdir != 0) {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let k: u64 = 0u64;
|
||||
for (k < slen) { srcd[k] = src[k]; k += 1u64; };
|
||||
for (slen > 1u64) {
|
||||
if (srcd[slen - 1u64] != 47u8) { break; };
|
||||
slen -= 1u64;
|
||||
};
|
||||
srcd[slen] = 0u8;
|
||||
} else {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let last: u64 = slen;
|
||||
let found: bool = false;
|
||||
@@ -1377,9 +1670,20 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(searchpath, off);
|
||||
};
|
||||
|
||||
// stem, .s, .o, .combined.ww, libwwrt.a
|
||||
// Stem for .s/.o/.combined.ww side files. Dir entry: <dir>/<base>;
|
||||
// file entry: src stripped of .ww.
|
||||
let stem: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makestem(stem, src);
|
||||
if (entryisdir != 0) {
|
||||
let dlen: u64 = cstrlen(srcd);
|
||||
let bo: u64 = basenameoff(srcd, dlen);
|
||||
let off: u64 = cstrinto(stem, 0u64, srcd);
|
||||
stem[off] = 47u8; off += 1u64; // '/'
|
||||
let i: u64 = bo;
|
||||
for (i < dlen) { stem[off] = srcd[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(stem, off);
|
||||
} else {
|
||||
makestem(stem, src);
|
||||
};
|
||||
let asmf: *u8 = appendlit(stem, ".s");
|
||||
let objf: *u8 = appendlit(stem, ".o");
|
||||
let combined: *u8 = appendlit(stem, ".combined.ww");
|
||||
@@ -1392,7 +1696,8 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(libwwrt, off);
|
||||
};
|
||||
|
||||
// Step 1: expand `use`s into the combined file.
|
||||
// Step 1: expand imports into the combined file. Dir entry →
|
||||
// enumerate the module dir; file entry → start at the file.
|
||||
let cf: i32 = os.open(pathstr(combined), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (cf < 0) {
|
||||
os.write(2, "ww: cannot open combined\n".ptr, 25u64);
|
||||
@@ -1404,7 +1709,8 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
c.out = cf;
|
||||
c.dirs = searchpath;
|
||||
c.visit = nil;
|
||||
expand(&c, src);
|
||||
if (entryisdir != 0) { expanddir(&c, srcd); }
|
||||
else { expand(&c, src); };
|
||||
};
|
||||
os.close(cf);
|
||||
|
||||
@@ -1566,43 +1872,41 @@ fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
||||
return buf;
|
||||
};
|
||||
|
||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8) *u8 = {
|
||||
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
||||
// "." to a concrete entry path. Sets *isdir when the entry is a
|
||||
// module directory (caller will dir-enumerate).
|
||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||
let nlen: u64 = cstrlen(name);
|
||||
|
||||
// (1) literal .ww file that exists
|
||||
// (1) Literal file that exists → use as-is.
|
||||
if (cstrendswithlit(name, ".ww")) {
|
||||
if (os.access(pathstr(name), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return arenadupcstr(a, name, nlen);
|
||||
};
|
||||
};
|
||||
|
||||
// (2) "." → cwd's <basename>.ww
|
||||
if (nlen == 1u64) {
|
||||
if (name[0u64] == 46u8) { // '.'
|
||||
let cwd: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
let r: i64 = os.getcwd(cwd, PATH_MAX);
|
||||
if (r <= 0i64) { return nil; };
|
||||
let cwdlen: u64 = (r: u64) - 1u64; // strip trailing NUL
|
||||
let bo: u64 = basenameoff(cwd, cwdlen);
|
||||
let blen: u64 = cwdlen - bo;
|
||||
let dot: *u8 = amalloc(a, 2u64): *u8;
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
let probe: *u8 = builddirmodulepath(a, dot, 1u64,
|
||||
cwd + bo, blen);
|
||||
if (os.access(pathstr(probe), 0i32) == 0) { return probe; };
|
||||
return nil;
|
||||
};
|
||||
// (2) Existing path → use as-is, dir vs file via stat.
|
||||
let fi: os.filestat;
|
||||
let sr: (void | os.oserror) = os.stat(&fi, pathstr(name));
|
||||
let found: bool = false;
|
||||
let foundisdir: i32 = 0;
|
||||
match (sr) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { foundisdir = 1; };
|
||||
found = true;
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (found) {
|
||||
*isdir = foundisdir;
|
||||
return arenadupcstr(a, name, nlen);
|
||||
};
|
||||
|
||||
// (3) <name>/<basename(name)>.ww — directory-as-module
|
||||
let bo: u64 = basenameoff(name, nlen);
|
||||
let probe: *u8 = builddirmodulepath(a, name, nlen,
|
||||
name + bo, nlen - bo);
|
||||
if (os.access(pathstr(probe), 0i32) == 0) { return probe; };
|
||||
|
||||
// (4) search path lookup
|
||||
// (3) Search-path lookup with dot-to-slash path translation.
|
||||
let search: *u8 = buildsearchpath(a, selfdir, incs);
|
||||
return locateimport(a, search, name, nlen);
|
||||
return locateimport(a, search, name, nlen, isdir);
|
||||
};
|
||||
|
||||
// ---- Subcommand handlers ----------------------------------------------
|
||||
@@ -1736,18 +2040,34 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
src = dot;
|
||||
};
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs);
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs, &isdir);
|
||||
if (resolved == nil) {
|
||||
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
||||
return 1;
|
||||
};
|
||||
let out: *u8 = defaultoutpath(resolved);
|
||||
let out: *u8 = nil;
|
||||
if (isdir != 0) {
|
||||
let rlen: u64 = cstrlen(resolved);
|
||||
for (rlen > 1u64) {
|
||||
if (resolved[rlen - 1u64] != 47u8) { break; };
|
||||
rlen -= 1u64;
|
||||
};
|
||||
let bo: u64 = basenameoff(resolved, rlen);
|
||||
out = os.alloc(PATH_MAX): *u8;
|
||||
let i: u64 = bo;
|
||||
let off: u64 = 0u64;
|
||||
for (i < rlen) { out[off] = resolved[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(out, off);
|
||||
} else {
|
||||
out = defaultoutpath(resolved);
|
||||
};
|
||||
let lf: lflags;
|
||||
lf.libdirs = libdirs;
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs;
|
||||
lf.nlibs = nlibs;
|
||||
return buildone(selfdir, resolved, out, incs, &lf);
|
||||
return buildone(selfdir, resolved, isdir, out, incs, &lf);
|
||||
};
|
||||
|
||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||
@@ -1875,7 +2195,8 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
src = dot;
|
||||
};
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs);
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs, &isdir);
|
||||
if (resolved == nil) {
|
||||
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
||||
return 1;
|
||||
@@ -1888,7 +2209,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs;
|
||||
lf.nlibs = nlibs;
|
||||
if (buildone(selfdir, resolved, tmp, incs, &lf) != 0) {
|
||||
if (buildone(selfdir, resolved, isdir, tmp, incs, &lf) != 0) {
|
||||
os.remove(pathstr(tmp));
|
||||
return 1;
|
||||
};
|
||||
@@ -1920,7 +2241,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
fn runsingletest(selfdir: *u8, src: *u8) i32 = {
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makeruntmp(tmp);
|
||||
if (buildone(selfdir, src, tmp, "\0".ptr, nil) != 0) {
|
||||
if (buildone(selfdir, src, 0, tmp, "\0".ptr, nil) != 0) {
|
||||
os.remove(pathstr(tmp));
|
||||
return 1;
|
||||
};
|
||||
@@ -1970,7 +2291,7 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
||||
cstrseal(tincs, ic);
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makeruntmp(tmp);
|
||||
let bres: i32 = buildone(selfdir, path, tmp, tincs, nil);
|
||||
let bres: i32 = buildone(selfdir, path, 0, tmp, tincs, nil);
|
||||
if (bres != 0) {
|
||||
fail += 1;
|
||||
os.write(2, "FAIL ".ptr, 5u64);
|
||||
|
||||
@@ -229,18 +229,31 @@ fn visitadd(c: *expctx, path: str) void = {
|
||||
c.visit = n;
|
||||
};
|
||||
|
||||
// Try <dir>/<name>.ww then <dir>/<name>/<name>.ww. Returns NUL-terminated
|
||||
// arena-resident path if found, else nil.
|
||||
//
|
||||
// Retained divergence from brief: directory-as-module enumeration is
|
||||
// NOT implemented here. The user's "module IS directory" mental model
|
||||
// is partially honored via the `package` keyword + file-walk + sibling
|
||||
// `import` chain. True dir enumeration (lib/foo/*.ww concatenated
|
||||
// atomically, no sibling-import boilerplate) is deferred to task #22
|
||||
// and needs a lib/os opendir/readdir wrapper around getdents64 first.
|
||||
// Rule 7 + rule 8 documentation.
|
||||
fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
// candidate 1: <dir>/<name>.ww
|
||||
// Translate dots in an `import` name to slashes for path lookup.
|
||||
// `encoding.utf8` → `encoding/utf8`. Mirrors Hare's hare(1)
|
||||
// use-path → fs-path mapping
|
||||
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
||||
// path::push per ident part).
|
||||
fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
||||
let buf: *u8 = amalloc(a, namelen + 1u64): *u8;
|
||||
let i: u64 = 0u64;
|
||||
for (i < namelen) {
|
||||
if (name[i] == 46u8) { buf[i] = 47u8; } // '.' -> '/'
|
||||
else { buf[i] = name[i]; };
|
||||
i += 1u64;
|
||||
};
|
||||
buf[namelen] = 0u8;
|
||||
return buf;
|
||||
};
|
||||
|
||||
// Try <dir>/<path>/ as a directory, then <dir>/<path>.ww as a file.
|
||||
// Sets *isdir on hit. Symmetric with cstage locate_import_in for
|
||||
// byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// form was dropped in task #22 — directory-as-module enumeration
|
||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||
// `foo/foo.ha` fallback; a module IS the directory).
|
||||
fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||
let buf: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
let off: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
@@ -248,15 +261,24 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
off += dirlen;
|
||||
buf[off] = 47u8; off += 1u64; // '/'
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf[off] = 46u8; off += 1u64; // '.'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
for (i < pflen) { buf[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf[off] = 0u8;
|
||||
if (os.access(pathstr(buf), 0i32) == 0) { return buf; };
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf));
|
||||
let isdirhit: bool = false;
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { isdirhit = true; };
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (isdirhit) {
|
||||
*isdir = 1;
|
||||
return buf;
|
||||
};
|
||||
|
||||
// candidate 2: <dir>/<name>/<name>.ww
|
||||
let buf2: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
off = 0u64;
|
||||
i = 0u64;
|
||||
@@ -264,22 +286,25 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64, name: *u8, namelen: u64) *u8 = {
|
||||
off += dirlen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf2[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < namelen) { buf2[off + i] = name[i]; i += 1u64; };
|
||||
off += namelen;
|
||||
buf2[off] = 46u8; off += 1u64;
|
||||
buf2[off] = 119u8; off += 1u64;
|
||||
buf2[off] = 119u8; off += 1u64;
|
||||
for (i < pflen) { buf2[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf2[off] = 46u8; off += 1u64; // '.'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 0u8;
|
||||
if (os.access(pathstr(buf2), 0i32) == 0) { return buf2; };
|
||||
if (os.access(pathstr(buf2), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return buf2;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Walk a colon-separated dirlist, return first hit or nil.
|
||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||
// *isdir on hit.
|
||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
||||
isdir: *i32) *u8 = {
|
||||
let pathform: *u8 = importpathform(a, name, namelen);
|
||||
let pflen: u64 = cstrlen(pathform);
|
||||
let total: u64 = cstrlen(dirs);
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
@@ -290,7 +315,8 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(a, dirs + p, seglen, name, namelen);
|
||||
let hit: *u8 = locatein(a, dirs + p, seglen,
|
||||
pathform, pflen, isdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
@@ -298,6 +324,123 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Filter for dir enumeration: keep `*.ww` minus `*test.ww` and the
|
||||
// `*.combined.ww` driver-generated concat artifacts (the previous
|
||||
// build leaves them in the source tree; they parse-error when
|
||||
// re-included). Returns true to keep.
|
||||
fn dirfilekeep(name: *u8, nlen: u64) bool = {
|
||||
if (nlen <= 3u64) { return false; };
|
||||
if (name[nlen - 3u64] != 46u8) { return false; }; // '.'
|
||||
if (name[nlen - 2u64] != 119u8) { return false; }; // 'w'
|
||||
if (name[nlen - 1u64] != 119u8) { return false; }; // 'w'
|
||||
if (nlen >= 7u64) {
|
||||
if (name[nlen - 7u64] == 116u8) { // 't'
|
||||
if (name[nlen - 6u64] == 101u8) { // 'e'
|
||||
if (name[nlen - 5u64] == 115u8) { // 's'
|
||||
if (name[nlen - 4u64] == 116u8) { // 't'
|
||||
return false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (nlen >= 12u64) {
|
||||
// ".combined.ww"
|
||||
if (name[nlen - 12u64] == 46u8) { // '.'
|
||||
if (name[nlen - 11u64] == 99u8) { // 'c'
|
||||
if (name[nlen - 10u64] == 111u8) { // 'o'
|
||||
if (name[nlen - 9u64] == 109u8) { // 'm'
|
||||
if (name[nlen - 8u64] == 98u8) { // 'b'
|
||||
return false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Byte-wise memcmp returning < 0, 0, > 0. Rule-10 byte-id requires
|
||||
// cstage and wwstage sort the same way; memcmp is the
|
||||
// locale-independent total order (mirrors ref/hare/sort/cmp/cmp.ha
|
||||
// strs).
|
||||
fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
||||
let n: u64 = alen;
|
||||
if (blen < n) { n = blen; };
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
let av: i32 = (a[i]): i32;
|
||||
let bv: i32 = (b[i]): i32;
|
||||
if (av < bv) { return -1; };
|
||||
if (av > bv) { return 1; };
|
||||
i += 1u64;
|
||||
};
|
||||
if (alen < blen) { return -1; };
|
||||
if (alen > blen) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
||||
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
||||
// name a NUL-terminated arena copy.
|
||||
fn enumeratedir(a: *arena, dirpath: *u8) (**u8, i32) = {
|
||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil: **u8, 0; };
|
||||
let maxnames: i32 = 256;
|
||||
let names: **u8 = amalloc(a, (maxnames: u64) * 8u64): **u8;
|
||||
let nlens: *u64 = amalloc(a, (maxnames: u64) * 8u64): *u64;
|
||||
let n: i32 = 0;
|
||||
let buf: *u8 = os.alloc(8192u64): *u8;
|
||||
let r: i64 = os.getdents64(fd, buf, 8192u64);
|
||||
for (r > 0i64) {
|
||||
let off: u64 = 0u64;
|
||||
let ru: u64 = r: u64;
|
||||
for (off < ru) {
|
||||
let blo: u64 = (buf[off + 16u64]): u64;
|
||||
let bhi: u64 = (buf[off + 17u64]): u64;
|
||||
let reclen: u64 = blo + (bhi * 256u64);
|
||||
let nm: *u8 = buf + off + 19u64;
|
||||
let nl: u64 = cstrlen(nm);
|
||||
if (dirfilekeep(nm, nl)) {
|
||||
if (n < maxnames) {
|
||||
let cp: *u8 = amalloc(a, nl + 1u64): *u8;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
};
|
||||
};
|
||||
off += reclen;
|
||||
};
|
||||
r = os.getdents64(fd, buf, 8192u64);
|
||||
};
|
||||
os.close(fd);
|
||||
|
||||
// Insertion sort, byte-wise. n is small (≤16 in practice).
|
||||
let i: i32 = 1;
|
||||
for (i < n) {
|
||||
let j: i32 = i;
|
||||
for (j > 0) {
|
||||
if (bytecmp(names[j - 1], nlens[j - 1],
|
||||
names[j], nlens[j]) <= 0) { j = 0; }
|
||||
else {
|
||||
let t: *u8 = names[j];
|
||||
names[j] = names[j - 1];
|
||||
names[j - 1] = t;
|
||||
let tl: u64 = nlens[j];
|
||||
nlens[j] = nlens[j - 1];
|
||||
nlens[j - 1] = tl;
|
||||
j -= 1;
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return names, n;
|
||||
};
|
||||
|
||||
// ---- file slurp -------------------------------------------------------
|
||||
|
||||
fn slurp(pathcs: *u8) (*u8, u64) = {
|
||||
@@ -367,9 +510,9 @@ fn scanuse(src: *u8, len: u64) (*u8, u64) = {
|
||||
};
|
||||
|
||||
// expand — emit one file's bytes verbatim into the combined stream,
|
||||
// after recursive-expanding its top-of-file `use X;` imports. Each
|
||||
// source declares its own `module <name>;` (parser stamps decls);
|
||||
// the driver no longer injects a `// MODULE:` marker.
|
||||
// after recursive-expanding its top-of-file `import X;` imports.
|
||||
// Each source declares its own `package <name>;` (parser stamps
|
||||
// decls).
|
||||
fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
let plen: u64 = cstrlen(pathcs);
|
||||
let pathstr: str = astrndup(c.a, pathcs, plen);
|
||||
@@ -384,7 +527,7 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// Pass 1: scan top-of-file `use X;` lines, recursively expand.
|
||||
// Pass 1: scan top-of-file `import X;` lines, recursively expand.
|
||||
let i: u64 = 0u64;
|
||||
for (i < blen) {
|
||||
let j: u64 = i;
|
||||
@@ -396,9 +539,12 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
let idn: u64;
|
||||
idp, idn = scanuse(bufp + i, j - i);
|
||||
if (idp != nil) {
|
||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn);
|
||||
let isdir: i32 = 0;
|
||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn,
|
||||
&isdir);
|
||||
if (ipath != nil) {
|
||||
expand(c, ipath);
|
||||
if (isdir != 0) { expanddir(c, ipath); }
|
||||
else { expand(c, ipath); };
|
||||
};
|
||||
};
|
||||
i = j + 1u64;
|
||||
@@ -408,6 +554,141 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
||||
os.writeall(c.out, "\n".ptr, 1u64);
|
||||
};
|
||||
|
||||
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
||||
// starts with `package <name>;` return the package name as a
|
||||
// borrowed-arena str, else nil. Same shape as cstage peek_package.
|
||||
fn peekpackage(a: *arena, pathcs: *u8) *u8 = {
|
||||
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil; };
|
||||
let buf: *u8 = os.alloc(2048u64): *u8;
|
||||
let n: i64 = os.read(fd, buf, 2048u64);
|
||||
os.close(fd);
|
||||
if (n <= 0i64) { return nil; };
|
||||
let nu: u64 = n: u64;
|
||||
let p: u64 = 0u64;
|
||||
for (p < nu) {
|
||||
let q: u64 = p;
|
||||
for (q < nu) {
|
||||
if (buf[q] == 10u8) { break; }; // '\n'
|
||||
q += 1u64;
|
||||
};
|
||||
let s: u64 = p;
|
||||
for (s < q) {
|
||||
if (buf[s] != 32u8) {
|
||||
if (buf[s] != 9u8) { break; };
|
||||
};
|
||||
s += 1u64;
|
||||
};
|
||||
if (s < q) {
|
||||
if (s + 1u64 < q) {
|
||||
if (buf[s] == 47u8) {
|
||||
if (buf[s + 1u64] == 47u8) {
|
||||
p = q + 1u64;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (s + 8u64 <= q) {
|
||||
if (buf[s] == 112u8) { // 'p'
|
||||
if (buf[s + 1u64] == 97u8) { // 'a'
|
||||
if (buf[s + 2u64] == 99u8) { // 'c'
|
||||
if (buf[s + 3u64] == 107u8) { // 'k'
|
||||
if (buf[s + 4u64] == 97u8) { // 'a'
|
||||
if (buf[s + 5u64] == 103u8) { // 'g'
|
||||
if (buf[s + 6u64] == 101u8) { // 'e'
|
||||
let sep: u8 = buf[s + 7u64];
|
||||
if (sep == 32u8) { }
|
||||
else { if (sep != 9u8) { return nil; }; };
|
||||
let t: u64 = s + 8u64;
|
||||
for (t < q) {
|
||||
if (buf[t] != 32u8) {
|
||||
if (buf[t] != 9u8) { break; };
|
||||
};
|
||||
t += 1u64;
|
||||
};
|
||||
let start: u64 = t;
|
||||
for (t < q) {
|
||||
let ch: u8 = buf[t];
|
||||
let isalpha: bool = false;
|
||||
if (ch >= 97u8) { if (ch <= 122u8) { isalpha = true; }; };
|
||||
if (ch >= 65u8) { if (ch <= 90u8) { isalpha = true; }; };
|
||||
if (ch >= 48u8) { if (ch <= 57u8) { isalpha = true; }; };
|
||||
if (ch == 95u8) { isalpha = true; };
|
||||
if (!isalpha) { break; };
|
||||
t += 1u64;
|
||||
};
|
||||
let plen: u64 = t - start;
|
||||
if (plen == 0u64) { return nil; };
|
||||
let r: *u8 = amalloc(a, plen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < plen) { r[k] = buf[start + k]; k += 1u64; };
|
||||
r[plen] = 0u8;
|
||||
return r;
|
||||
}; }; }; }; }; }; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
p = q + 1u64;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Strict-same-package error helper. Bundled here per task #22
|
||||
// brief — failure mode is dir-enum's own.
|
||||
fn strictpkgmismatch(file: *u8, pkg: *u8, dirpkg: *u8, dirpath: *u8) void = {
|
||||
os.write(2, "ww: ".ptr, 4u64);
|
||||
os.write(2, file, cstrlen(file));
|
||||
os.write(2, ": package ".ptr, 10u64);
|
||||
os.write(2, pkg, cstrlen(pkg));
|
||||
os.write(2, " differs from ".ptr, 14u64);
|
||||
os.write(2, dirpkg, cstrlen(dirpkg));
|
||||
os.write(2, " in same module dir ".ptr, 20u64);
|
||||
os.write(2, dirpath, cstrlen(dirpath));
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
os.exit(1);
|
||||
};
|
||||
|
||||
// expanddir — enumerate <dirpath>/*.ww (skip *test.ww and
|
||||
// *.combined.ww), byte-sort, recurse into each. Mirrors
|
||||
// ref/hare/hare/module/srcs.ha:183 `_findsrcs` minus tag handling.
|
||||
// The visited set keys on concrete file paths so multi-file modules
|
||||
// are pulled once. Strict-same-package: all enumerated files must
|
||||
// declare the same `package <name>;` (task #23 subset; failure
|
||||
// mode native to dir-enum).
|
||||
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||
let names: **u8;
|
||||
let n: i32;
|
||||
names, n = enumeratedir(c.a, dirpath);
|
||||
let dlen: u64 = cstrlen(dirpath);
|
||||
let dirpkg: *u8 = nil;
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
// Two-step deref+index to avoid wwstage chained `names[i][k]`
|
||||
// cgen UNDER (task #24 — wwstage cgen chained-index inner
|
||||
// element size on **T). Wwstage treats inner element as 8B
|
||||
// (sizeof *u8) instead of 1B (sizeof u8); cstage handles
|
||||
// via typed-AST natively. Retire once the wwstage fix lands.
|
||||
let nm: *u8 = names[i];
|
||||
let nlen: u64 = cstrlen(nm);
|
||||
let fp: *u8 = amalloc(c.a, dlen + 1u64 + nlen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dirpath[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = nm[k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
let pkg: *u8 = peekpackage(c.a, fp);
|
||||
if (pkg != nil) {
|
||||
if (dirpkg == nil) { dirpkg = pkg; }
|
||||
else { if (!cstreq(dirpkg, pkg)) {
|
||||
strictpkgmismatch(fp, pkg, dirpkg, dirpath);
|
||||
}; };
|
||||
};
|
||||
expand(c, fp);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// ---- Build pipeline ---------------------------------------------------
|
||||
|
||||
// Strip the trailing ".ww" off `src` (a NUL-terminated path) into
|
||||
@@ -448,19 +729,21 @@ type lflags = struct {
|
||||
nlibs: i32,
|
||||
};
|
||||
|
||||
// buildone — compile `src` into the executable named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (w6c_ww/w6a_ww/w6l_ww)
|
||||
// src: NUL-terminated path to the .ww file
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
// buildone — compile `src` (file or directory) into the executable
|
||||
// named `out`.
|
||||
// selfdir: NUL-terminated dir containing this driver and the
|
||||
// wwstage tools (w6c_ww/w6a_ww/w6l_ww)
|
||||
// src: NUL-terminated entry path (file or directory).
|
||||
// entryisdir: non-zero when src is a module directory.
|
||||
// out: NUL-terminated desired output path
|
||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||
//
|
||||
// The ww-side driver shells to the ww-side tools so a `ww_ww build`
|
||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
||||
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||
// pipelines to byte-identical output on a corpus.
|
||||
fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
let a: *arena = newarena();
|
||||
|
||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||
@@ -475,13 +758,23 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(dotdotlib, off);
|
||||
};
|
||||
|
||||
// Compute the source-file's directory: bytes of `src` up to the
|
||||
// last '/'. If `src` has no '/', use ".". Hare's CWD-first
|
||||
// Compute the source directory. For a file entry: bytes of `src`
|
||||
// up to the last '/' (or "." when src has no '/'). For a dir
|
||||
// entry: the dir itself (less trailing slashes). Hare's CWD-first
|
||||
// convention assumes you're running from the module dir; our
|
||||
// wrappers don't cd, so dirname(src) stands in as the closest
|
||||
// analog. Source-dir wins ties over the system path (cc -I.).
|
||||
let srcd: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
{
|
||||
if (entryisdir != 0) {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let k: u64 = 0u64;
|
||||
for (k < slen) { srcd[k] = src[k]; k += 1u64; };
|
||||
for (slen > 1u64) {
|
||||
if (srcd[slen - 1u64] != 47u8) { break; };
|
||||
slen -= 1u64;
|
||||
};
|
||||
srcd[slen] = 0u8;
|
||||
} else {
|
||||
let slen: u64 = cstrlen(src);
|
||||
let last: u64 = slen;
|
||||
let found: bool = false;
|
||||
@@ -517,9 +810,20 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(searchpath, off);
|
||||
};
|
||||
|
||||
// stem, .s, .o, .combined.ww, libwwrt.a
|
||||
// Stem for .s/.o/.combined.ww side files. Dir entry: <dir>/<base>;
|
||||
// file entry: src stripped of .ww.
|
||||
let stem: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makestem(stem, src);
|
||||
if (entryisdir != 0) {
|
||||
let dlen: u64 = cstrlen(srcd);
|
||||
let bo: u64 = basenameoff(srcd, dlen);
|
||||
let off: u64 = cstrinto(stem, 0u64, srcd);
|
||||
stem[off] = 47u8; off += 1u64; // '/'
|
||||
let i: u64 = bo;
|
||||
for (i < dlen) { stem[off] = srcd[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(stem, off);
|
||||
} else {
|
||||
makestem(stem, src);
|
||||
};
|
||||
let asmf: *u8 = appendlit(stem, ".s");
|
||||
let objf: *u8 = appendlit(stem, ".o");
|
||||
let combined: *u8 = appendlit(stem, ".combined.ww");
|
||||
@@ -532,7 +836,8 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
cstrseal(libwwrt, off);
|
||||
};
|
||||
|
||||
// Step 1: expand `use`s into the combined file.
|
||||
// Step 1: expand imports into the combined file. Dir entry →
|
||||
// enumerate the module dir; file entry → start at the file.
|
||||
let cf: i32 = os.open(pathstr(combined), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (cf < 0) {
|
||||
os.write(2, "ww: cannot open combined\n".ptr, 25u64);
|
||||
@@ -544,7 +849,8 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||
c.out = cf;
|
||||
c.dirs = searchpath;
|
||||
c.visit = nil;
|
||||
expand(&c, src);
|
||||
if (entryisdir != 0) { expanddir(&c, srcd); }
|
||||
else { expand(&c, src); };
|
||||
};
|
||||
os.close(cf);
|
||||
|
||||
@@ -706,43 +1012,41 @@ fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
||||
return buf;
|
||||
};
|
||||
|
||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8) *u8 = {
|
||||
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
||||
// "." to a concrete entry path. Sets *isdir when the entry is a
|
||||
// module directory (caller will dir-enumerate).
|
||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||
let nlen: u64 = cstrlen(name);
|
||||
|
||||
// (1) literal .ww file that exists
|
||||
// (1) Literal file that exists → use as-is.
|
||||
if (cstrendswithlit(name, ".ww")) {
|
||||
if (os.access(pathstr(name), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return arenadupcstr(a, name, nlen);
|
||||
};
|
||||
};
|
||||
|
||||
// (2) "." → cwd's <basename>.ww
|
||||
if (nlen == 1u64) {
|
||||
if (name[0u64] == 46u8) { // '.'
|
||||
let cwd: *u8 = amalloc(a, PATH_MAX): *u8;
|
||||
let r: i64 = os.getcwd(cwd, PATH_MAX);
|
||||
if (r <= 0i64) { return nil; };
|
||||
let cwdlen: u64 = (r: u64) - 1u64; // strip trailing NUL
|
||||
let bo: u64 = basenameoff(cwd, cwdlen);
|
||||
let blen: u64 = cwdlen - bo;
|
||||
let dot: *u8 = amalloc(a, 2u64): *u8;
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
let probe: *u8 = builddirmodulepath(a, dot, 1u64,
|
||||
cwd + bo, blen);
|
||||
if (os.access(pathstr(probe), 0i32) == 0) { return probe; };
|
||||
return nil;
|
||||
};
|
||||
// (2) Existing path → use as-is, dir vs file via stat.
|
||||
let fi: os.filestat;
|
||||
let sr: (void | os.oserror) = os.stat(&fi, pathstr(name));
|
||||
let found: bool = false;
|
||||
let foundisdir: i32 = 0;
|
||||
match (sr) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { foundisdir = 1; };
|
||||
found = true;
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (found) {
|
||||
*isdir = foundisdir;
|
||||
return arenadupcstr(a, name, nlen);
|
||||
};
|
||||
|
||||
// (3) <name>/<basename(name)>.ww — directory-as-module
|
||||
let bo: u64 = basenameoff(name, nlen);
|
||||
let probe: *u8 = builddirmodulepath(a, name, nlen,
|
||||
name + bo, nlen - bo);
|
||||
if (os.access(pathstr(probe), 0i32) == 0) { return probe; };
|
||||
|
||||
// (4) search path lookup
|
||||
// (3) Search-path lookup with dot-to-slash path translation.
|
||||
let search: *u8 = buildsearchpath(a, selfdir, incs);
|
||||
return locateimport(a, search, name, nlen);
|
||||
return locateimport(a, search, name, nlen, isdir);
|
||||
};
|
||||
|
||||
// ---- Subcommand handlers ----------------------------------------------
|
||||
@@ -876,18 +1180,34 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
src = dot;
|
||||
};
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs);
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs, &isdir);
|
||||
if (resolved == nil) {
|
||||
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
||||
return 1;
|
||||
};
|
||||
let out: *u8 = defaultoutpath(resolved);
|
||||
let out: *u8 = nil;
|
||||
if (isdir != 0) {
|
||||
let rlen: u64 = cstrlen(resolved);
|
||||
for (rlen > 1u64) {
|
||||
if (resolved[rlen - 1u64] != 47u8) { break; };
|
||||
rlen -= 1u64;
|
||||
};
|
||||
let bo: u64 = basenameoff(resolved, rlen);
|
||||
out = os.alloc(PATH_MAX): *u8;
|
||||
let i: u64 = bo;
|
||||
let off: u64 = 0u64;
|
||||
for (i < rlen) { out[off] = resolved[i]; off += 1u64; i += 1u64; };
|
||||
cstrseal(out, off);
|
||||
} else {
|
||||
out = defaultoutpath(resolved);
|
||||
};
|
||||
let lf: lflags;
|
||||
lf.libdirs = libdirs;
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs;
|
||||
lf.nlibs = nlibs;
|
||||
return buildone(selfdir, resolved, out, incs, &lf);
|
||||
return buildone(selfdir, resolved, isdir, out, incs, &lf);
|
||||
};
|
||||
|
||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||
@@ -1015,7 +1335,8 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
dot[0] = 46u8; dot[1] = 0u8;
|
||||
src = dot;
|
||||
};
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs);
|
||||
let isdir: i32 = 0;
|
||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs, &isdir);
|
||||
if (resolved == nil) {
|
||||
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
||||
return 1;
|
||||
@@ -1028,7 +1349,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs;
|
||||
lf.nlibs = nlibs;
|
||||
if (buildone(selfdir, resolved, tmp, incs, &lf) != 0) {
|
||||
if (buildone(selfdir, resolved, isdir, tmp, incs, &lf) != 0) {
|
||||
os.remove(pathstr(tmp));
|
||||
return 1;
|
||||
};
|
||||
@@ -1060,7 +1381,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
fn runsingletest(selfdir: *u8, src: *u8) i32 = {
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makeruntmp(tmp);
|
||||
if (buildone(selfdir, src, tmp, "\0".ptr, nil) != 0) {
|
||||
if (buildone(selfdir, src, 0, tmp, "\0".ptr, nil) != 0) {
|
||||
os.remove(pathstr(tmp));
|
||||
return 1;
|
||||
};
|
||||
@@ -1110,7 +1431,7 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
||||
cstrseal(tincs, ic);
|
||||
let tmp: *u8 = os.alloc(PATH_MAX): *u8;
|
||||
makeruntmp(tmp);
|
||||
let bres: i32 = buildone(selfdir, path, tmp, tincs, nil);
|
||||
let bres: i32 = buildone(selfdir, path, 0, tmp, tincs, nil);
|
||||
if (bres != 0) {
|
||||
fail += 1;
|
||||
os.write(2, "FAIL ".ptr, 5u64);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1277,7 +1277,7 @@ export fn encoderune(out: []u8, r: rune) i32 = {
|
||||
package strings;
|
||||
|
||||
import bytes;
|
||||
import utf8;
|
||||
import encoding.utf8;
|
||||
import os;
|
||||
|
||||
// toutf8 — borrowed []u8 view of `s`. ref/hare/strings/utf8.ha:29.
|
||||
|
||||
Reference in New Issue
Block a user