980 lines
27 KiB
Plaintext
980 lines
27 KiB
Plaintext
// Port of cmd/w6c/main.c.
|
|
|
|
package main;
|
|
|
|
import os;
|
|
import strconv;
|
|
import strings;
|
|
import syntax;
|
|
import wcc;
|
|
|
|
type publication = struct {
|
|
dst: *u8,
|
|
stage: str,
|
|
backup: str,
|
|
hadold: bool,
|
|
installed: bool,
|
|
passthrough: bool,
|
|
};
|
|
|
|
fn publicationpath(dst: *u8, kind: str) str = {
|
|
return strings.concat(pathstr(dst), ".w6c.",
|
|
strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind);
|
|
};
|
|
|
|
fn pathischardevice(path: *u8) bool = {
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, pathstr(path))) {
|
|
case void => {
|
|
return (((fi.mode: u32) & 61440u32) == (os.mode.CHR: u32));
|
|
};
|
|
case let e: os.oserror => return false;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
// Open a destination-adjacent file and immediately unlink its name. Cgen's
|
|
// fatal exit then leaves only a kernel-owned fd, never a partial destination
|
|
// or named staging artifact.
|
|
fn anonymousfd(dst: *u8, kind: str) i32 = {
|
|
let path: str = publicationpath(dst, kind);
|
|
let fd: i32 = os.open(path,
|
|
os.flag.RDWR | os.flag.CREATE | os.flag.EXCL, 384i32);
|
|
if (fd < 0 && pathischardevice(dst)) {
|
|
path = strings.concat("/tmp/ww-w6c.",
|
|
strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind);
|
|
fd = os.open(path,
|
|
os.flag.RDWR | os.flag.CREATE | os.flag.EXCL, 384i32);
|
|
};
|
|
if (fd < 0) {
|
|
os.write(2, "w6c: cannot create anonymous output\n".ptr,
|
|
"w6c: cannot create anonymous output\n".len: u64);
|
|
return -1;
|
|
};
|
|
if (os.remove(path) != 0) {
|
|
os.close(fd);
|
|
os.write(2, "w6c: cannot unlink anonymous output\n".ptr,
|
|
"w6c: cannot unlink anonymous output\n".len: u64);
|
|
return -1;
|
|
};
|
|
return fd;
|
|
};
|
|
|
|
fn copystream(src: i32, dst: i32) bool = {
|
|
if (os.lseek(src, 0i64, os.whence.SET) < 0) { return false; };
|
|
let buf: [65536]u8;
|
|
for (true) {
|
|
let n: i64 = os.read(src, buf.ptr, 65536u64);
|
|
if (n < 0) { return false; };
|
|
if (n == 0) { return true; };
|
|
let wr: (i64 | os.oserror) = os.writeall(dst, buf.ptr, n: u64);
|
|
match (wr) {
|
|
case let wrote: i64 => { if (wrote != n) { return false; }; };
|
|
case let e: os.oserror => return false;
|
|
};
|
|
};
|
|
return false;
|
|
};
|
|
|
|
fn materializestream(src: i32, dst: *u8, p: *publication) bool = {
|
|
p.dst = dst;
|
|
p.passthrough = false;
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, pathstr(dst))) {
|
|
case void => {
|
|
if (((fi.mode: u32) & 61440u32) == (os.mode.CHR: u32)) {
|
|
let sink: i32 = os.open(pathstr(dst), os.flag.WRONLY, 0i32);
|
|
if (sink < 0) { return false; };
|
|
let sinkok: bool = copystream(src, sink);
|
|
if (os.close(sink) != 0) { sinkok = false; };
|
|
if (sinkok) { p.passthrough = true; };
|
|
return sinkok;
|
|
};
|
|
};
|
|
case let e: os.oserror => void;
|
|
};
|
|
p.stage = publicationpath(dst, "new");
|
|
p.backup = publicationpath(dst, "old");
|
|
p.hadold = false;
|
|
p.installed = false;
|
|
let fd: i32 = os.open(p.stage,
|
|
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 420i32);
|
|
if (fd < 0) { return false; };
|
|
let ok: bool = copystream(src, fd);
|
|
if (os.close(fd) != 0) { ok = false; };
|
|
if (!ok) { os.remove(p.stage); };
|
|
return ok;
|
|
};
|
|
|
|
// A publication rollback name is occupied by any terminal directory entry,
|
|
// including a dangling symlink. Never follow it while deciding whether the
|
|
// compiler may park an existing destination there.
|
|
fn pathexistsnofollow(path: str) i32 = {
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, path)) {
|
|
case void => return 1;
|
|
case let e: os.oserror => {
|
|
if ((e: i64) == -2i64) { return 0; };
|
|
return -1;
|
|
};
|
|
};
|
|
return -1;
|
|
};
|
|
|
|
fn pathisregularnofollow(path: str) bool = {
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, path)) {
|
|
case void => {
|
|
return (((fi.mode: u32) & 61440u32) == (os.mode.REG: u32));
|
|
};
|
|
case let e: os.oserror => return false;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
fn publishdiag(prefix: str, dst: *u8) void = {
|
|
os.write(2, prefix.ptr, prefix.len: u64);
|
|
let path: str = pathstr(dst);
|
|
os.write(2, path.ptr, path.len: u64);
|
|
os.write(2, "\n".ptr, 1u64);
|
|
};
|
|
|
|
fn publishall(p: *publication, n: i32) bool = {
|
|
let i: i32 = 0;
|
|
let ok: bool = true;
|
|
for (i < n) {
|
|
if (p[i].passthrough) { i += 1; continue; };
|
|
if (!pathisregularnofollow(p[i].stage)) {
|
|
publishdiag("w6c: publication stage is not a regular file for ",
|
|
p[i].dst);
|
|
ok = false;
|
|
break;
|
|
};
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, pathstr(p[i].dst))) {
|
|
case void => {
|
|
if (((fi.mode: u32) & 61440u32) != (os.mode.REG: u32)) {
|
|
publishdiag(
|
|
"w6c: publication destination is not a regular file: ",
|
|
p[i].dst);
|
|
ok = false;
|
|
};
|
|
};
|
|
case let e: os.oserror => {
|
|
if ((e: i64) != -2i64) {
|
|
publishdiag("w6c: cannot inspect publication destination ",
|
|
p[i].dst);
|
|
ok = false;
|
|
};
|
|
};
|
|
};
|
|
if (!ok) { break; };
|
|
i += 1;
|
|
};
|
|
if (ok) {
|
|
i = 0;
|
|
for (i < n) {
|
|
if (p[i].passthrough) { i += 1; continue; };
|
|
let exists: i32 = pathexistsnofollow(p[i].backup);
|
|
if (exists != 0) {
|
|
if (exists < 0) {
|
|
publishdiag("w6c: cannot inspect publication backup for ",
|
|
p[i].dst);
|
|
} else {
|
|
publishdiag("w6c: publication backup exists for ", p[i].dst);
|
|
};
|
|
ok = false;
|
|
break;
|
|
};
|
|
i += 1;
|
|
};
|
|
};
|
|
if (ok) {
|
|
i = 0;
|
|
for (i < n) {
|
|
if (p[i].passthrough) { i += 1; continue; };
|
|
let rc: i32 = os.rename(pathstr(p[i].dst), p[i].backup);
|
|
if (rc == 0) { p[i].hadold = true; }
|
|
else { if (rc != -2) {
|
|
publishdiag("w6c: cannot preserve ", p[i].dst);
|
|
ok = false;
|
|
break;
|
|
}; };
|
|
i += 1;
|
|
};
|
|
};
|
|
if (ok) {
|
|
i = 0;
|
|
for (i < n) {
|
|
if (p[i].passthrough) { i += 1; continue; };
|
|
if (os.rename(p[i].stage, pathstr(p[i].dst)) != 0) {
|
|
publishdiag("w6c: cannot publish ", p[i].dst);
|
|
ok = false;
|
|
break;
|
|
};
|
|
p[i].installed = true;
|
|
i += 1;
|
|
};
|
|
if (ok) {
|
|
// Installation is the commit point. Backup cleanup cannot
|
|
// truthfully turn a completely installed pair into rejection.
|
|
i = 0;
|
|
for (i < n) {
|
|
if (!p[i].passthrough && p[i].hadold
|
|
&& os.remove(p[i].backup) != 0) {
|
|
publishdiag("w6c: cannot remove backup for ", p[i].dst);
|
|
};
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
};
|
|
i = n - 1;
|
|
for (i >= 0) {
|
|
if (p[i].passthrough) { i -= 1; continue; };
|
|
if (p[i].installed) { os.remove(pathstr(p[i].dst)); };
|
|
if (p[i].hadold) { os.rename(p[i].backup, pathstr(p[i].dst)); };
|
|
if (p[i].stage.len != 0) { os.remove(p[i].stage); };
|
|
i -= 1;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
fn cstreq(a: *u8, lit: str) bool = {
|
|
let n: u64 = lit.len: u64;
|
|
let i: u64 = 0u64;
|
|
for (i < n) {
|
|
let li: i32 = i: i32;
|
|
if (a[i] != lit[li]) { return false; };
|
|
i += 1u64;
|
|
};
|
|
if (a[i] != 0u8) { return false; };
|
|
return true;
|
|
};
|
|
|
|
fn cstrlen(p: *u8) u64 = {
|
|
let n: u64 = 0u64;
|
|
for (p[n] != 0u8) { n += 1u64; };
|
|
return n;
|
|
};
|
|
|
|
// pathstr — view a NUL-terminated *u8 as a str. lib/os entrypoints
|
|
// take str post-task-#23; this bridges call sites that still hold
|
|
// C-string paths (argv entries, arena-allocated buffers).
|
|
fn pathstr(p: *u8) str = {
|
|
let r: str;
|
|
r.ptr = p;
|
|
r.len = cstrlen(p): i32;
|
|
return r;
|
|
};
|
|
|
|
fn slurp(path: *u8) (*u8, u64) = {
|
|
let fd: i32 = os.open(pathstr(path), os.flag.RDONLY, 0i32);
|
|
if (fd < 0) { return nil, 0u64; };
|
|
let szr: (i64 | os.oserror) = os.filesize(fd);
|
|
let n: i64 = 0i64;
|
|
match (szr) {
|
|
case let v: i64 => n = v;
|
|
case let e: os.oserror => { os.close(fd); return nil, 0u64; };
|
|
};
|
|
let nz: u64 = n: u64;
|
|
let buf: []u8 = alloc([], nz + 1u64)!;
|
|
buf.len = (nz + 1u64): i32;
|
|
let rr: (i64 | os.oserror) = os.readall(fd, buf.ptr, nz);
|
|
os.close(fd);
|
|
let got: i64 = 0i64;
|
|
match (rr) {
|
|
case let v: i64 => got = v;
|
|
case let e: os.oserror => return nil, 0u64;
|
|
};
|
|
if (got != n) { return nil, 0u64; };
|
|
buf[nz] = 0u8;
|
|
return buf.ptr, nz;
|
|
};
|
|
|
|
fn exportownermatches(buf: *u8, n: u64, path: *u8) bool = {
|
|
let prefix: str = "//ww:module ";
|
|
let pn: u64 = cstrlen(path);
|
|
let poff: u64 = prefix.len: u64;
|
|
let need: u64 = poff + pn + 1u64;
|
|
if (n < need) { return false; };
|
|
let i: u64 = 0u64;
|
|
for (i < poff) {
|
|
if (buf[i] != prefix.ptr[i]) { return false; };
|
|
i += 1u64;
|
|
};
|
|
i = 0u64;
|
|
for (i < pn) {
|
|
if (buf[poff + i] != path[i]) { return false; };
|
|
i += 1u64;
|
|
};
|
|
return buf[need - 1u64] == 10u8;
|
|
};
|
|
|
|
type importmap = struct {
|
|
source: *u8,
|
|
path: *u8,
|
|
seen: bool,
|
|
};
|
|
|
|
fn allocimportmaps(count: i32) ([]importmap | nomem) = {
|
|
let value: []importmap = alloc([], count: u64)?;
|
|
return value;
|
|
};
|
|
|
|
fn allocimportptrs(count: i32) ([]*u8 | nomem) = {
|
|
let value: []*u8 = alloc([], count: u64)?;
|
|
return value;
|
|
};
|
|
|
|
fn allocimportstrs(count: i32) ([]str | nomem) = {
|
|
let value: []str = alloc([], count: u64)?;
|
|
return value;
|
|
};
|
|
|
|
fn allocnodeptrs(count: i32) ([]*syntax.node | nomem) = {
|
|
let value: []*syntax.node = alloc([], count: u64)?;
|
|
return value;
|
|
};
|
|
|
|
fn canonicalpkgname(path: str, name: str) bool = {
|
|
let prefix: str = "__wwi_";
|
|
if (name.len != prefix.len + path.len * 2) { return false; };
|
|
let i: i32 = 0;
|
|
for (i < prefix.len) {
|
|
if (name[i] != prefix[i]) { return false; };
|
|
i += 1;
|
|
};
|
|
i = 0;
|
|
for (i < path.len) {
|
|
let hi: u8 = path[i] >> 4u8;
|
|
let lo: u8 = path[i] & 15u8;
|
|
let hib: u8 = hi + 48u8;
|
|
let lob: u8 = lo + 48u8;
|
|
if (hi >= 10u8) { hib = hi - 10u8 + 97u8; };
|
|
if (lo >= 10u8) { lob = lo - 10u8 + 97u8; };
|
|
if (name[prefix.len + i * 2] != hib
|
|
|| name[prefix.len + i * 2 + 1] != lob) { return false; };
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
// A paired interface owns PATH's declared name. Compiler-private names keep
|
|
// transitive fact sections semantic and are ignored when a real name exists.
|
|
fn importpkgname(asts: []*syntax.node, paths: []*u8, nasts: i32, path: str,
|
|
primary: *syntax.node, conflict: *bool) str = {
|
|
let name: str;
|
|
let placeholder: str;
|
|
let i: i32 = 0;
|
|
for (i < nasts) {
|
|
if (!cstreq(paths[i], path)) { i += 1; continue; };
|
|
let f: *syntax.node = asts[i];
|
|
let p: *syntax.node = nil;
|
|
if (f != nil) { p = f.body; };
|
|
for (p != nil) {
|
|
if (p.nmod.len > 0 && p.pkgname.len > 0
|
|
&& syntax.streq(p.nmod, path)) {
|
|
if (canonicalpkgname(path, p.pkgname)) {
|
|
placeholder = p.pkgname;
|
|
} else { if (name.len > 0
|
|
&& !syntax.streq(name, p.pkgname)) {
|
|
*conflict = true;
|
|
let empty: str;
|
|
return empty;
|
|
} else { name = p.pkgname; }; };
|
|
};
|
|
p = p.next;
|
|
};
|
|
i += 1;
|
|
};
|
|
let p: *syntax.node = nil;
|
|
if (primary != nil) { p = primary.body; };
|
|
for (p != nil) {
|
|
if (p.nmod.len > 0 && p.pkgname.len > 0
|
|
&& syntax.streq(p.nmod, path)) {
|
|
if (canonicalpkgname(path, p.pkgname)) {
|
|
placeholder = p.pkgname;
|
|
} else { if (name.len > 0 && !syntax.streq(name, p.pkgname)) {
|
|
*conflict = true;
|
|
let empty: str;
|
|
return empty;
|
|
} else { name = p.pkgname; }; };
|
|
};
|
|
p = p.next;
|
|
};
|
|
if (name.len == 0) { name = placeholder; };
|
|
return name;
|
|
};
|
|
|
|
fn bindimportnames(list: *syntax.node, asts: []*syntax.node, paths: []*u8,
|
|
nasts: i32,
|
|
primary: *syntax.node, testsupport: *u8) bool = {
|
|
let u: *syntax.node = list;
|
|
for (u != nil) {
|
|
if (u.kind == syntax.nkind.N_USE && u.usepath.len > 0) {
|
|
let reserved: bool = testsupport != nil
|
|
&& cstreq(testsupport, "__wwtest")
|
|
&& syntax.streq(u.usepath, "__wwtest");
|
|
if (!reserved) {
|
|
let conflict: bool = false;
|
|
let name: str = importpkgname(asts, paths, nasts, u.usepath,
|
|
primary, &conflict);
|
|
if (conflict) {
|
|
let pre: str = "w6c: package ";
|
|
let post: str = " has conflicting declared names in export data\n";
|
|
os.write(2, pre.ptr, pre.len: u64);
|
|
os.write(2, u.usepath.ptr, u.usepath.len: u64);
|
|
os.write(2, post.ptr, post.len: u64);
|
|
return false;
|
|
};
|
|
if (name.len > 0) {
|
|
u.usepkgname = name;
|
|
if (u.useblank == 0) {
|
|
if (u.usealias.len > 0) { u.str = u.usealias; }
|
|
else { u.str = name; };
|
|
};
|
|
} else { if (u.imported == 0) {
|
|
let pre: str = "w6c: import ";
|
|
let post: str = " has no declared package name in direct export data\n";
|
|
os.write(2, pre.ptr, pre.len: u64);
|
|
os.write(2, u.usepath.ptr, u.usepath.len: u64);
|
|
os.write(2, post.ptr, post.len: u64);
|
|
return false;
|
|
} else {
|
|
// A closure-only import may have no declarations in this
|
|
// interface. Its canonical path must never become a leaf
|
|
// qualifier by fallback.
|
|
if (u.useblank == 0) {
|
|
if (u.usealias.len > 0) { u.str = u.usealias; }
|
|
else { u.str = u.usepath; };
|
|
};
|
|
}; };
|
|
};
|
|
};
|
|
u = u.next;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
export fn main(argc: i32, argv: **u8) i32 = {
|
|
let src: *u8 = nil;
|
|
let out: *u8 = nil;
|
|
let wwiout: *u8 = nil; // -I <out.wwi>: M2 export-data producer
|
|
let testsupport: *u8 = nil;
|
|
let testmode: i32 = 0i32; // #15: `-T` test-mode
|
|
let testpackage: i32 = 0i32;
|
|
let commandpackage: i32 = 0i32;
|
|
let entrymode: i32 = 0i32;
|
|
let packageinit: *u8 = nil;
|
|
let initdispatch: *u8 = nil;
|
|
let sepmode: i32 = 0i32; // -c: #22 M3 separate-compile / primary-
|
|
// only codegen (emit imported==0 decls
|
|
// only; treat `.wwi` deps as external)
|
|
let pathallocation: ([]*u8 | nomem) = allocimportptrs(argc);
|
|
let fileallocation: ([]*u8 | nomem) = allocimportptrs(argc);
|
|
let targetallocation: ([]str | nomem) = allocimportstrs(argc);
|
|
let importpaths: []*u8;
|
|
let importfiles: []*u8;
|
|
let testtargets: []str;
|
|
let astallocation: ([]*syntax.node | nomem) = allocnodeptrs(argc);
|
|
let importasts: []*syntax.node;
|
|
match (pathallocation) {
|
|
case let value: []*u8 => importpaths = value;
|
|
case nomem => {
|
|
let m: str = "w6c: out of memory\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
};
|
|
match (fileallocation) {
|
|
case let value: []*u8 => importfiles = value;
|
|
case nomem => {
|
|
let m: str = "w6c: out of memory\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
};
|
|
importpaths.len = argc;
|
|
importfiles.len = argc;
|
|
match (targetallocation) {
|
|
case let value: []str => testtargets = value;
|
|
case nomem => {
|
|
let m: str = "w6c: out of memory\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
};
|
|
testtargets.len = argc;
|
|
match (astallocation) {
|
|
case let value: []*syntax.node => importasts = value;
|
|
case nomem => {
|
|
let m: str = "w6c: out of memory\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
};
|
|
importasts.len = argc;
|
|
let nimports: i32 = 0;
|
|
let ntesttargets: i32 = 0;
|
|
let mapallocation: ([]importmap | nomem) = allocimportmaps(argc);
|
|
let importmaps: []importmap;
|
|
match (mapallocation) {
|
|
case let value: []importmap => importmaps = value;
|
|
case nomem => {
|
|
let m: str = "w6c: out of memory\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
};
|
|
importmaps.len = argc;
|
|
let nmaps: i32 = 0;
|
|
|
|
let i: i32 = 1;
|
|
for (i < argc) {
|
|
let a: *u8 = argv[i];
|
|
if (cstreq(a, "-o")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: -o requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
out = argv[i];
|
|
} else { if (cstreq(a, "-I")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: -I requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
wwiout = argv[i];
|
|
} else { if (cstreq(a, "-T")) {
|
|
testmode = 1i32;
|
|
} else { if (cstreq(a, "--test-package")) {
|
|
testpackage = 1i32;
|
|
} else { if (cstreq(a, "--command-package")) {
|
|
commandpackage = 1i32;
|
|
} else { if (cstreq(a, "--entry")) {
|
|
entrymode = 1i32;
|
|
} else { if (cstreq(a, "--package-init-symbol")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: --package-init-symbol requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
packageinit = argv[i];
|
|
} else { if (cstreq(a, "--init-dispatch-symbol")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: --init-dispatch-symbol requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
initdispatch = argv[i];
|
|
} else { if (cstreq(a, "--test-support-module")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: --test-support-module requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
testsupport = argv[i];
|
|
} else { if (cstreq(a, "--test-target-package")) {
|
|
i += 1;
|
|
if (i >= argc) {
|
|
let m: str = "w6c: --test-target-package requires arg\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
testtargets[ntesttargets] = pathstr(argv[i]);
|
|
ntesttargets += 1;
|
|
} else { if (cstreq(a, "-c")) {
|
|
sepmode = 1i32;
|
|
} else { if (cstreq(a, "--import")) {
|
|
if (i + 2 >= argc) {
|
|
let m: str = "w6c: --import requires path and file\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
importpaths[nimports] = argv[i + 1];
|
|
importfiles[nimports] = argv[i + 2];
|
|
nimports += 1;
|
|
i += 2;
|
|
} else { if (cstreq(a, "--import-map")) {
|
|
if (i + 2 >= argc) {
|
|
let m: str = "w6c: --import-map requires source and path\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
importmaps[nmaps].source = argv[i + 1];
|
|
importmaps[nmaps].path = argv[i + 2];
|
|
importmaps[nmaps].seen = false;
|
|
nmaps += 1;
|
|
i += 2;
|
|
} else { if (a[0u64] == 45u8) {
|
|
let m: str = "w6c: unknown flag\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
} else {
|
|
if (src != nil) {
|
|
let m: str = "w6c: only one input\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
src = a;
|
|
}; }; }; }; }; }; }; }; }; }; }; }; }; };
|
|
i += 1;
|
|
};
|
|
|
|
if (src == nil) {
|
|
let m: str = "usage: w6c_ww [-T|--test-package] [--command-package] [--entry] [--package-init-symbol symbol] [--init-dispatch-symbol symbol] [--test-target-package path] [-c] [-I out.wwi] [--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (out != nil && wwiout != nil && cstreq(out, pathstr(wwiout))) {
|
|
let m: str = "w6c: assembly and interface outputs must be distinct\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (nimports > 0 && sepmode == 0) {
|
|
let m: str = "w6c: --import requires -c\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (nmaps > 0 && sepmode == 0) {
|
|
let m: str = "w6c: --import-map requires -c\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if ((entrymode != 0 || testpackage != 0 || commandpackage != 0)
|
|
&& sepmode == 0) {
|
|
let m: str = "w6c: --entry, --test-package, and --command-package require -c\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if ((packageinit != nil || initdispatch != nil) && sepmode == 0) {
|
|
let m: str = "w6c: package initialization symbols require -c\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (packageinit != nil && packageinit[0u64] == 0u8) {
|
|
let m: str = "w6c: --package-init-symbol is empty\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (initdispatch != nil
|
|
&& (initdispatch[0u64] == 0u8 || entrymode == 0)) {
|
|
let m: str = "w6c: --init-dispatch-symbol requires --entry and a non-empty symbol\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (testmode != 0 && testpackage != 0) {
|
|
let m: str = "w6c: -T and --test-package are mutually exclusive\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
let importi: i32 = 0;
|
|
for (importi < nimports) {
|
|
if (importpaths[importi][0u64] == 0u8) {
|
|
let m: str = "w6c: --import path is empty\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (importi > 0 && strings.compare(pathstr(importpaths[importi - 1]),
|
|
pathstr(importpaths[importi])) >= 0) {
|
|
let m: str = "w6c: --import paths must be sorted and unique\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
importi += 1;
|
|
};
|
|
let mapi: i32 = 0;
|
|
for (mapi < nmaps) {
|
|
if (importmaps[mapi].source[0u64] == 0u8
|
|
|| importmaps[mapi].path[0u64] == 0u8) {
|
|
let m: str = "w6c: --import-map path is empty\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (mapi > 0 && strings.compare(pathstr(importmaps[mapi - 1].source),
|
|
pathstr(importmaps[mapi].source)) >= 0) {
|
|
let m: str = "w6c: --import-map sources must be sorted and unique\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
let direct: bool = false;
|
|
let directi: i32 = 0;
|
|
for (directi < nimports) {
|
|
if (cstreq(importmaps[mapi].path,
|
|
pathstr(importpaths[directi]))) {
|
|
direct = true;
|
|
break;
|
|
};
|
|
directi += 1;
|
|
};
|
|
if (!direct) {
|
|
let m: str = "w6c: --import-map target is not a direct import\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
mapi += 1;
|
|
};
|
|
if (testsupport != nil && (sepmode == 0
|
|
|| (!cstreq(testsupport, "test")
|
|
&& !cstreq(testsupport, "__wwtest")))) {
|
|
let m: str = "w6c: invalid --test-support-module\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
let targeti: i32 = 0;
|
|
for (targeti < ntesttargets) {
|
|
let testtarget: str = testtargets[targeti];
|
|
if (sepmode == 0 || testmode == 0 || testtarget.len == 0) {
|
|
let m: str = "w6c: invalid --test-target-package\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
if (targeti > 0
|
|
&& strings.compare(testtargets[targeti - 1], testtarget) >= 0) {
|
|
let m: str = "w6c: --test-target-package paths must be sorted and unique\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
let direct: bool = false;
|
|
importi = 0;
|
|
for (importi < nimports) {
|
|
if (syntax.streq(pathstr(importpaths[importi]), testtarget)) {
|
|
direct = true;
|
|
};
|
|
importi += 1;
|
|
};
|
|
if (!direct) {
|
|
let m: str = "w6c: --test-target-package is not a direct import\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
targeti += 1;
|
|
};
|
|
|
|
let importhead: *syntax.node = nil;
|
|
let importtail: *syntax.node = nil;
|
|
importi = 0;
|
|
for (importi < nimports) {
|
|
let ibuf: *u8;
|
|
let ilen: u64;
|
|
ibuf, ilen = slurp(importfiles[importi]);
|
|
if (ibuf == nil) {
|
|
let pre: str = "w6c: import ";
|
|
let mid: str = ": cannot read ";
|
|
let nl: str = "\n";
|
|
let ipath: str = pathstr(importpaths[importi]);
|
|
let ifile: str = pathstr(importfiles[importi]);
|
|
os.write(2, pre.ptr, pre.len: u64);
|
|
os.write(2, ipath.ptr, ipath.len: u64);
|
|
os.write(2, mid.ptr, mid.len: u64);
|
|
os.write(2, ifile.ptr, ifile.len: u64);
|
|
os.write(2, nl.ptr, nl.len: u64);
|
|
return 1;
|
|
};
|
|
if (!exportownermatches(ibuf, ilen, importpaths[importi])) {
|
|
let pre: str = "w6c: import ";
|
|
let mid: str = ": export owner mismatch in ";
|
|
let nl: str = "\n";
|
|
os.write(2, pre.ptr, pre.len: u64);
|
|
os.write(2, importpaths[importi], cstrlen(importpaths[importi]));
|
|
os.write(2, mid.ptr, mid.len: u64);
|
|
os.write(2, importfiles[importi], cstrlen(importfiles[importi]));
|
|
os.write(2, nl.ptr, nl.len: u64);
|
|
return 1;
|
|
};
|
|
let il: syntax.lex;
|
|
syntax.lexinit(&il, strings.dup(pathstr(importfiles[importi])), ibuf, ilen);
|
|
let ips: syntax.parser;
|
|
syntax.parserinit(&ips, &il);
|
|
let imod: str = pathstr(importpaths[importi]);
|
|
ips.pathmod = imod;
|
|
ips.curmod = imod;
|
|
if (testsupport != nil) { ips.testmodule = pathstr(testsupport); };
|
|
let imported: *syntax.node = syntax.parsefile(&ips);
|
|
if (il.errs > 0 || ips.errs > 0) { return 1; };
|
|
importasts[importi] = imported;
|
|
if (!bindimportnames(imported.list, importasts, importpaths, importi + 1,
|
|
nil, testsupport)) { return 1; };
|
|
let d: *syntax.node = imported.list;
|
|
if (d != nil) {
|
|
if (importhead == nil) { importhead = d; }
|
|
else { importtail.next = d; };
|
|
for (d.next != nil) { d = d.next; };
|
|
importtail = d;
|
|
};
|
|
importi += 1;
|
|
};
|
|
|
|
let buf: *u8;
|
|
let blen: u64;
|
|
buf, blen = slurp(src);
|
|
if (buf == nil) {
|
|
let m: str = "w6c: cannot read input\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
let l: syntax.lex;
|
|
syntax.lexinit(&l, strings.dup(pathstr(src)), buf, blen);
|
|
let ps: syntax.parser;
|
|
syntax.parserinit(&ps, &l);
|
|
if (testsupport != nil) { ps.testmodule = pathstr(testsupport); };
|
|
// Entry compilation classifies an ordinary command package. The explicit
|
|
// marker carries only that parser fact for non-entry command test variants.
|
|
ps.commandpackage = commandpackage != 0 || entrymode != 0;
|
|
let f: *syntax.node = syntax.parsefile(&ps);
|
|
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
|
|
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
|
|
// cgen and emits junk asm with a zero exit (silent miscompile).
|
|
if (l.errs > 0 || ps.errs > 0) { return 1; };
|
|
let use: *syntax.node = f.list;
|
|
for (use != nil) {
|
|
if (use.kind == syntax.nkind.N_USE && use.usepath.len != 0) {
|
|
let source: str = use.usesource;
|
|
if (source.len == 0) { source = use.usepath; };
|
|
mapi = 0;
|
|
for (mapi < nmaps) {
|
|
if (syntax.streq(source,
|
|
pathstr(importmaps[mapi].source))) {
|
|
use.usepath = pathstr(importmaps[mapi].path);
|
|
importmaps[mapi].seen = true;
|
|
break;
|
|
};
|
|
mapi += 1;
|
|
};
|
|
};
|
|
use = use.next;
|
|
};
|
|
mapi = 0;
|
|
for (mapi < nmaps) {
|
|
if (!importmaps[mapi].seen) {
|
|
let m: str = "w6c: --import-map source is not in primary input\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
mapi += 1;
|
|
};
|
|
// A later direct interface may supply metadata for an origin section used
|
|
// by an earlier interface, so bind once more against the complete set.
|
|
importi = 0;
|
|
for (importi < nimports) {
|
|
if (!bindimportnames(importasts[importi].list, importasts, importpaths,
|
|
nimports,
|
|
nil, testsupport)) { return 1; };
|
|
importi += 1;
|
|
};
|
|
if (!bindimportnames(f.list, importasts, importpaths, nimports, f,
|
|
testsupport)) {
|
|
return 1;
|
|
};
|
|
targeti = 0;
|
|
for (targeti < ntesttargets) {
|
|
let seen: i32 = 0;
|
|
let targetuse: *syntax.node = f.list;
|
|
for (targetuse != nil) {
|
|
if (targetuse.kind == syntax.nkind.N_USE
|
|
&& targetuse.imported == 0
|
|
&& syntax.streq(targetuse.usepath, testtargets[targeti])) {
|
|
targetuse.str = targetuse.usepath;
|
|
seen += 1;
|
|
};
|
|
targetuse = targetuse.next;
|
|
};
|
|
if (seen != 1) {
|
|
let m: str = "w6c: generated test target import is not unique\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 2;
|
|
};
|
|
targeti += 1;
|
|
};
|
|
if (importhead != nil) {
|
|
importtail.next = f.list;
|
|
f.list = importhead;
|
|
};
|
|
|
|
let wwifd: i32 = -1;
|
|
if (wwiout != nil) {
|
|
wwifd = anonymousfd(wwiout, "anon.wwi");
|
|
if (wwifd < 0) { return 1; };
|
|
};
|
|
// cgen writes directly to fd 1. Redirect it only to an already-unlinked
|
|
// anonymous file, never to the named destination.
|
|
let asmfd: i32 = -1;
|
|
if (out != nil) {
|
|
asmfd = anonymousfd(out, "anon.asm");
|
|
if (asmfd < 0) {
|
|
if (wwifd >= 0) { os.close(wwifd); };
|
|
return 1;
|
|
};
|
|
if (os.dup2(asmfd, 1i32) < 0) {
|
|
let m: str = "w6c: dup2 failed\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
os.close(asmfd);
|
|
if (wwifd >= 0) { os.close(wwifd); };
|
|
return 1;
|
|
};
|
|
};
|
|
|
|
let testmodule: str;
|
|
if (testsupport != nil) { testmodule = pathstr(testsupport); };
|
|
testtargets.len = ntesttargets;
|
|
let packageinitsymbol: str;
|
|
if (packageinit != nil) { packageinitsymbol = pathstr(packageinit); };
|
|
let initdispatchsymbol: str;
|
|
if (initdispatch != nil) { initdispatchsymbol = pathstr(initdispatch); };
|
|
let haswwi: i32 = 0;
|
|
if (wwiout != nil) { haswwi = 1; };
|
|
let compilerc: i32 = wcc.compilefile(f, testmode, testpackage, testmodule,
|
|
testtargets, sepmode, wwifd, haswwi, entrymode,
|
|
packageinitsymbol, initdispatchsymbol);
|
|
if (out != nil) { os.close(1i32); };
|
|
if (compilerc != 0) {
|
|
if (asmfd >= 0) { os.close(asmfd); };
|
|
if (wwifd >= 0) { os.close(wwifd); };
|
|
return compilerc;
|
|
};
|
|
let pubs: [2]publication;
|
|
let pi: i32 = 0;
|
|
for (pi < 2) {
|
|
pubs[pi].dst = nil;
|
|
pubs[pi].stage = "";
|
|
pubs[pi].backup = "";
|
|
pubs[pi].hadold = false;
|
|
pubs[pi].installed = false;
|
|
pubs[pi].passthrough = false;
|
|
pi += 1;
|
|
};
|
|
let npub: i32 = 0;
|
|
let stagedok: bool = true;
|
|
if (wwiout != nil) {
|
|
stagedok = materializestream(wwifd, wwiout, &pubs[npub]);
|
|
if (stagedok) { npub += 1; };
|
|
};
|
|
if (stagedok && out != nil) {
|
|
stagedok = materializestream(asmfd, out, &pubs[npub]);
|
|
if (stagedok) { npub += 1; };
|
|
};
|
|
if (asmfd >= 0) { os.close(asmfd); };
|
|
if (wwifd >= 0) { os.close(wwifd); };
|
|
if (!stagedok) {
|
|
pi = 0;
|
|
for (pi < 2) {
|
|
if (pubs[pi].stage.len != 0) { os.remove(pubs[pi].stage); };
|
|
pi += 1;
|
|
};
|
|
let m: str = "w6c: cannot stage compiler output\n";
|
|
os.write(2, m.ptr, m.len: u64);
|
|
return 1;
|
|
};
|
|
if (!publishall(&pubs[0], npub)) {
|
|
return 1;
|
|
};
|
|
return 0;
|
|
};
|