ww: implement recursive local package patterns
This commit is contained in:
@@ -31,6 +31,8 @@ def SEP_LOCAL_IMPORT_PREFIX: str = "__wwlocal";
|
||||
let selfpath: *u8;
|
||||
let sepfatalallocation: bool;
|
||||
|
||||
@symbol("rt_envp") fn rawenvp() **u8;
|
||||
|
||||
// Tool-local (NOT a lib wrapper): messages are built from many
|
||||
// fragments and we route through os.write to avoid libc stdio.
|
||||
// .len replaces the error-prone hand-counted byte literals these sites
|
||||
@@ -77,6 +79,28 @@ fn cstrlen(p: *u8) u64 = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn rawenvvalue(name: str) *u8 = {
|
||||
let env: **u8 = rawenvp();
|
||||
let i: i32 = 0;
|
||||
for (env[i] != nil) {
|
||||
let entry: *u8 = env[i];
|
||||
let j: i32 = 0;
|
||||
for (j < name.len && entry[j] == name[j]) { j += 1; };
|
||||
if (j == name.len && entry[j] == '=': u8) {
|
||||
return entry + ((j + 1): u64);
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
fn clipathfits(command: str, flag: str, value: *u8) bool = {
|
||||
if (cstrlen(value) < os.PATH_MAX: u64) { return true; };
|
||||
cerr("ww "); cerr(command); cerr(": "); cerr(flag);
|
||||
cerr(" path is too long\n");
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bridges the driver's argv-style *u8 paths to lib/os entrypoints
|
||||
// (str post-task-#23).
|
||||
fn pathstr(p: *u8) str = {
|
||||
@@ -242,21 +266,39 @@ 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, rootidentity: *u8, adddot: bool) i32 = {
|
||||
let prog: *u8 = joinpathlit(selfdir, "wwtest");
|
||||
match (os.getenv("WW_WWTEST")) {
|
||||
case let p: str => {
|
||||
if (p.len != 0) { prog = owncstr(p); };
|
||||
};
|
||||
case void => void;
|
||||
targetindex: i32, resolved: *u8, rootidentity: *u8, adddot: bool,
|
||||
buildonly: bool) i32 = {
|
||||
let prog: *u8 = rawenvvalue("WW_WWTEST");
|
||||
if (prog == nil || prog[0u64] == 0u8) {
|
||||
prog = sepjoinpathlit(selfdir, "wwtest");
|
||||
if (prog == nil) { return 1; };
|
||||
};
|
||||
|
||||
let cap: i32 = argc - start + 8;
|
||||
let execargv: []*u8 = alloc([], cap: u64)!;
|
||||
execargv.len = cap;
|
||||
if (start < 0 || start > argc || argc - start > SEP_COUNT_MAX - 10) {
|
||||
sepfailsize();
|
||||
return 1;
|
||||
};
|
||||
let cap: i32 = argc - start + 10;
|
||||
let execargv: []*u8;
|
||||
let argvallocation: ([]*u8 | nomem) = sepallocptrs(cap);
|
||||
match (argvallocation) {
|
||||
case let value: []*u8 => { execargv = value; execargv.len = cap; };
|
||||
case nomem => {
|
||||
if (buildonly) {
|
||||
cerr("ww build: cannot allocate package coordinator arguments\n");
|
||||
} else {
|
||||
cerr("ww test: cannot allocate package coordinator arguments\n");
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
let n: i32 = 0;
|
||||
execargv[n] = prog; n += 1;
|
||||
execargv[n] = "package".ptr; n += 1;
|
||||
if (buildonly) {
|
||||
execargv[n] = "--ww-operation".ptr; n += 1;
|
||||
execargv[n] = "build".ptr; n += 1;
|
||||
};
|
||||
execargv[n] = "--ww-driver".ptr; n += 1;
|
||||
execargv[n] = selfpath; n += 1;
|
||||
if (rootidentity != nil) {
|
||||
@@ -278,14 +320,9 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
||||
if (adddot && !dotted) { execargv[n] = ".".ptr; n += 1; };
|
||||
execargv[n] = nil;
|
||||
|
||||
let env: []str = os.getenvs();
|
||||
let envp: []*u8 = alloc([], (env.len + 1): u64)!;
|
||||
envp.len = env.len + 1;
|
||||
i = 0;
|
||||
for (i < env.len) { envp[i] = owncstr(env[i]); i += 1; };
|
||||
envp[env.len] = nil;
|
||||
os.execve(pathstr(prog), execargv.ptr, envp.ptr);
|
||||
cerr("ww test: cannot exec package test coordinator\n");
|
||||
os.execve(pathstr(prog), execargv.ptr, rawenvp());
|
||||
if (buildonly) { cerr("ww build: cannot exec package coordinator\n"); }
|
||||
else { cerr("ww test: cannot exec package coordinator\n"); };
|
||||
return 1;
|
||||
};
|
||||
|
||||
@@ -534,6 +571,7 @@ fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
|
||||
// nlen<=3 guard kept: a bare ".ww" (len 3) is rejected here but
|
||||
// would pass strings.hassuffix(".ww"); preserves cstage parity.
|
||||
if (nlen <= 3u64) { return 0; };
|
||||
if (name[0] == '.' || name[0] == '_') { return 0; };
|
||||
let s: str;
|
||||
s.ptr = name;
|
||||
s.len = nlen: i32;
|
||||
@@ -545,13 +583,27 @@ fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64,
|
||||
let fi: os.filestat;
|
||||
let sr: (void | os.oserror) = os.lstat(&fi, pathstr(source));
|
||||
let regular: bool = false;
|
||||
let directory: bool = false;
|
||||
match (sr) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32;
|
||||
if (t == os.mode.REG: u32) { regular = true; };
|
||||
if (t == os.mode.REG: u32) { regular = true; }
|
||||
else if (t == os.mode.LINK: u32) {
|
||||
let target: os.filestat;
|
||||
match (os.stat(&target, pathstr(source))) {
|
||||
case void => {
|
||||
let tt: u32 = (target.mode: u32) & 61440u32;
|
||||
if (tt == os.mode.REG: u32) { regular = true; };
|
||||
if (tt == os.mode.DIR: u32) { directory = true; };
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
// go/build ignores a source-shaped symlink to a directory.
|
||||
if (directory) { return 0; };
|
||||
if (!regular) {
|
||||
cerr("ww: "); cerr(pathstr(source));
|
||||
cerr(": package source is not a regular file\n");
|
||||
@@ -4265,6 +4317,14 @@ fn validatepackageoutputpath(out: *u8) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn validatecommandoutputpath(out: *u8) i32 = {
|
||||
if (out == nil || cstrlen(out) + 1u64 > os.PATH_MAX: u64) {
|
||||
cerr("ww: command output path is too long\n");
|
||||
return -1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// The stamp pins the non-content build inputs a unit compare cannot see:
|
||||
// the -T/-S/root-action shape of the producer pass and the artifact protocol
|
||||
// revision (bump "fmt" when the unit/archive/commit format changes).
|
||||
@@ -4374,6 +4434,84 @@ fn invalidateworkdirunits(scratch: *u8) i32 = {
|
||||
return rc;
|
||||
};
|
||||
|
||||
type sepcreateddirs = struct {
|
||||
path: [4096]u8,
|
||||
offset: [2048]u16,
|
||||
n: i32,
|
||||
};
|
||||
|
||||
fn seprollbackdirs(created: *sepcreateddirs) void = {
|
||||
for (created.n > 0) {
|
||||
created.n -= 1;
|
||||
let at: u64 = created.offset[created.n]: u64;
|
||||
let saved: u8 = created.path[at];
|
||||
created.path[at] = 0u8;
|
||||
let ignored: i32 = os.rmdir(pathstr(&created.path[0]));
|
||||
created.path[at] = saved;
|
||||
};
|
||||
};
|
||||
|
||||
// Keep the prefixes created after semantic preflight separately from
|
||||
// pre-existing caller state so a later setup failure can reclaim only them.
|
||||
fn sepmkdirsrecord(path: *u8, mode: i32, created: *sepcreateddirs) i32 = {
|
||||
created.n = 0;
|
||||
let n: u64 = cstrlen(path);
|
||||
if (n == 0u64 || n >= os.PATH_MAX: u64) { return -1; };
|
||||
let buf: [4096]u8;
|
||||
bytecpy(&buf[0], path, n + 1u64);
|
||||
for (n > 1u64 && buf[n - 1u64] == 47u8) {
|
||||
n -= 1u64;
|
||||
buf[n] = 0u8;
|
||||
};
|
||||
bytecpy(&created.path[0], &buf[0], n + 1u64);
|
||||
let i: u64 = 0u64;
|
||||
if (buf[0] == 47u8) { i = 1u64; };
|
||||
for (i <= n) {
|
||||
if (buf[i] != 47u8 && buf[i] != 0u8) {
|
||||
i += 1u64;
|
||||
continue;
|
||||
};
|
||||
let saved: u8 = buf[i];
|
||||
buf[i] = 0u8;
|
||||
if (buf[0] != 0u8) {
|
||||
let fi: os.filestat;
|
||||
let missing: bool = false;
|
||||
match (os.stat(&fi, pathstr(&buf[0]))) {
|
||||
case void => {
|
||||
let typ: u32 = (fi.mode: u32) & 61440u32;
|
||||
if (typ != os.mode.DIR: u32) {
|
||||
buf[i] = saved;
|
||||
seprollbackdirs(created);
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => {
|
||||
if ((e: i64) == -2i64) { missing = true; }
|
||||
else {
|
||||
buf[i] = saved;
|
||||
seprollbackdirs(created);
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (missing) {
|
||||
if (created.n >= 2048
|
||||
|| os.mkdir(pathstr(&buf[0]), mode) != 0) {
|
||||
buf[i] = saved;
|
||||
seprollbackdirs(created);
|
||||
return -1;
|
||||
};
|
||||
created.offset[created.n] = i: u16;
|
||||
created.n += 1;
|
||||
};
|
||||
};
|
||||
buf[i] = saved;
|
||||
if (saved == 0u8) { break; };
|
||||
i += 1u64;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// cerrpath — the "ww: <head><path>\n" diagnostic shape shared by the
|
||||
// workdir error sites; byte-identical wording to the cstage twin's
|
||||
// fprintf(..., "%s", path) forms.
|
||||
@@ -4387,7 +4525,8 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
out: *u8, objstem: *u8, incs: *u8, lf: *lflags,
|
||||
publishpackage: i32, requirecommand: i32, istest: i32,
|
||||
products: *sepproduct, nproducts: i32, emitasm: i32,
|
||||
workdir: *u8, scratchout: **u8,
|
||||
workdir: *u8, createworkdir: bool, createoutputdir: *u8,
|
||||
scratchout: **u8,
|
||||
graphout: **sepgraph) i32 = {
|
||||
sepfatalallocation = false;
|
||||
if (nproducts < 1) { return 1; };
|
||||
@@ -4461,6 +4600,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
let effstem: *u8 = stem;
|
||||
if (objstem != nil) { effstem = objstem; };
|
||||
let warm: bool = false;
|
||||
let workdirexists: bool = false;
|
||||
if (workdir != nil) {
|
||||
if (workdir[0u64] != 0u8) { warm = true; };
|
||||
};
|
||||
@@ -4468,23 +4608,38 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
if (warm) {
|
||||
let wfi: os.filestat;
|
||||
let wok: bool = false;
|
||||
let missing: bool = false;
|
||||
match (os.stat(&wfi, pathstr(workdir))) {
|
||||
case void => {
|
||||
let wt: u32 = (wfi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (wt == os.mode.DIR: u32) { wok = true; };
|
||||
if (wt == os.mode.DIR: u32) {
|
||||
wok = true; workdirexists = true;
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
case let e: os.oserror => {
|
||||
if ((e: i64) == -2i64) { missing = true; };
|
||||
};
|
||||
if (!wok) {
|
||||
};
|
||||
if (!wok && !(createworkdir && missing)) {
|
||||
cerrpath("ww: workdir ", workdir,
|
||||
" is not a directory\n");
|
||||
return 1;
|
||||
};
|
||||
if (cstrlen(workdir) + 1u64 > os.PATH_MAX: u64) {
|
||||
cerr("ww: workdir path is too long\n");
|
||||
return 1;
|
||||
};
|
||||
// The workdir is caller-owned and persistent: no acquisition,
|
||||
// no refusal, and scratchout stays nil so the wrapper never
|
||||
// cleans it.
|
||||
scratch = workdir;
|
||||
} else {
|
||||
let suffix: u64 = ".sepwork".len: u64 + 1u64;
|
||||
if (suffix > os.PATH_MAX: u64
|
||||
|| cstrlen(effstem) > os.PATH_MAX: u64 - suffix) {
|
||||
cerr("ww: scratch path is too long\n");
|
||||
return 1;
|
||||
};
|
||||
scratch = sepappendlit(effstem, ".sepwork");
|
||||
if (scratch == nil) { return 1; };
|
||||
};
|
||||
@@ -4500,6 +4655,12 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
cerrpath("ww: cannot read driver identity ", selfpath, "\n");
|
||||
return 1;
|
||||
};
|
||||
let toolsuffix: u64 = "/.wwtool.stamp".len: u64 + 1u64;
|
||||
if (toolsuffix > os.PATH_MAX: u64
|
||||
|| cstrlen(scratch) > os.PATH_MAX: u64 - toolsuffix) {
|
||||
cerr("ww: workdir path is too long\n");
|
||||
return 1;
|
||||
};
|
||||
toolw = sepjoinpathlit(scratch, ".wwtool.ww");
|
||||
toolc = sepjoinpathlit(scratch, ".wwtool.w6c");
|
||||
toola = sepjoinpathlit(scratch, ".wwtool.w6a");
|
||||
@@ -4753,11 +4914,21 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
producti += 1;
|
||||
};
|
||||
};
|
||||
let rootpackage: bool = istest == 0
|
||||
producti = 0;
|
||||
for (producti < nproducts) {
|
||||
let root: i32 = products[producti].root;
|
||||
if (!g.pkg[root].failed && seprootiscommand(&g.pkg[root])
|
||||
&& validatecommandoutputpath(products[producti].out) < 0) {
|
||||
return 1;
|
||||
};
|
||||
producti += 1;
|
||||
};
|
||||
let rootpackage: bool = istest == 0 && nproducts == 1
|
||||
&& !g.pkg[products[0].root].failed
|
||||
&& !seprootiscommand(&g.pkg[products[0].root]);
|
||||
if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; };
|
||||
if (warm && sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
|
||||
if (warm && workdirexists
|
||||
&& sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
|
||||
let ci: i32 = 0;
|
||||
let order: []i32;
|
||||
let stack: []i32;
|
||||
@@ -4836,9 +5007,30 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
if (!viableproduct) { return 1; };
|
||||
// Source-derived resolution and contextual legality are complete before
|
||||
// coordinator completion markers or persistent vouchers are changed.
|
||||
let createdoutput: sepcreateddirs;
|
||||
let createdwork: sepcreateddirs;
|
||||
createdoutput.n = 0;
|
||||
createdwork.n = 0;
|
||||
if (createoutputdir != nil
|
||||
&& sepmkdirsrecord(createoutputdir, 448, &createdoutput) != 0) {
|
||||
cerrpath("ww: cannot create build output directory ",
|
||||
createoutputdir, "\n");
|
||||
return 1;
|
||||
};
|
||||
if (warm && !workdirexists) {
|
||||
if (!createworkdir
|
||||
|| sepmkdirsrecord(scratch, 448, &createdwork) != 0) {
|
||||
cerrpath("ww: workdir ", scratch, " is not a directory\n");
|
||||
seprollbackdirs(&createdoutput);
|
||||
return 1;
|
||||
};
|
||||
workdirexists = true;
|
||||
};
|
||||
if (!warm) {
|
||||
if (os.mkdir(pathstr(scratch), 493i32) != 0) {
|
||||
cerr("ww: cannot create scratch\n");
|
||||
cerrpath("ww: cannot create scratch ", scratch, "\n");
|
||||
seprollbackdirs(&createdwork);
|
||||
seprollbackdirs(&createdoutput);
|
||||
return 1;
|
||||
};
|
||||
// The wrapper owns only the directory this invocation acquired.
|
||||
@@ -5179,7 +5371,22 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
};
|
||||
};
|
||||
};
|
||||
if (emitasm != 0) { if (anyfailed) { return 1; }; return 0; };
|
||||
if (emitasm != 0) {
|
||||
let producti: i32 = 0;
|
||||
for (producti < nproducts) {
|
||||
let root: i32 = products[producti].root;
|
||||
if (g.pkg[root].failed) {
|
||||
anyfailed = true;
|
||||
} else if (recordproductstatus(products[producti].status) != 0) {
|
||||
cerr("ww: cannot record package-build product\n");
|
||||
g.pkg[root].failed = true;
|
||||
anyfailed = true;
|
||||
};
|
||||
producti += 1;
|
||||
};
|
||||
if (anyfailed) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
if (rootpackage) {
|
||||
let root: i32 = products[0].root;
|
||||
if (g.pkg[root].failed) { return 1; };
|
||||
@@ -5196,6 +5403,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
if (recordproductstatus(products[0].status) != 0) {
|
||||
cerr("ww: cannot record package-build product\n");
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -5220,6 +5431,15 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
producti += 1;
|
||||
continue;
|
||||
};
|
||||
if (istest == 0 && !seprootiscommand(&g.pkg[root])) {
|
||||
if (recordproductstatus(products[producti].status) != 0) {
|
||||
cerr("ww: cannot record package-build product\n");
|
||||
g.pkg[root].failed = true;
|
||||
anyfailed = true;
|
||||
};
|
||||
producti += 1;
|
||||
continue;
|
||||
};
|
||||
ci = 0;
|
||||
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
|
||||
let linkorder: []i32;
|
||||
@@ -5369,7 +5589,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
product.support = -1;
|
||||
let r: i32 = buildonesepimpl(selfdir, src, entryisdir, out, objstem,
|
||||
incs, lf, publishpackage, requirecommand, istest, &product, 1,
|
||||
emitasm, workdir, &scratch, &g);
|
||||
emitasm, workdir, false, nil, &scratch, &g);
|
||||
sepgraphfree(g);
|
||||
if (keepscratch == 0 && scratch != nil) {
|
||||
if (cstrendswithlit(scratch, ".sepwork")) {
|
||||
@@ -5398,20 +5618,21 @@ 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, rootidentity: *u8,
|
||||
incs: *u8, workdir: *u8, products: *sepproduct, nproducts: i32) i32 = {
|
||||
incs: *u8, workdir: *u8, products: *sepproduct, nproducts: i32,
|
||||
istest: i32, publishpackage: i32, lf: *lflags, emitasm: i32,
|
||||
createworkdir: bool, createoutputdir: *u8) 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 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, 0, 1,
|
||||
products, nproducts, 0, workdir, &scratch, &g);
|
||||
products[0].out, products[0].out, incs, lf,
|
||||
publishpackage, 0, istest,
|
||||
products, nproducts, emitasm, workdir, createworkdir,
|
||||
createoutputdir, &scratch, &g);
|
||||
sepgraphfree(g);
|
||||
return r;
|
||||
};
|
||||
@@ -5507,7 +5728,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||
};
|
||||
|
||||
fn writeusage(fd: i32) void = {
|
||||
let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-S] [-w DIR] [-I DIR] [-o FILE] [path] build a local package graph\n run [path] ... build then exec, passing extra args to the program\n test [-S -o STEM] [-w DIR] [options] [path] build/run tests; -S emits package asm\n version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then the source library for foo.ww or foo/\n lib/foo directory: build its package sources\n -o publishes a non-main archive FILE + FILE.wwi\n lib/... every package under lib, recursively (test only)\n . build the cwd's <basename>.ww\n";
|
||||
let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-S] [-w DIR] [-I DIR] [-o FILE] [path ...] build local package graphs\n run [path] ... build then exec, passing extra args to the program\n test [-S -o STEM] [-w DIR] [options] [path ...] build/run tests; -S emits package asm\n version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then the source library for foo.ww or foo/\n lib/foo directory: build its package sources\n -o publishes a non-main archive FILE + FILE.wwi\n lib/... every eligible package under lib, recursively\n . build the cwd's <basename>.ww\n";
|
||||
os.write(fd, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
@@ -5548,6 +5769,8 @@ fn defaultoutpath(src: *u8) *u8 = {
|
||||
|
||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let src: *u8 = nil;
|
||||
let srcindex: i32 = -1;
|
||||
let multipleroots: bool = false;
|
||||
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
|
||||
let workdir: *u8 = nil; // -w persistent package-artifact workdir
|
||||
let emitasm: i32 = 0;
|
||||
@@ -5564,18 +5787,19 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
cstrseal(incs.ptr, 0u64);
|
||||
|
||||
let maxlflags: i32 = 32;
|
||||
let libdirs: []*u8;
|
||||
if (!sepmakeptrs(maxlflags, &libdirs)) { return 1; };
|
||||
let libdirs: [32]*u8;
|
||||
let nlibdirs: i32 = 0;
|
||||
let libs: []*u8;
|
||||
if (!sepmakeptrs(maxlflags, &libs)) { return 1; };
|
||||
let libs: [32]*u8;
|
||||
let nlibs: i32 = 0;
|
||||
|
||||
let i: i32 = start;
|
||||
for (i < argc) {
|
||||
let p: *u8 = argv[i];
|
||||
if (p[0u64] == 45u8) { // '-'
|
||||
if (cstreqlit(p, "-S")) {
|
||||
if (cstreqlit(p, "--")) {
|
||||
multipleroots = true;
|
||||
break;
|
||||
} else { if (cstreqlit(p, "-S")) {
|
||||
emitasm = 1;
|
||||
} else { if (p[1u64] == 73u8) { // '-I'
|
||||
let dir: *u8 = nil;
|
||||
@@ -5632,17 +5856,18 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
libs[nlibs] = nm;
|
||||
nlibs += 1;
|
||||
} else { if (p[1u64] == 111u8) { // '-o'
|
||||
if (p[2u64] != 0u8) {
|
||||
outflag = p + 2u64;
|
||||
if (p[2u64] != 0u8) {
|
||||
outflag = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
cerr("ww build: -o needs an argument\n");
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
outflag = argv[i];
|
||||
};
|
||||
} else { if (p[1u64] == 119u8) { // '-w'
|
||||
outflag = argv[i];
|
||||
};
|
||||
if (!clipathfits("build", "-o", outflag)) { return 2; };
|
||||
} else { if (p[1u64] == 119u8) { // '-w'
|
||||
if (p[2u64] != 0u8) {
|
||||
workdir = p + 2u64;
|
||||
} else {
|
||||
@@ -5651,14 +5876,21 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
workdir = argv[i];
|
||||
};
|
||||
workdir = argv[i];
|
||||
};
|
||||
if (!clipathfits("build", "-w", workdir)) { return 2; };
|
||||
} else {
|
||||
cerr("ww build: unknown flag\n");
|
||||
return 2;
|
||||
}; }; }; }; }; };
|
||||
}; }; }; }; }; }; };
|
||||
} else {
|
||||
if (src == nil) { src = p; };
|
||||
if (src == nil) {
|
||||
src = p;
|
||||
srcindex = i;
|
||||
i += 1;
|
||||
if (i < argc) { multipleroots = true; };
|
||||
break;
|
||||
} else { multipleroots = true; break; };
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
@@ -5667,6 +5899,10 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let dot: [2]u8 = ['.': u8, 0u8];
|
||||
src = &dot[0];
|
||||
};
|
||||
if (multipleroots || strings.contains(pathstr(src), "...")) {
|
||||
return execpackagetests(selfdir, argv, argc, start, srcindex,
|
||||
nil, nil, false, true);
|
||||
};
|
||||
let requestedliteral: bool = false;
|
||||
let requestedstat: os.filestat;
|
||||
match (os.stat(&requestedstat, pathstr(src))) {
|
||||
@@ -5704,9 +5940,9 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
out = defaultoutpath(resolved);
|
||||
}; };
|
||||
let lf: lflags;
|
||||
lf.libdirs = libdirs.ptr;
|
||||
lf.libdirs = &libdirs[0];
|
||||
lf.nlibdirs = nlibdirs;
|
||||
lf.libs = libs.ptr;
|
||||
lf.libs = &libs[0];
|
||||
lf.nlibs = nlibs;
|
||||
let rootidentity: *u8 = nil;
|
||||
if (!requestedliteral && isdir != 0) { rootidentity = src; };
|
||||
@@ -6085,6 +6321,18 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let requestidentity: *u8 = nil;
|
||||
let products: []sepproduct;
|
||||
let packageopts: bool = false;
|
||||
let packagebuild: bool = false;
|
||||
let packagepublish: bool = false;
|
||||
let packagecreateworkdir: bool = false;
|
||||
let packagecreateoutputdir: *u8 = nil;
|
||||
let maxpackagelflags: i32 = 32;
|
||||
let packagelibdirs: [32]*u8;
|
||||
let packagelibs: [32]*u8;
|
||||
let packagelinks: lflags;
|
||||
packagelinks.libdirs = &packagelibdirs[0];
|
||||
packagelinks.nlibdirs = 0;
|
||||
packagelinks.libs = &packagelibs[0];
|
||||
packagelinks.nlibs = 0;
|
||||
let afterdash: bool = false;
|
||||
let i: i32 = start;
|
||||
for (i < argc) {
|
||||
@@ -6105,6 +6353,44 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
i += 2;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-package-build")) {
|
||||
if (packagebuild) {
|
||||
cerr("ww test: invalid --ww-package-build\n");
|
||||
return 2;
|
||||
};
|
||||
packagebuild = true;
|
||||
compileonly = 1;
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-package-publish")) {
|
||||
if (packagepublish) {
|
||||
cerr("ww test: invalid --ww-package-publish\n");
|
||||
return 2;
|
||||
};
|
||||
packagepublish = true;
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-create-workdir")) {
|
||||
if (packagecreateworkdir) {
|
||||
cerr("ww test: invalid --ww-create-workdir\n");
|
||||
return 2;
|
||||
};
|
||||
packagecreateworkdir = true;
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-create-output-dir")) {
|
||||
if (i + 1 >= argc || packagecreateoutputdir != nil
|
||||
|| argv[i + 1][0u64] == 0u8) {
|
||||
cerr("ww test: invalid --ww-create-output-dir\n");
|
||||
return 2;
|
||||
};
|
||||
packagecreateoutputdir = argv[i + 1];
|
||||
i += 2;
|
||||
continue;
|
||||
};
|
||||
if (cstreqlit(p, "--ww-package-test")) {
|
||||
if (i + 5 >= argc) {
|
||||
cerr("ww test: --ww-package-test needs kind, package, directory, output, and status\n");
|
||||
@@ -6178,9 +6464,49 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
};
|
||||
if (cstreqlit(p, "-c")) { compileonly = 1; i += 1; continue; };
|
||||
if (cstreqlit(p, "-S")) { emitasm = 1; i += 1; continue; };
|
||||
if (cstreqlit(p, "-list")) {
|
||||
if (!packagebuild && cstreqlit(p, "-list")) {
|
||||
packageopts = true; i += 1; continue;
|
||||
};
|
||||
if (packagebuild && p[1u64] == 76u8) { // '-L'
|
||||
let dir: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
dir = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
cerr("ww test: -L needs an argument\n");
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
dir = argv[i];
|
||||
};
|
||||
if (packagelinks.nlibdirs >= maxpackagelflags) {
|
||||
cerr("ww test: too many -L\n");
|
||||
return 2;
|
||||
};
|
||||
packagelibdirs[packagelinks.nlibdirs] = dir;
|
||||
packagelinks.nlibdirs += 1;
|
||||
i += 1; continue;
|
||||
};
|
||||
if (packagebuild && p[1u64] == 108u8) { // '-l'
|
||||
let name: *u8 = nil;
|
||||
if (p[2u64] != 0u8) {
|
||||
name = p + 2u64;
|
||||
} else {
|
||||
if (i + 1 >= argc) {
|
||||
cerr("ww test: -l needs an argument\n");
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
name = argv[i];
|
||||
};
|
||||
if (packagelinks.nlibs >= maxpackagelflags) {
|
||||
cerr("ww test: too many -l\n");
|
||||
return 2;
|
||||
};
|
||||
packagelibs[packagelinks.nlibs] = name;
|
||||
packagelinks.nlibs += 1;
|
||||
i += 1; continue;
|
||||
};
|
||||
if (cstreqlit(p, "-j") || cstreqlit(p, "-run")
|
||||
|| cstreqlit(p, "-filter")) {
|
||||
if (i + 1 >= argc) {
|
||||
@@ -6203,9 +6529,10 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
outstem = argv[i];
|
||||
};
|
||||
i += 1; continue;
|
||||
outstem = argv[i];
|
||||
};
|
||||
if (!clipathfits("test", "-o", outstem)) { return 2; };
|
||||
i += 1; continue;
|
||||
};
|
||||
if (p[1u64] == 119u8) { // '-w'
|
||||
if (p[2u64] != 0u8) {
|
||||
@@ -6216,8 +6543,9 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return 2;
|
||||
};
|
||||
i += 1;
|
||||
workdir = argv[i];
|
||||
};
|
||||
workdir = argv[i];
|
||||
};
|
||||
if (!clipathfits("test", "-w", workdir)) { return 2; };
|
||||
i += 1; continue;
|
||||
};
|
||||
cerr("ww test: unknown flag\n"); return 2;
|
||||
@@ -6231,7 +6559,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let dot: [2]u8 = ['.': u8, 0u8];
|
||||
target = &dot[0];
|
||||
};
|
||||
if (emitasm != 0 && outstem == nil) {
|
||||
if (emitasm != 0 && outstem == nil && products.len == 0) {
|
||||
cerr("ww test: -S needs -o\n");
|
||||
return 2;
|
||||
};
|
||||
@@ -6267,22 +6595,82 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
cerr("ww test: package-test variant rejects package options\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagebuild && products.len == 0) {
|
||||
cerr("ww test: --ww-package-build needs products\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagepublish && (!packagebuild || products.len != 1)) {
|
||||
cerr("ww test: invalid --ww-package-publish\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagecreateoutputdir != nil && !packagebuild) {
|
||||
cerr("ww test: invalid private directory creation\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagecreateworkdir && products.len == 0) {
|
||||
cerr("ww test: invalid private directory creation\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagecreateworkdir && workdir == nil) {
|
||||
cerr("ww test: --ww-create-workdir needs -w\n");
|
||||
return 2;
|
||||
};
|
||||
if (!packagebuild && (packagelinks.nlibdirs != 0
|
||||
|| packagelinks.nlibs != 0)) {
|
||||
cerr("ww test: unknown flag\n");
|
||||
return 2;
|
||||
};
|
||||
if (packagebuild) {
|
||||
producti = 0;
|
||||
for (producti < products.len) {
|
||||
if (products[producti].variant != SEP_VARIANT_PRODUCTION) {
|
||||
cerr("ww test: package-build products must be production\n");
|
||||
return 2;
|
||||
};
|
||||
producti += 1;
|
||||
};
|
||||
};
|
||||
if (patarg != nil) {
|
||||
let first: os.filestat;
|
||||
let firstregular: bool = false;
|
||||
let firstfound: bool = false;
|
||||
match (os.stat(&first, pathstr(target))) {
|
||||
case void => {
|
||||
firstfound = true;
|
||||
firstregular = (((first.mode: u32) & 61440u32)
|
||||
== (os.mode.REG: u32));
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (!firstregular) {
|
||||
let firstresolved: *u8 = nil;
|
||||
let firstisdir: i32 = 0;
|
||||
if (!firstfound) {
|
||||
firstresolved = resolvemodule(selfdir, target, incs.ptr,
|
||||
&firstisdir);
|
||||
};
|
||||
if (firstfound || firstresolved == nil || firstisdir != 0) {
|
||||
let replacement: *u8 = nil;
|
||||
let identity: *u8 = requestidentity;
|
||||
if (firstisdir != 0) {
|
||||
replacement = firstresolved;
|
||||
identity = target;
|
||||
};
|
||||
return execpackagetests(selfdir, argv, argc, start,
|
||||
targetindex, replacement, identity, false, false);
|
||||
};
|
||||
// A logical import resolving to one source retains patarg as its
|
||||
// historical test-name filter and follows the single-file path.
|
||||
};
|
||||
};
|
||||
if (products.len != 0 && outstem != nil) {
|
||||
cerr("ww test: package-test products reject -o\n");
|
||||
return 2;
|
||||
};
|
||||
|
||||
// Go's ./... form: a trailing "..." element is a package-tree
|
||||
// request for the coordinator, never a literal path — recognized
|
||||
// before stat, with the directory-mode rejects.
|
||||
let tlen: u64 = cstrlen(target);
|
||||
let istree: bool = cstreqlit(target, "...");
|
||||
if (!istree && tlen >= 4u64) {
|
||||
istree = target[tlen - 4u64] == '/'
|
||||
&& target[tlen - 3u64] == '.'
|
||||
&& target[tlen - 2u64] == '.'
|
||||
&& target[tlen - 1u64] == '.';
|
||||
};
|
||||
// A local spelling containing "..." enters request selection before
|
||||
// literal-path or logical-import resolution.
|
||||
let istree: bool = strings.contains(pathstr(target), "...");
|
||||
if (istree) {
|
||||
if (products.len != 0) {
|
||||
cerr("ww test: package-test variant needs one directory\n");
|
||||
@@ -6298,14 +6686,10 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
cerr("ww test: -o needs -c for a package target\n");
|
||||
return 2;
|
||||
};
|
||||
if (patarg != nil) {
|
||||
cerr("ww test: pattern needs a single test file\n");
|
||||
return 2;
|
||||
};
|
||||
// -w forwards: the coordinator keys one persistent driver workdir
|
||||
// for the complete selected test request.
|
||||
// -w forwards one caller-owned semantic-action store shared by
|
||||
// the complete selected package universe.
|
||||
return execpackagetests(selfdir, argv, argc, start,
|
||||
targetindex, nil, nil, false);
|
||||
targetindex, nil, nil, false, false);
|
||||
};
|
||||
|
||||
let resolved: *u8 = target;
|
||||
@@ -6345,7 +6729,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
emitasm, outstem, workdir, patarg);
|
||||
};
|
||||
|
||||
if (emitasm != 0) {
|
||||
if (emitasm != 0 && products.len == 0) {
|
||||
cerr("ww test: -S needs a single test file\n");
|
||||
return 2;
|
||||
};
|
||||
@@ -6353,23 +6737,25 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
cerr("ww test: -o needs -c for a package target\n");
|
||||
return 2;
|
||||
};
|
||||
if (patarg != nil) {
|
||||
cerr("ww test: pattern needs a single test file\n");
|
||||
return 2;
|
||||
};
|
||||
if (products.len != 0) {
|
||||
if (compileonly == 0) {
|
||||
cerr("ww test: package-test products need -c\n");
|
||||
return 2;
|
||||
};
|
||||
let packagemode: i32 = 1;
|
||||
if (packagebuild) { packagemode = 0; };
|
||||
let publishmode: i32 = 0;
|
||||
if (packagepublish) { publishmode = 1; };
|
||||
return buildpackagetests(selfdir, resolved, rootidentity,
|
||||
incs.ptr, workdir,
|
||||
products.ptr, products.len);
|
||||
products.ptr, products.len, packagemode, publishmode,
|
||||
&packagelinks, emitasm, packagecreateworkdir,
|
||||
packagecreateoutputdir);
|
||||
};
|
||||
let replacement: *u8 = nil;
|
||||
if (resolved != target) { replacement = resolved; };
|
||||
return execpackagetests(selfdir, argv, argc, start, targetindex,
|
||||
replacement, rootidentity, targetindex < 0);
|
||||
replacement, rootidentity, targetindex < 0, false);
|
||||
};
|
||||
|
||||
export fn main(argc: i32, argv: **u8) i32 = {
|
||||
|
||||
Reference in New Issue
Block a user