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

@@ -372,6 +372,79 @@ strs_cmp(const void *a, const void *b)
return strcmp(sa, sb); return strcmp(sa, sb);
} }
static int
sep_name_is(const char *s, size_t n, const char *word)
{
return strlen(word) == n && memcmp(s, word, n) == 0;
}
static int
sep_known_os(const char *s, size_t n)
{
static const char *known[] = {
"aix", "android", "darwin", "dragonfly", "freebsd", "hurd",
"illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd",
"plan9", "solaris", "wasip1", "windows", "zos",
};
for (size_t i = 0; i < sizeof known / sizeof known[0]; i++)
if (sep_name_is(s, n, known[i])) return 1;
return 0;
}
static int
sep_known_arch(const char *s, size_t n)
{
static const char *known[] = {
"386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be",
"loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32",
"mips64p32le", "ppc", "ppc64", "ppc64le", "riscv", "riscv64",
"s390", "s390x", "sparc", "sparc64", "wasm",
};
for (size_t i = 0; i < sizeof known / sizeof known[0]; i++)
if (sep_name_is(s, n, known[i])) return 1;
return 0;
}
static int
sep_target_tag(const char *s, size_t n)
{
return sep_name_is(s, n, "linux") || sep_name_is(s, n, "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. */
static int
sep_source_matches_target(const char *name)
{
size_t stem = strcspn(name, ".");
if (memchr(name, '_', stem) == NULL) return 1;
size_t end = stem;
size_t last = end;
while (last > 0 && name[last - 1] != '_') last--;
if (sep_name_is(name + last, end - last, "test")) {
if (last == 0) return 1;
end = last - 1;
last = end;
while (last > 0 && name[last - 1] != '_') last--;
}
if (last == 0) return 1;
const char *final = name + last;
size_t nfinal = end - last;
size_t prevend = last - 1;
size_t prev = prevend;
while (prev > 0 && name[prev - 1] != '_') prev--;
if (prev < prevend
&& sep_known_os(name + prev, prevend - prev)
&& sep_known_arch(final, nfinal))
return sep_target_tag(final, nfinal)
&& sep_target_tag(name + prev, prevend - prev);
if (sep_known_os(final, nfinal) || sep_known_arch(final, nfinal))
return sep_target_tag(final, nfinal);
return 1;
}
static int sep_slurp(const char*, char**, u64*); static int sep_slurp(const char*, char**, u64*);
/* Go's contract: only *_test.ww is a test source. Ask the compiler parser, /* Go's contract: only *_test.ww is a test source. Ask the compiler parser,
@@ -564,15 +637,39 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
{ {
DIR *d = opendir(dirpath); DIR *d = opendir(dirpath);
if (d == NULL) { *out_files = NULL; return -1; } if (d == NULL) { *out_files = NULL; return -1; }
char **names = NULL;
int nnames = 0, capnames = 0;
char **prod = NULL, **tests = NULL; char **prod = NULL, **tests = NULL;
int nprod = 0, capprod = 0, ntests = 0, captests = 0; int nprod = 0, capprod = 0, ntests = 0, captests = 0;
struct dirent *ent; struct dirent *ent = NULL;
while ((ent = readdir(d)) != NULL) { for (;;) {
errno = 0;
ent = readdir(d);
if (ent == NULL) break;
const char *nm = ent->d_name; const char *nm = ent->d_name;
size_t nl = strlen(nm); size_t nl = strlen(nm);
if (nl <= 3) continue; if (nl <= 3) continue;
if (nm[0] == '.' || nm[0] == '_') continue; if (nm[0] == '.' || nm[0] == '_') continue;
if (strcmp(nm + nl - 3, ".ww") != 0) continue; if (strcmp(nm + nl - 3, ".ww") != 0) continue;
if (source_list_add(&names, &nnames, &capnames, nm) < 0) {
source_list_free(names, nnames);
closedir(d);
*out_files = NULL;
return -2;
}
}
int direrr = errno;
closedir(d);
if (direrr != 0) {
source_list_free(names, nnames);
*out_files = NULL;
return -1;
}
if (nnames > 1) qsort(names, (size_t)nnames, sizeof *names, strs_cmp);
for (int ni = 0; ni < nnames; ni++) {
const char *nm = names[ni];
size_t nl = strlen(nm);
if (!sep_source_matches_target(nm)) continue;
int is_test = nl >= 8 && strcmp(nm + nl - 8, "_test.ww") == 0; int is_test = nl >= 8 && strcmp(nm + nl - 8, "_test.ww") == 0;
if (variant == SEP_VARIANT_PRODUCTION && is_test) continue; if (variant == SEP_VARIANT_PRODUCTION && is_test) continue;
if (variant == SEP_VARIANT_EXTERNAL && !is_test) continue; if (variant == SEP_VARIANT_EXTERNAL && !is_test) continue;
@@ -580,9 +677,9 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
int pn = snprintf(path, sizeof path, "%s/%s", dirpath, nm); int pn = snprintf(path, sizeof path, "%s/%s", dirpath, nm);
if (pn < 0 || (size_t)pn >= sizeof path) { if (pn < 0 || (size_t)pn >= sizeof path) {
fprintf(stderr, "ww: package source path is too long\n"); fprintf(stderr, "ww: package source path is too long\n");
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
@@ -590,9 +687,9 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
if (lstat(path, &st) != 0) { if (lstat(path, &st) != 0) {
fprintf(stderr, fprintf(stderr,
"ww: %s: package source is not a regular file\n", path); "ww: %s: package source is not a regular file\n", path);
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
@@ -601,9 +698,9 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
fprintf(stderr, fprintf(stderr,
"ww: %s: package source is not a regular file\n", "ww: %s: package source is not a regular file\n",
path); path);
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
@@ -613,17 +710,17 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
if (!S_ISREG(st.st_mode)) { if (!S_ISREG(st.st_mode)) {
fprintf(stderr, fprintf(stderr,
"ww: %s: package source is not a regular file\n", path); "ww: %s: package source is not a regular file\n", path);
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
int has_test = !is_test ? source_has_test_decl(path) : 0; int has_test = !is_test ? source_has_test_decl(path) : 0;
if (has_test < 0) { if (has_test < 0) {
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
@@ -631,18 +728,18 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
fprintf(stderr, fprintf(stderr,
"ww: %s: @test declaration outside *_test.ww\n", "ww: %s: @test declaration outside *_test.ww\n",
path); path);
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
if (is_test) { if (is_test) {
char *package = NULL; char *package = NULL;
if (source_package_name(path, &package) < 0) { if (source_package_name(path, &package) < 0) {
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
@@ -656,14 +753,14 @@ enumerate_dir_ww(const char *dirpath, int variant, const char *test_package,
int *n = is_test ? &ntests : &nprod; int *n = is_test ? &ntests : &nprod;
int *cap = is_test ? &captests : &capprod; int *cap = is_test ? &captests : &capprod;
if (source_list_add(list, n, cap, path) < 0) { if (source_list_add(list, n, cap, path) < 0) {
source_list_free(names, nnames);
source_list_free(prod, nprod); source_list_free(prod, nprod);
source_list_free(tests, ntests); source_list_free(tests, ntests);
closedir(d);
*out_files = NULL; *out_files = NULL;
return -2; return -2;
} }
} }
closedir(d); source_list_free(names, nnames);
if (nprod > 1) qsort(prod, (size_t)nprod, sizeof *prod, strs_cmp); if (nprod > 1) qsort(prod, (size_t)nprod, sizeof *prod, strs_cmp);
if (ntests > 1) qsort(tests, (size_t)ntests, sizeof *tests, strs_cmp); if (ntests > 1) qsort(tests, (size_t)ntests, sizeof *tests, strs_cmp);
if (nprod > INT_MAX - ntests) { if (nprod > INT_MAX - ntests) {
@@ -4161,7 +4258,7 @@ static void
workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm) workdir_stamp_text(char *buf, size_t bufsz, int is_test, int emit_asm)
{ {
snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n", snprintf(buf, bufsz, "ww workdir fmt %d mode %s asm %d\n",
is_test ? 16 : 17, is_test ? "test" : "build", emit_asm); is_test ? 17 : 18, is_test ? "test" : "build", emit_asm);
} }
static int static int

View File

@@ -582,9 +582,108 @@ fn pkgisreg(path: str) bool = {
}; };
}; };
fn pkgnamerangeis(name: str, start: i32, end: i32, word: str) bool = {
if (end < start || end - start != word.len) { return false; };
let i: i32 = 0;
for (i < end - start) {
if (name[start + i] != word[i]) { return false; };
i += 1;
};
return true;
};
fn pkgknownos(name: str, start: i32, end: i32) bool = {
return pkgnamerangeis(name, start, end, "aix")
|| pkgnamerangeis(name, start, end, "android")
|| pkgnamerangeis(name, start, end, "darwin")
|| pkgnamerangeis(name, start, end, "dragonfly")
|| pkgnamerangeis(name, start, end, "freebsd")
|| pkgnamerangeis(name, start, end, "hurd")
|| pkgnamerangeis(name, start, end, "illumos")
|| pkgnamerangeis(name, start, end, "ios")
|| pkgnamerangeis(name, start, end, "js")
|| pkgnamerangeis(name, start, end, "linux")
|| pkgnamerangeis(name, start, end, "nacl")
|| pkgnamerangeis(name, start, end, "netbsd")
|| pkgnamerangeis(name, start, end, "openbsd")
|| pkgnamerangeis(name, start, end, "plan9")
|| pkgnamerangeis(name, start, end, "solaris")
|| pkgnamerangeis(name, start, end, "wasip1")
|| pkgnamerangeis(name, start, end, "windows")
|| pkgnamerangeis(name, start, end, "zos");
};
fn pkgknownarch(name: str, start: i32, end: i32) bool = {
return pkgnamerangeis(name, start, end, "386")
|| pkgnamerangeis(name, start, end, "amd64")
|| pkgnamerangeis(name, start, end, "amd64p32")
|| pkgnamerangeis(name, start, end, "arm")
|| pkgnamerangeis(name, start, end, "armbe")
|| pkgnamerangeis(name, start, end, "arm64")
|| pkgnamerangeis(name, start, end, "arm64be")
|| pkgnamerangeis(name, start, end, "loong64")
|| pkgnamerangeis(name, start, end, "mips")
|| pkgnamerangeis(name, start, end, "mipsle")
|| pkgnamerangeis(name, start, end, "mips64")
|| pkgnamerangeis(name, start, end, "mips64le")
|| pkgnamerangeis(name, start, end, "mips64p32")
|| pkgnamerangeis(name, start, end, "mips64p32le")
|| pkgnamerangeis(name, start, end, "ppc")
|| pkgnamerangeis(name, start, end, "ppc64")
|| pkgnamerangeis(name, start, end, "ppc64le")
|| pkgnamerangeis(name, start, end, "riscv")
|| pkgnamerangeis(name, start, end, "riscv64")
|| pkgnamerangeis(name, start, end, "s390")
|| pkgnamerangeis(name, start, end, "s390x")
|| pkgnamerangeis(name, start, end, "sparc")
|| pkgnamerangeis(name, start, end, "sparc64")
|| pkgnamerangeis(name, start, end, "wasm");
};
fn pkgtargettag(name: str, start: i32, end: i32) bool = {
return pkgnamerangeis(name, start, end, "linux")
|| pkgnamerangeis(name, start, end, "amd64");
};
// Recursive discovery must decide platform eligibility before a source can
// enter any production or test package graph.
fn pkgsourceplatform(name: str) bool = {
let stem: i32 = 0;
let hasunderscore: bool = false;
for (stem < name.len && name[stem] != '.') {
if (name[stem] == '_') { hasunderscore = true; };
stem += 1;
};
if (!hasunderscore) { return true; };
let end: i32 = stem;
let last: i32 = end;
for (last > 0 && name[last - 1] != '_') { last -= 1; };
if (pkgnamerangeis(name, last, end, "test")) {
if (last == 0) { return true; };
end = last - 1;
last = end;
for (last > 0 && name[last - 1] != '_') { last -= 1; };
};
if (last == 0) { return true; };
let prevend: i32 = last - 1;
let prev: i32 = prevend;
for (prev > 0 && name[prev - 1] != '_') { prev -= 1; };
if (prev < prevend && pkgknownos(name, prev, prevend)
&& pkgknownarch(name, last, end)) {
return pkgtargettag(name, last, end)
&& pkgtargettag(name, prev, prevend);
};
if (pkgknownos(name, last, end) || pkgknownarch(name, last, end)) {
return pkgtargettag(name, last, end);
};
return true;
};
fn pkgkeepfile(name: str) bool = { fn pkgkeepfile(name: str) bool = {
if (name.len <= 3 || name[0] == '.' || name[0] == '_') { return false; }; if (name.len <= 3 || name[0] == '.' || name[0] == '_') { return false; };
return strings.hassuffix(name, ".ww"); return strings.hassuffix(name, ".ww") && pkgsourceplatform(name);
}; };
type pkgmatcher = struct { type pkgmatcher = struct {

View File

@@ -563,6 +563,105 @@ def SEP_LOAD_VENDOR: i32 = -4;
def SEP_INITIAL_CAP: i32 = 8; def SEP_INITIAL_CAP: i32 = 8;
def SEP_COUNT_MAX: i32 = 2147483647; 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, // Classify a selected directory entry: 1 production, 2 test, 0 skipped,
// -1 @test outside *_test.ww, -2 non-regular source. // -1 @test outside *_test.ww, -2 non-regular source.
fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64, fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
@@ -575,10 +674,12 @@ fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
s.ptr = name; s.ptr = name;
s.len = nlen: i32; s.len = nlen: i32;
if (!strings.hassuffix(s, ".ww")) { return 0; }; if (!strings.hassuffix(s, ".ww")) { return 0; };
if (!sepsourcematchestarget(name, nlen)) { return 0; };
let istest: bool = strings.hassuffix(s, "_test.ww"); let istest: bool = strings.hassuffix(s, "_test.ww");
if (istest && variant == SEP_VARIANT_PRODUCTION) { return 0; }; if (istest && variant == SEP_VARIANT_PRODUCTION) { return 0; };
if (!istest && variant == SEP_VARIANT_EXTERNAL) { 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 fi: os.filestat;
let sr: (void | os.oserror) = os.lstat(&fi, pathstr(source)); let sr: (void | os.oserror) = os.lstat(&fi, pathstr(source));
let regular: bool = false; let regular: bool = false;
@@ -695,31 +796,11 @@ fn enumeratedir(dirpath: *u8, variant: i32,
let reclen: u64 = blo + (bhi * 256u64); let reclen: u64 = blo + (bhi * 256u64);
let nm: *u8 = buf.ptr + off + 19u64; let nm: *u8 = buf.ptr + off + 19u64;
let nl: u64 = cstrlen(nm); let nl: u64 = cstrlen(nm);
let cls: i32 = dirfileclass(dirpath, nm, nl, variant); if (nl > 3u64 && nm[0] != '.' && nm[0] != '_') {
if (cls == -1) { let view: str;
cerr("ww: "); view.ptr = nm;
cerr(pathstr(joinpath(dirpath, nm))); view.len = nl: i32;
cerr(": @test declaration outside *_test.ww\n"); if (strings.hassuffix(view, ".ww")) {
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 (n == SEP_COUNT_MAX) { if (n == SEP_COUNT_MAX) {
sepfailsize(); sepfailsize();
os.close(fd); os.close(fd);
@@ -730,11 +811,17 @@ fn enumeratedir(dirpath: *u8, variant: i32,
os.close(fd); os.close(fd);
return nil: **u8, -2; return nil: **u8, -2;
}; };
names[n] = full; let owned: *u8 = sepdupcstr(nm, nl);
nlens[n] = cstrlen(full); if (owned == nil) {
kinds[n] = cls; os.close(fd);
return nil: **u8, -2;
};
names[n] = owned;
nlens[n] = nl;
kinds[n] = 0;
n += 1; n += 1;
}; };
};
off += reclen; off += reclen;
}; };
r = os.getdents64(fd, buf.ptr, 8192u64); r = os.getdents64(fd, buf.ptr, 8192u64);
@@ -749,8 +836,69 @@ fn enumeratedir(dirpath: *u8, variant: i32,
return nil: **u8, -1; 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; 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) { for (i < n) {
let j: i32 = i; let j: i32 = i;
for (j > 0) { for (j > 0) {
@@ -4981,14 +5129,14 @@ fn validatecommandoutputpath(out: *u8) i32 = {
fn workdirstamptext(istest: i32, emitasm: i32) str = { fn workdirstamptext(istest: i32, emitasm: i32) str = {
if (istest != 0) { if (istest != 0) {
if (emitasm != 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) { 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 = { fn stampmatches(path: *u8, want: str) bool = {