ww: build intermediates follow -o output stem, not the source dir (test-perf T3a)
build_one/buildone gain an explicit objstem: 'ww build -o X' derives
main.{combined.ww,s,o} beside X; 'ww run' uses its per-pid /tmp stem;
default no--o stays next-to-source (load-bearing for the make regen of
tracked combined.ww and the freshness gate). Realizes the redirect TODO
in 995_self_rebuild.c. Kills the concurrent-test torn-read race on
selfhost/cmd/<tool>/main.* (the 991 transient). 993/995/949 pass -o
into their per-pid workdirs; 949's '-o /dev/null' relied on the old
driver ignoring -o (audited: the only live driver case). Atomic
combined write (os.rename) deferred to its own commit - lib/os lacks
rename and that addition is an import-floor regen.
This commit is contained in:
@@ -375,9 +375,15 @@ expand(FILE *out, const char *path, struct ImportSet *visited,
|
|||||||
fclose(in);
|
fclose(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* objstem (when non-NULL/non-empty) redirects the .s/.o/.combined.ww
|
||||||
|
* side files to live beside the build's OUTPUT instead of next to the
|
||||||
|
* source (task #15/T3). `ww run` and `ww build -o` pass it so concurrent
|
||||||
|
* builds never share the next-to-source fixed paths; the default
|
||||||
|
* (objstem == NULL) keeps the old next-to-source layout, load-bearing
|
||||||
|
* for make's tracked-combined.ww regen + the #110 freshness gate. */
|
||||||
static int
|
static int
|
||||||
build_one(const char *src, int entry_is_dir, const char *out,
|
build_one(const char *src, int entry_is_dir, const char *out,
|
||||||
const char *extra_includes, const char *extra_libs,
|
const char *objstem, const char *extra_includes, const char *extra_libs,
|
||||||
const char *extra_libdirs)
|
const char *extra_libdirs)
|
||||||
{
|
{
|
||||||
const char *c6 = toolpath("WW_W6C", "w6c");
|
const char *c6 = toolpath("WW_W6C", "w6c");
|
||||||
@@ -447,10 +453,11 @@ build_one(const char *src, int entry_is_dir, const char *out,
|
|||||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *ostem = (objstem && objstem[0]) ? objstem : stem;
|
||||||
char asmf[1024], obj[1024], combined[1024];
|
char asmf[1024], obj[1024], combined[1024];
|
||||||
snprintf(asmf, sizeof asmf, "%s.s", stem);
|
snprintf(asmf, sizeof asmf, "%s.s", ostem);
|
||||||
snprintf(obj, sizeof obj, "%s.o", stem);
|
snprintf(obj, sizeof obj, "%s.o", ostem);
|
||||||
snprintf(combined, sizeof combined, "%s.combined.ww", stem);
|
snprintf(combined, sizeof combined, "%s.combined.ww", ostem);
|
||||||
|
|
||||||
/* Resolve imports by concatenating sources into a temp file. The
|
/* Resolve imports by concatenating sources into a temp file. The
|
||||||
* compiler then sees one flat source. Dir entry → enumerate the
|
* compiler then sees one flat source. Dir entry → enumerate the
|
||||||
@@ -629,6 +636,7 @@ parse_build_flags(int argc, char **argv,
|
|||||||
char *incs, size_t incsz,
|
char *incs, size_t incsz,
|
||||||
char *libdirs, size_t libdirsz,
|
char *libdirs, size_t libdirsz,
|
||||||
char *libs, size_t libsz,
|
char *libs, size_t libsz,
|
||||||
|
char *outpath, size_t outsz,
|
||||||
const char **src_out)
|
const char **src_out)
|
||||||
{
|
{
|
||||||
*src_out = NULL;
|
*src_out = NULL;
|
||||||
@@ -658,6 +666,10 @@ parse_build_flags(int argc, char **argv,
|
|||||||
size_t n = strlen(incs);
|
size_t n = strlen(incs);
|
||||||
snprintf(incs + n, incsz - n,
|
snprintf(incs + n, incsz - n,
|
||||||
"%s%s", n ? ":" : "", argv[i] + 2);
|
"%s%s", n ? ":" : "", argv[i] + 2);
|
||||||
|
} else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
|
||||||
|
snprintf(outpath, outsz, "%s", argv[++i]);
|
||||||
|
} else if (strncmp(argv[i], "-o", 2) == 0 && argv[i][2]) {
|
||||||
|
snprintf(outpath, outsz, "%s", argv[i] + 2);
|
||||||
} else if (*src_out == NULL) {
|
} else if (*src_out == NULL) {
|
||||||
*src_out = argv[i];
|
*src_out = argv[i];
|
||||||
} else {
|
} else {
|
||||||
@@ -674,8 +686,10 @@ do_build(int argc, char **argv)
|
|||||||
char libs[2048] = {0};
|
char libs[2048] = {0};
|
||||||
char libdirs[2048] = {0};
|
char libdirs[2048] = {0};
|
||||||
char incs[2048] = {0};
|
char incs[2048] = {0};
|
||||||
|
char outflag[1024] = {0};
|
||||||
parse_build_flags(argc, argv, incs, sizeof incs,
|
parse_build_flags(argc, argv, incs, sizeof incs,
|
||||||
libdirs, sizeof libdirs, libs, sizeof libs, &src);
|
libdirs, sizeof libdirs, libs, sizeof libs,
|
||||||
|
outflag, sizeof outflag, &src);
|
||||||
if (src == NULL) src = "."; /* default: build cwd */
|
if (src == NULL) src = "."; /* default: build cwd */
|
||||||
char resolved[1024];
|
char resolved[1024];
|
||||||
int is_dir = 0;
|
int is_dir = 0;
|
||||||
@@ -684,7 +698,13 @@ do_build(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
char out[1024];
|
char out[1024];
|
||||||
if (is_dir) {
|
const char *objstem = NULL;
|
||||||
|
if (outflag[0]) {
|
||||||
|
/* -o sets both the binary path and the intermediate stem so
|
||||||
|
* artifacts land beside the requested output (T3). */
|
||||||
|
snprintf(out, sizeof out, "%s", outflag);
|
||||||
|
objstem = out;
|
||||||
|
} else if (is_dir) {
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
snprintf(tmp, sizeof tmp, "%s", resolved);
|
snprintf(tmp, sizeof tmp, "%s", resolved);
|
||||||
size_t n = strlen(tmp);
|
size_t n = strlen(tmp);
|
||||||
@@ -694,7 +714,7 @@ do_build(int argc, char **argv)
|
|||||||
} else {
|
} else {
|
||||||
basename_no_ext(resolved, out, sizeof out);
|
basename_no_ext(resolved, out, sizeof out);
|
||||||
}
|
}
|
||||||
return build_one(resolved, is_dir, out, incs, libs, libdirs);
|
return build_one(resolved, is_dir, out, objstem, incs, libs, libdirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -704,8 +724,10 @@ do_run(int argc, char **argv)
|
|||||||
char libs[2048] = {0};
|
char libs[2048] = {0};
|
||||||
char libdirs[2048] = {0};
|
char libdirs[2048] = {0};
|
||||||
char incs[2048] = {0};
|
char incs[2048] = {0};
|
||||||
|
char outflag[1024] = {0}; /* -o accepted+ignored: run always uses the temp */
|
||||||
int next = parse_build_flags(argc, argv, incs, sizeof incs,
|
int next = parse_build_flags(argc, argv, incs, sizeof incs,
|
||||||
libdirs, sizeof libdirs, libs, sizeof libs, &src);
|
libdirs, sizeof libdirs, libs, sizeof libs,
|
||||||
|
outflag, sizeof outflag, &src);
|
||||||
if (src == NULL) src = ".";
|
if (src == NULL) src = ".";
|
||||||
char resolved[1024];
|
char resolved[1024];
|
||||||
int is_dir = 0;
|
int is_dir = 0;
|
||||||
@@ -715,7 +737,9 @@ do_run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
snprintf(tmp, sizeof tmp, "/tmp/ww_run_%d", getpid());
|
snprintf(tmp, sizeof tmp, "/tmp/ww_run_%d", getpid());
|
||||||
if (build_one(resolved, is_dir, tmp, incs, libs, libdirs) != 0)
|
/* objstem = tmp → intermediates land at /tmp/ww_run_<pid>.{s,o,
|
||||||
|
* combined.ww}, never next to the source (T3). */
|
||||||
|
if (build_one(resolved, is_dir, tmp, tmp, incs, libs, libdirs) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
/* exec the built binary with any trailing argv as its argv. */
|
/* exec the built binary with any trailing argv as its argv. */
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@@ -754,7 +778,7 @@ do_test(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid());
|
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid());
|
||||||
if (build_one(resolved, is_dir, tmp, "", "", "") != 0) return 1;
|
if (build_one(resolved, is_dir, tmp, NULL, "", "", "") != 0) return 1;
|
||||||
int rc = run(tmp);
|
int rc = run(tmp);
|
||||||
unlink(tmp);
|
unlink(tmp);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -763,7 +787,7 @@ do_test(int argc, char **argv)
|
|||||||
/* single .ww file — build+run it. */
|
/* single .ww file — build+run it. */
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid());
|
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid());
|
||||||
if (build_one(target, 0, tmp, "", "", "") != 0) return 1;
|
if (build_one(target, 0, tmp, NULL, "", "", "") != 0) return 1;
|
||||||
int rc = run(tmp);
|
int rc = run(tmp);
|
||||||
unlink(tmp);
|
unlink(tmp);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -789,7 +813,7 @@ do_test(int argc, char **argv)
|
|||||||
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d_%d", getpid(), i);
|
snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d_%d", getpid(), i);
|
||||||
const char *label = strrchr(files[i], '/');
|
const char *label = strrchr(files[i], '/');
|
||||||
label = label ? label + 1 : files[i];
|
label = label ? label + 1 : files[i];
|
||||||
int rc = build_one(files[i], 0, tmp, target, "", "");
|
int rc = build_one(files[i], 0, tmp, NULL, target, "", "");
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
fprintf(stderr, "FAIL %s (build)\n", label);
|
fprintf(stderr, "FAIL %s (build)\n", label);
|
||||||
fail++;
|
fail++;
|
||||||
|
|||||||
@@ -3581,8 +3581,11 @@ fn appendlit(stem: *u8, suffix: str) *u8 = {
|
|||||||
return buf.ptr;
|
return buf.ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// linker flags bundled as a struct so buildone stays at the wwstage
|
// linker flags (-L<dir>, -l<name>) grouped as one struct so buildone's
|
||||||
// w6c's 6-argument calling-convention limit.
|
// param list stays readable. (The earlier note here claimed a 6-argument
|
||||||
|
// wwstage calling-convention cap; that is stale — emittuplerowrelocs in
|
||||||
|
// cgen.ww takes 7 params and self-compiles green, and buildone itself now
|
||||||
|
// takes 7.)
|
||||||
type lflags = struct {
|
type lflags = struct {
|
||||||
libdirs: **u8,
|
libdirs: **u8,
|
||||||
nlibdirs: i32,
|
nlibdirs: i32,
|
||||||
@@ -3597,6 +3600,13 @@ type lflags = struct {
|
|||||||
// src: NUL-terminated entry path (file or directory).
|
// src: NUL-terminated entry path (file or directory).
|
||||||
// entryisdir: non-zero when src is a module directory.
|
// entryisdir: non-zero when src is a module directory.
|
||||||
// out: NUL-terminated desired output path
|
// out: NUL-terminated desired output path
|
||||||
|
// objstem: when non-nil, redirects the .s/.o/.combined.ww side
|
||||||
|
// files to live beside this stem instead of next to the
|
||||||
|
// source (T3 / task #15). `ww run` and `ww build -o` pass
|
||||||
|
// it so concurrent builds never share next-to-source fixed
|
||||||
|
// paths; nil keeps the old next-to-source layout (the make
|
||||||
|
// tracked-combined.ww regen + #110 gate depend on it). Twin
|
||||||
|
// of cmd/ww/main.c build_one's objstem.
|
||||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||||
//
|
//
|
||||||
@@ -3604,7 +3614,7 @@ type lflags = struct {
|
|||||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
// 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
|
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||||
// pipelines to byte-identical output on a corpus.
|
// pipelines to byte-identical output on a corpus.
|
||||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, objstem: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||||
@@ -3687,9 +3697,11 @@ fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *l
|
|||||||
} else {
|
} else {
|
||||||
makestem(stem.ptr, src);
|
makestem(stem.ptr, src);
|
||||||
};
|
};
|
||||||
let asmf: *u8 = appendlit(stem.ptr, ".s");
|
let effstem: *u8 = stem.ptr;
|
||||||
let objf: *u8 = appendlit(stem.ptr, ".o");
|
if (objstem != nil) { effstem = objstem; };
|
||||||
let combined: *u8 = appendlit(stem.ptr, ".combined.ww");
|
let asmf: *u8 = appendlit(effstem, ".s");
|
||||||
|
let objf: *u8 = appendlit(effstem, ".o");
|
||||||
|
let combined: *u8 = appendlit(effstem, ".combined.ww");
|
||||||
|
|
||||||
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
||||||
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
@@ -3934,6 +3946,7 @@ fn defaultoutpath(src: *u8) *u8 = {
|
|||||||
|
|
||||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
|
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
|
||||||
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
||||||
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
||||||
let incoff: u64 = 0u64;
|
let incoff: u64 = 0u64;
|
||||||
@@ -4005,10 +4018,21 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
libs[nlibs] = nm;
|
libs[nlibs] = nm;
|
||||||
nlibs += 1;
|
nlibs += 1;
|
||||||
|
} else { if (p[1u64] == 111u8) { // '-o'
|
||||||
|
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 {
|
} else {
|
||||||
cerr("ww build: unknown flag\n");
|
cerr("ww build: unknown flag\n");
|
||||||
return 2;
|
return 2;
|
||||||
}; }; };
|
}; }; }; };
|
||||||
} else {
|
} else {
|
||||||
if (src == nil) { src = p; };
|
if (src == nil) { src = p; };
|
||||||
};
|
};
|
||||||
@@ -4027,7 +4051,13 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let out: *u8 = nil;
|
let out: *u8 = nil;
|
||||||
if (isdir != 0) {
|
let objstem: *u8 = nil;
|
||||||
|
if (outflag != nil) {
|
||||||
|
// -o sets both the binary path and the intermediate stem so
|
||||||
|
// artifacts land beside the requested output (T3).
|
||||||
|
out = outflag;
|
||||||
|
objstem = outflag;
|
||||||
|
} else { if (isdir != 0) {
|
||||||
let rlen: u64 = cstrlen(resolved);
|
let rlen: u64 = cstrlen(resolved);
|
||||||
for (rlen > 1u64) {
|
for (rlen > 1u64) {
|
||||||
if (resolved[rlen - 1u64] != 47u8) { break; };
|
if (resolved[rlen - 1u64] != 47u8) { break; };
|
||||||
@@ -4043,13 +4073,13 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
cstrseal(out, off);
|
cstrseal(out, off);
|
||||||
} else {
|
} else {
|
||||||
out = defaultoutpath(resolved);
|
out = defaultoutpath(resolved);
|
||||||
};
|
}; };
|
||||||
let lf: lflags;
|
let lf: lflags;
|
||||||
lf.libdirs = libdirs.ptr;
|
lf.libdirs = libdirs.ptr;
|
||||||
lf.nlibdirs = nlibdirs;
|
lf.nlibdirs = nlibdirs;
|
||||||
lf.libs = libs.ptr;
|
lf.libs = libs.ptr;
|
||||||
lf.nlibs = nlibs;
|
lf.nlibs = nlibs;
|
||||||
return buildone(selfdir, resolved, isdir, out, incs.ptr, &lf);
|
return buildone(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||||
@@ -4158,10 +4188,20 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
libs[nlibs] = nm;
|
libs[nlibs] = nm;
|
||||||
nlibs += 1;
|
nlibs += 1;
|
||||||
|
} else { if (p[1u64] == 111u8) { // '-o'
|
||||||
|
// run always execs the temp binary; -o is accepted+
|
||||||
|
// ignored, mirroring the C driver's shared flag parser.
|
||||||
|
if (p[2u64] == 0u8) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
cerr("ww run: -o needs an argument\n");
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
cerr("ww run: unknown flag\n");
|
cerr("ww run: unknown flag\n");
|
||||||
return 2;
|
return 2;
|
||||||
}; }; };
|
}; }; }; };
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
if (src == nil) {
|
if (src == nil) {
|
||||||
@@ -4193,7 +4233,9 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
lf.nlibdirs = nlibdirs;
|
lf.nlibdirs = nlibdirs;
|
||||||
lf.libs = libs.ptr;
|
lf.libs = libs.ptr;
|
||||||
lf.nlibs = nlibs;
|
lf.nlibs = nlibs;
|
||||||
if (buildone(selfdir, resolved, isdir, tmp.ptr, incs.ptr, &lf) != 0) {
|
// objstem = tmp → intermediates at /tmp/ww_run_<pid>.{s,o,combined.ww},
|
||||||
|
// never next to the source (T3).
|
||||||
|
if (buildone(selfdir, resolved, isdir, tmp.ptr, tmp.ptr, incs.ptr, &lf) != 0) {
|
||||||
os.remove(pathstr(tmp.ptr));
|
os.remove(pathstr(tmp.ptr));
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -4227,7 +4269,7 @@ fn runsingletest(selfdir: *u8, src: *u8) i32 = {
|
|||||||
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
tmp.len = os.PATH_MAX;
|
tmp.len = os.PATH_MAX;
|
||||||
makeruntmp(tmp.ptr);
|
makeruntmp(tmp.ptr);
|
||||||
if (buildone(selfdir, src, 0, tmp.ptr, "\0".ptr, nil) != 0) {
|
if (buildone(selfdir, src, 0, tmp.ptr, nil, "\0".ptr, nil) != 0) {
|
||||||
os.remove(pathstr(tmp.ptr));
|
os.remove(pathstr(tmp.ptr));
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -4282,7 +4324,7 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
|||||||
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
tmp.len = os.PATH_MAX;
|
tmp.len = os.PATH_MAX;
|
||||||
makeruntmp(tmp.ptr);
|
makeruntmp(tmp.ptr);
|
||||||
let bres: i32 = buildone(selfdir, path.ptr, 0, tmp.ptr, tincs.ptr, nil);
|
let bres: i32 = buildone(selfdir, path.ptr, 0, tmp.ptr, nil, tincs.ptr, nil);
|
||||||
if (bres != 0) {
|
if (bres != 0) {
|
||||||
fail += 1;
|
fail += 1;
|
||||||
cerr("FAIL ");
|
cerr("FAIL ");
|
||||||
|
|||||||
@@ -795,8 +795,11 @@ fn appendlit(stem: *u8, suffix: str) *u8 = {
|
|||||||
return buf.ptr;
|
return buf.ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// linker flags bundled as a struct so buildone stays at the wwstage
|
// linker flags (-L<dir>, -l<name>) grouped as one struct so buildone's
|
||||||
// w6c's 6-argument calling-convention limit.
|
// param list stays readable. (The earlier note here claimed a 6-argument
|
||||||
|
// wwstage calling-convention cap; that is stale — emittuplerowrelocs in
|
||||||
|
// cgen.ww takes 7 params and self-compiles green, and buildone itself now
|
||||||
|
// takes 7.)
|
||||||
type lflags = struct {
|
type lflags = struct {
|
||||||
libdirs: **u8,
|
libdirs: **u8,
|
||||||
nlibdirs: i32,
|
nlibdirs: i32,
|
||||||
@@ -811,6 +814,13 @@ type lflags = struct {
|
|||||||
// src: NUL-terminated entry path (file or directory).
|
// src: NUL-terminated entry path (file or directory).
|
||||||
// entryisdir: non-zero when src is a module directory.
|
// entryisdir: non-zero when src is a module directory.
|
||||||
// out: NUL-terminated desired output path
|
// out: NUL-terminated desired output path
|
||||||
|
// objstem: when non-nil, redirects the .s/.o/.combined.ww side
|
||||||
|
// files to live beside this stem instead of next to the
|
||||||
|
// source (T3 / task #15). `ww run` and `ww build -o` pass
|
||||||
|
// it so concurrent builds never share next-to-source fixed
|
||||||
|
// paths; nil keeps the old next-to-source layout (the make
|
||||||
|
// tracked-combined.ww regen + #110 gate depend on it). Twin
|
||||||
|
// of cmd/ww/main.c build_one's objstem.
|
||||||
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
// incs: NUL-terminated colon-list of -I dirs (may be empty)
|
||||||
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
// lf: extra linker flags (-L<dir>, -l<name>); may be nil
|
||||||
//
|
//
|
||||||
@@ -818,7 +828,7 @@ type lflags = struct {
|
|||||||
// touches no C-built code at runtime. The C `ww` driver in cmd/ww/
|
// 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
|
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||||
// pipelines to byte-identical output on a corpus.
|
// pipelines to byte-identical output on a corpus.
|
||||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, objstem: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||||
@@ -901,9 +911,11 @@ fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *l
|
|||||||
} else {
|
} else {
|
||||||
makestem(stem.ptr, src);
|
makestem(stem.ptr, src);
|
||||||
};
|
};
|
||||||
let asmf: *u8 = appendlit(stem.ptr, ".s");
|
let effstem: *u8 = stem.ptr;
|
||||||
let objf: *u8 = appendlit(stem.ptr, ".o");
|
if (objstem != nil) { effstem = objstem; };
|
||||||
let combined: *u8 = appendlit(stem.ptr, ".combined.ww");
|
let asmf: *u8 = appendlit(effstem, ".s");
|
||||||
|
let objf: *u8 = appendlit(effstem, ".o");
|
||||||
|
let combined: *u8 = appendlit(effstem, ".combined.ww");
|
||||||
|
|
||||||
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
|
||||||
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
@@ -1148,6 +1160,7 @@ fn defaultoutpath(src: *u8) *u8 = {
|
|||||||
|
|
||||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
|
let outflag: *u8 = nil; // -o target (binary + intermediate stem); T3
|
||||||
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
let incs: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
|
||||||
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
incs.len = ((os.PATH_MAX: u64) * 2u64): i32;
|
||||||
let incoff: u64 = 0u64;
|
let incoff: u64 = 0u64;
|
||||||
@@ -1219,10 +1232,21 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
libs[nlibs] = nm;
|
libs[nlibs] = nm;
|
||||||
nlibs += 1;
|
nlibs += 1;
|
||||||
|
} else { if (p[1u64] == 111u8) { // '-o'
|
||||||
|
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 {
|
} else {
|
||||||
cerr("ww build: unknown flag\n");
|
cerr("ww build: unknown flag\n");
|
||||||
return 2;
|
return 2;
|
||||||
}; }; };
|
}; }; }; };
|
||||||
} else {
|
} else {
|
||||||
if (src == nil) { src = p; };
|
if (src == nil) { src = p; };
|
||||||
};
|
};
|
||||||
@@ -1241,7 +1265,13 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let out: *u8 = nil;
|
let out: *u8 = nil;
|
||||||
if (isdir != 0) {
|
let objstem: *u8 = nil;
|
||||||
|
if (outflag != nil) {
|
||||||
|
// -o sets both the binary path and the intermediate stem so
|
||||||
|
// artifacts land beside the requested output (T3).
|
||||||
|
out = outflag;
|
||||||
|
objstem = outflag;
|
||||||
|
} else { if (isdir != 0) {
|
||||||
let rlen: u64 = cstrlen(resolved);
|
let rlen: u64 = cstrlen(resolved);
|
||||||
for (rlen > 1u64) {
|
for (rlen > 1u64) {
|
||||||
if (resolved[rlen - 1u64] != 47u8) { break; };
|
if (resolved[rlen - 1u64] != 47u8) { break; };
|
||||||
@@ -1257,13 +1287,13 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
cstrseal(out, off);
|
cstrseal(out, off);
|
||||||
} else {
|
} else {
|
||||||
out = defaultoutpath(resolved);
|
out = defaultoutpath(resolved);
|
||||||
};
|
}; };
|
||||||
let lf: lflags;
|
let lf: lflags;
|
||||||
lf.libdirs = libdirs.ptr;
|
lf.libdirs = libdirs.ptr;
|
||||||
lf.nlibdirs = nlibdirs;
|
lf.nlibdirs = nlibdirs;
|
||||||
lf.libs = libs.ptr;
|
lf.libs = libs.ptr;
|
||||||
lf.nlibs = nlibs;
|
lf.nlibs = nlibs;
|
||||||
return buildone(selfdir, resolved, isdir, out, incs.ptr, &lf);
|
return buildone(selfdir, resolved, isdir, out, objstem, incs.ptr, &lf);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
// Format the scratch path /tmp/ww_run_<pid> into buf. Returns NUL-
|
||||||
@@ -1372,10 +1402,20 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
};
|
};
|
||||||
libs[nlibs] = nm;
|
libs[nlibs] = nm;
|
||||||
nlibs += 1;
|
nlibs += 1;
|
||||||
|
} else { if (p[1u64] == 111u8) { // '-o'
|
||||||
|
// run always execs the temp binary; -o is accepted+
|
||||||
|
// ignored, mirroring the C driver's shared flag parser.
|
||||||
|
if (p[2u64] == 0u8) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
cerr("ww run: -o needs an argument\n");
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
cerr("ww run: unknown flag\n");
|
cerr("ww run: unknown flag\n");
|
||||||
return 2;
|
return 2;
|
||||||
}; }; };
|
}; }; }; };
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
if (src == nil) {
|
if (src == nil) {
|
||||||
@@ -1407,7 +1447,9 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
lf.nlibdirs = nlibdirs;
|
lf.nlibdirs = nlibdirs;
|
||||||
lf.libs = libs.ptr;
|
lf.libs = libs.ptr;
|
||||||
lf.nlibs = nlibs;
|
lf.nlibs = nlibs;
|
||||||
if (buildone(selfdir, resolved, isdir, tmp.ptr, incs.ptr, &lf) != 0) {
|
// objstem = tmp → intermediates at /tmp/ww_run_<pid>.{s,o,combined.ww},
|
||||||
|
// never next to the source (T3).
|
||||||
|
if (buildone(selfdir, resolved, isdir, tmp.ptr, tmp.ptr, incs.ptr, &lf) != 0) {
|
||||||
os.remove(pathstr(tmp.ptr));
|
os.remove(pathstr(tmp.ptr));
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -1441,7 +1483,7 @@ fn runsingletest(selfdir: *u8, src: *u8) i32 = {
|
|||||||
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
tmp.len = os.PATH_MAX;
|
tmp.len = os.PATH_MAX;
|
||||||
makeruntmp(tmp.ptr);
|
makeruntmp(tmp.ptr);
|
||||||
if (buildone(selfdir, src, 0, tmp.ptr, "\0".ptr, nil) != 0) {
|
if (buildone(selfdir, src, 0, tmp.ptr, nil, "\0".ptr, nil) != 0) {
|
||||||
os.remove(pathstr(tmp.ptr));
|
os.remove(pathstr(tmp.ptr));
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -1496,7 +1538,7 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
|||||||
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
let tmp: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||||
tmp.len = os.PATH_MAX;
|
tmp.len = os.PATH_MAX;
|
||||||
makeruntmp(tmp.ptr);
|
makeruntmp(tmp.ptr);
|
||||||
let bres: i32 = buildone(selfdir, path.ptr, 0, tmp.ptr, tincs.ptr, nil);
|
let bres: i32 = buildone(selfdir, path.ptr, 0, tmp.ptr, nil, tincs.ptr, nil);
|
||||||
if (bres != 0) {
|
if (bres != 0) {
|
||||||
fail += 1;
|
fail += 1;
|
||||||
cerr("FAIL ");
|
cerr("FAIL ");
|
||||||
|
|||||||
@@ -35,21 +35,26 @@ static int
|
|||||||
run_miss(const char *bin)
|
run_miss(const char *bin)
|
||||||
{
|
{
|
||||||
int pid = getpid();
|
int pid = getpid();
|
||||||
char src[64], errf[64], cmd[2048], line[4096];
|
char src[64], errf[64], outb[64], comb[80], cmd[2048], line[4096];
|
||||||
snprintf(src, sizeof src, "/tmp/mp949_miss_%d.ww", pid);
|
snprintf(src, sizeof src, "/tmp/mp949_miss_%d.ww", pid);
|
||||||
snprintf(errf, sizeof errf, "/tmp/mp949_miss_%d.err", pid);
|
snprintf(errf, sizeof errf, "/tmp/mp949_miss_%d.err", pid);
|
||||||
|
/* -o a writable /tmp stem (not /dev/null): post-T3 the intermediate
|
||||||
|
* .combined.ww/.s/.o follow -o, so /dev/null would yield an
|
||||||
|
* uncreatable /dev/null.combined.ww and mask the missing-pkg fatal. */
|
||||||
|
snprintf(outb, sizeof outb, "/tmp/mp949_miss_%d.out", pid);
|
||||||
|
snprintf(comb, sizeof comb, "/tmp/mp949_miss_%d.out.combined.ww", pid);
|
||||||
FILE *f = fopen(src, "w");
|
FILE *f = fopen(src, "w");
|
||||||
if (!f) { fprintf(stderr, "949 FAIL: stage miss\n"); return 1; }
|
if (!f) { fprintf(stderr, "949 FAIL: stage miss\n"); return 1; }
|
||||||
fputs("import nosuchpkg;\n"
|
fputs("import nosuchpkg;\n"
|
||||||
"export fn main() i32 = { return 0; };\n", f);
|
"export fn main() i32 = { return 0; };\n", f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
snprintf(cmd, sizeof cmd, "%s/ww build %s -o /dev/null 2>%s",
|
snprintf(cmd, sizeof cmd, "%s/ww build %s -o %s 2>%s",
|
||||||
bin, src, errf);
|
bin, src, outb, errf);
|
||||||
int rc = runwait(cmd);
|
int rc = runwait(cmd);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
fprintf(stderr, "949 FAIL: ww accepted a missing import\n");
|
fprintf(stderr, "949 FAIL: ww accepted a missing import\n");
|
||||||
unlink(src); unlink(errf);
|
unlink(src); unlink(errf); unlink(outb); unlink(comb);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int found = 0;
|
int found = 0;
|
||||||
@@ -61,7 +66,7 @@ run_miss(const char *bin)
|
|||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
unlink(src); unlink(errf);
|
unlink(src); unlink(errf); unlink(outb); unlink(comb);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
fprintf(stderr, "949 FAIL: no 'cannot find package nosuchpkg' "
|
fprintf(stderr, "949 FAIL: no 'cannot find package nosuchpkg' "
|
||||||
"on stderr\n");
|
"on stderr\n");
|
||||||
|
|||||||
@@ -62,17 +62,20 @@ slurp_eq(const char *a, const char *b)
|
|||||||
* success. */
|
* success. */
|
||||||
static int
|
static int
|
||||||
build_via(const char *bin, const char *driver, const char *src,
|
build_via(const char *bin, const char *driver, const char *src,
|
||||||
const char *workdir, const char *incs)
|
const char *workdir, const char *incs, const char *out_basename)
|
||||||
{
|
{
|
||||||
char cmd[4096];
|
char cmd[4096];
|
||||||
|
/* -o <workdir>/<out> keeps the binary where the diff expects it AND
|
||||||
|
* routes the .combined.ww/.s/.o into the workdir (T3) — no longer
|
||||||
|
* next to the source, so concurrent driver builds never collide. */
|
||||||
if (incs && incs[0]) {
|
if (incs && incs[0]) {
|
||||||
snprintf(cmd, sizeof cmd,
|
snprintf(cmd, sizeof cmd,
|
||||||
"cd %s && timeout 180 %s/%s build -I %s %s 2>/dev/null",
|
"cd %s && timeout 180 %s/%s build -o %s/%s -I %s %s 2>/dev/null",
|
||||||
workdir, bin, driver, incs, src);
|
workdir, bin, driver, workdir, out_basename, incs, src);
|
||||||
} else {
|
} else {
|
||||||
snprintf(cmd, sizeof cmd,
|
snprintf(cmd, sizeof cmd,
|
||||||
"cd %s && timeout 180 %s/%s build %s 2>/dev/null",
|
"cd %s && timeout 180 %s/%s build -o %s/%s %s 2>/dev/null",
|
||||||
workdir, bin, driver, src);
|
workdir, bin, driver, workdir, out_basename, src);
|
||||||
}
|
}
|
||||||
return runwait(cmd);
|
return runwait(cmd);
|
||||||
}
|
}
|
||||||
@@ -90,11 +93,11 @@ diff_one(const char *bin, const char *cwd, const char *label,
|
|||||||
snprintf(cmd, sizeof cmd, "rm -rf %s %s && mkdir -p %s %s", dc, dw, dc, dw);
|
snprintf(cmd, sizeof cmd, "rm -rf %s %s && mkdir -p %s %s", dc, dw, dc, dw);
|
||||||
if (runwait(cmd) != 0) return -1;
|
if (runwait(cmd) != 0) return -1;
|
||||||
|
|
||||||
if (build_via(bin, "ww", src, dc, incs) != 0) {
|
if (build_via(bin, "ww", src, dc, incs, out_basename) != 0) {
|
||||||
fprintf(stderr, "ww_ww FAIL: C ww errored on %s\n", label);
|
fprintf(stderr, "ww_ww FAIL: C ww errored on %s\n", label);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (build_via(bin, "ww_ww", src, dw, incs) != 0) {
|
if (build_via(bin, "ww_ww", src, dw, incs, out_basename) != 0) {
|
||||||
fprintf(stderr, "ww_ww FAIL: ww ww errored on %s\n", label);
|
fprintf(stderr, "ww_ww FAIL: ww ww errored on %s\n", label);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,10 +82,12 @@ struct buildjob {
|
|||||||
/* Fork a child that runs the `ww_ww build` for this tool. The child
|
/* Fork a child that runs the `ww_ww build` for this tool. The child
|
||||||
* inherits no concurrent siblings — system() spawns a fresh /bin/sh -c.
|
* inherits no concurrent siblings — system() spawns a fresh /bin/sh -c.
|
||||||
* stderr lands in <workdir>/build.err so concurrent builds don't merge
|
* stderr lands in <workdir>/build.err so concurrent builds don't merge
|
||||||
* their diagnostics. The driver leaves intermediates next to every
|
* their diagnostics. `-o <workdir>/main` (T3) makes the driver write its
|
||||||
* source it traverses (selfhost/cmd/<tool>/main.s, etc.) — task #15 in
|
* intermediates (main.{combined.ww,s,o}) beside the output in <workdir>
|
||||||
* the queue will let us redirect via `-o <outdir>`; until then, test/run
|
* instead of next to every traversed source — so concurrent driver
|
||||||
* serializes 990/995 after the parallel phase. */
|
* builds no longer share the next-to-source fixed paths, and this gate
|
||||||
|
* may run in the parallel group. The rebuilt binary still lands at
|
||||||
|
* <workdir>/main where collect_build diffs it. */
|
||||||
static int
|
static int
|
||||||
spawn_build(const char *bin, const char *cwd, struct buildjob *j)
|
spawn_build(const char *bin, const char *cwd, struct buildjob *j)
|
||||||
{
|
{
|
||||||
@@ -99,17 +101,17 @@ spawn_build(const char *bin, const char *cwd, struct buildjob *j)
|
|||||||
char cmd[4096];
|
char cmd[4096];
|
||||||
if (j->inc_local && j->inc_local[0]) {
|
if (j->inc_local && j->inc_local[0]) {
|
||||||
snprintf(cmd, sizeof cmd,
|
snprintf(cmd, sizeof cmd,
|
||||||
"cd %s && timeout 180 %s/ww_ww build -I %s/%s "
|
"cd %s && timeout 180 %s/ww_ww build -o %s/main -I %s/%s "
|
||||||
"-I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse "
|
"-I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse "
|
||||||
"-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err",
|
"-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err",
|
||||||
j->workdir, bin, cwd, j->inc_local,
|
j->workdir, bin, j->workdir, cwd, j->inc_local,
|
||||||
cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
|
cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
|
||||||
} else {
|
} else {
|
||||||
snprintf(cmd, sizeof cmd,
|
snprintf(cmd, sizeof cmd,
|
||||||
"cd %s && timeout 180 %s/ww_ww build "
|
"cd %s && timeout 180 %s/ww_ww build -o %s/main "
|
||||||
"-I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse "
|
"-I %s/lib/ww -I %s/lib/ww/lex -I %s/lib/ww/parse "
|
||||||
"-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err",
|
"-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err",
|
||||||
j->workdir, bin, cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
|
j->workdir, bin, j->workdir, cwd, cwd, cwd, cwd, cwd, j->src_rel, j->workdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t p = fork();
|
pid_t p = fork();
|
||||||
|
|||||||
Reference in New Issue
Block a user