ww: select sources by Go platform suffixes

This commit is contained in:
2026-08-14 21:25:16 +09:00
parent c59a7c11de
commit f138844737
3 changed files with 391 additions and 47 deletions

View File

@@ -563,6 +563,105 @@ def SEP_LOAD_VENDOR: i32 = -4;
def SEP_INITIAL_CAP: i32 = 8;
def SEP_COUNT_MAX: i32 = 2147483647;
fn sepnamerangeis(name: *u8, start: u64, end: u64, word: str) bool = {
if (end < start || end - start != word.len: u64) { return false; };
let i: u64 = 0u64;
for (i < end - start) {
if (name[start + i] != word.ptr[i]) { return false; };
i += 1u64;
};
return true;
};
fn sepknownos(name: *u8, start: u64, end: u64) bool = {
return sepnamerangeis(name, start, end, "aix")
|| sepnamerangeis(name, start, end, "android")
|| sepnamerangeis(name, start, end, "darwin")
|| sepnamerangeis(name, start, end, "dragonfly")
|| sepnamerangeis(name, start, end, "freebsd")
|| sepnamerangeis(name, start, end, "hurd")
|| sepnamerangeis(name, start, end, "illumos")
|| sepnamerangeis(name, start, end, "ios")
|| sepnamerangeis(name, start, end, "js")
|| sepnamerangeis(name, start, end, "linux")
|| sepnamerangeis(name, start, end, "nacl")
|| sepnamerangeis(name, start, end, "netbsd")
|| sepnamerangeis(name, start, end, "openbsd")
|| sepnamerangeis(name, start, end, "plan9")
|| sepnamerangeis(name, start, end, "solaris")
|| sepnamerangeis(name, start, end, "wasip1")
|| sepnamerangeis(name, start, end, "windows")
|| sepnamerangeis(name, start, end, "zos");
};
fn sepknownarch(name: *u8, start: u64, end: u64) bool = {
return sepnamerangeis(name, start, end, "386")
|| sepnamerangeis(name, start, end, "amd64")
|| sepnamerangeis(name, start, end, "amd64p32")
|| sepnamerangeis(name, start, end, "arm")
|| sepnamerangeis(name, start, end, "armbe")
|| sepnamerangeis(name, start, end, "arm64")
|| sepnamerangeis(name, start, end, "arm64be")
|| sepnamerangeis(name, start, end, "loong64")
|| sepnamerangeis(name, start, end, "mips")
|| sepnamerangeis(name, start, end, "mipsle")
|| sepnamerangeis(name, start, end, "mips64")
|| sepnamerangeis(name, start, end, "mips64le")
|| sepnamerangeis(name, start, end, "mips64p32")
|| sepnamerangeis(name, start, end, "mips64p32le")
|| sepnamerangeis(name, start, end, "ppc")
|| sepnamerangeis(name, start, end, "ppc64")
|| sepnamerangeis(name, start, end, "ppc64le")
|| sepnamerangeis(name, start, end, "riscv")
|| sepnamerangeis(name, start, end, "riscv64")
|| sepnamerangeis(name, start, end, "s390")
|| sepnamerangeis(name, start, end, "s390x")
|| sepnamerangeis(name, start, end, "sparc")
|| sepnamerangeis(name, start, end, "sparc64")
|| sepnamerangeis(name, start, end, "wasm");
};
fn septargettag(name: *u8, start: u64, end: u64) bool = {
return sepnamerangeis(name, start, end, "linux")
|| sepnamerangeis(name, start, end, "amd64");
};
// Go 1.26.5 build.go:1980-2027 filters known platform suffixes before
// opening a source. WW currently has one honest target, linux/amd64.
fn sepsourcematchestarget(name: *u8, nlen: u64) bool = {
let stem: u64 = 0u64;
let hasunderscore: bool = false;
for (stem < nlen && name[stem] != '.') {
if (name[stem] == '_') { hasunderscore = true; };
stem += 1u64;
};
if (!hasunderscore) { return true; };
let end: u64 = stem;
let last: u64 = end;
for (last > 0u64 && name[last - 1u64] != '_') { last -= 1u64; };
if (sepnamerangeis(name, last, end, "test")) {
if (last == 0u64) { return true; };
end = last - 1u64;
last = end;
for (last > 0u64 && name[last - 1u64] != '_') { last -= 1u64; };
};
if (last == 0u64) { return true; };
let prevend: u64 = last - 1u64;
let prev: u64 = prevend;
for (prev > 0u64 && name[prev - 1u64] != '_') { prev -= 1u64; };
if (prev < prevend && sepknownos(name, prev, prevend)
&& sepknownarch(name, last, end)) {
return septargettag(name, last, end)
&& septargettag(name, prev, prevend);
};
if (sepknownos(name, last, end) || sepknownarch(name, last, end)) {
return septargettag(name, last, end);
};
return true;
};
// Classify a selected directory entry: 1 production, 2 test, 0 skipped,
// -1 @test outside *_test.ww, -2 non-regular source.
fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
@@ -575,10 +674,12 @@ fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
s.ptr = name;
s.len = nlen: i32;
if (!strings.hassuffix(s, ".ww")) { return 0; };
if (!sepsourcematchestarget(name, nlen)) { return 0; };
let istest: bool = strings.hassuffix(s, "_test.ww");
if (istest && variant == SEP_VARIANT_PRODUCTION) { return 0; };
if (!istest && variant == SEP_VARIANT_EXTERNAL) { return 0; };
let source: *u8 = joinpath(dirpath, name);
let source: *u8 = sepjoinpath(dirpath, name);
if (source == nil) { return -2; };
let fi: os.filestat;
let sr: (void | os.oserror) = os.lstat(&fi, pathstr(source));
let regular: bool = false;
@@ -695,31 +796,11 @@ fn enumeratedir(dirpath: *u8, variant: i32,
let reclen: u64 = blo + (bhi * 256u64);
let nm: *u8 = buf.ptr + off + 19u64;
let nl: u64 = cstrlen(nm);
let cls: i32 = dirfileclass(dirpath, nm, nl, variant);
if (cls == -1) {
cerr("ww: ");
cerr(pathstr(joinpath(dirpath, nm)));
cerr(": @test declaration outside *_test.ww\n");
os.close(fd);
return nil: **u8, -2;
};
if (cls == -2) {
os.close(fd);
return nil: **u8, -2;
};
if (cls > 0) {
let full: *u8 = joinpath(dirpath, nm);
if (cls == 2) {
let pn: *u8 = dirpackagename(full);
if (pn == nil) {
os.close(fd);
return nil: **u8, -2;
};
if (testpackage == nil || !cstreq(pn, testpackage)) {
off += reclen;
continue;
};
};
if (nl > 3u64 && nm[0] != '.' && nm[0] != '_') {
let view: str;
view.ptr = nm;
view.len = nl: i32;
if (strings.hassuffix(view, ".ww")) {
if (n == SEP_COUNT_MAX) {
sepfailsize();
os.close(fd);
@@ -730,10 +811,16 @@ fn enumeratedir(dirpath: *u8, variant: i32,
os.close(fd);
return nil: **u8, -2;
};
names[n] = full;
nlens[n] = cstrlen(full);
kinds[n] = cls;
let owned: *u8 = sepdupcstr(nm, nl);
if (owned == nil) {
os.close(fd);
return nil: **u8, -2;
};
names[n] = owned;
nlens[n] = nl;
kinds[n] = 0;
n += 1;
};
};
off += reclen;
};
@@ -749,8 +836,69 @@ fn enumeratedir(dirpath: *u8, variant: i32,
return nil: **u8, -1;
};
// Insertion sort, byte-wise. n is small (≤16 in practice).
// Go's directory reader presents a byte-sorted name list to the loader.
let i: i32 = 1;
for (i < n) {
let j: i32 = i;
for (j > 0) {
let c: i32 = bytecmp(names[j - 1], nlens[j - 1],
names[j], nlens[j]);
if (c <= 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;
let tk: i32 = kinds[j];
kinds[j] = kinds[j - 1];
kinds[j - 1] = tk;
j -= 1;
};
};
i += 1;
};
let raw: i32 = n;
let selected: i32 = 0;
let ri: i32 = 0;
for (ri < raw) {
let nm: *u8 = names[ri];
let nl: u64 = nlens[ri];
let cls: i32 = dirfileclass(dirpath, nm, nl, variant);
if (cls == -1) {
let badpath: *u8 = sepjoinpath(dirpath, nm);
if (badpath == nil) { return nil: **u8, -2; };
cerr("ww: ");
cerr(pathstr(badpath));
cerr(": @test declaration outside *_test.ww\n");
return nil: **u8, -2;
};
if (cls == -2) { return nil: **u8, -2; };
if (cls > 0) {
let full: *u8 = sepjoinpath(dirpath, nm);
if (full == nil) { return nil: **u8, -2; };
let keep: bool = true;
if (cls == 2) {
let pn: *u8 = dirpackagename(full);
if (pn == nil) { return nil: **u8, -2; };
if (testpackage == nil || !cstreq(pn, testpackage)) {
keep = false;
};
};
if (keep) {
names[selected] = full;
nlens[selected] = cstrlen(full);
kinds[selected] = cls;
selected += 1;
};
};
ri += 1;
};
n = selected;
// Production files precede test files; each partition remains byte-sorted.
i = 1;
for (i < n) {
let j: i32 = i;
for (j > 0) {
@@ -4981,14 +5129,14 @@ fn validatecommandoutputpath(out: *u8) i32 = {
fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) {
if (emitasm != 0) {
return "ww workdir fmt 16 mode test asm 1\n";
return "ww workdir fmt 17 mode test asm 1\n";
};
return "ww workdir fmt 16 mode test asm 0\n";
return "ww workdir fmt 17 mode test asm 0\n";
};
if (emitasm != 0) {
return "ww workdir fmt 17 mode build asm 1\n";
return "ww workdir fmt 18 mode build asm 1\n";
};
return "ww workdir fmt 17 mode build asm 0\n";
return "ww workdir fmt 18 mode build asm 0\n";
};
fn stampmatches(path: *u8, want: str) bool = {