ww: implement package initialization
This commit is contained in:
@@ -3,10 +3,204 @@
|
||||
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,
|
||||
};
|
||||
|
||||
fn publicationpath(dst: *u8, kind: str) str = {
|
||||
return strings.concat(pathstr(dst), ".w6c.",
|
||||
strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind);
|
||||
};
|
||||
|
||||
// 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) {
|
||||
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.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 (!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) {
|
||||
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) {
|
||||
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 (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].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].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;
|
||||
@@ -192,8 +386,10 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, paths: []*u8,
|
||||
};
|
||||
if (name.len > 0) {
|
||||
u.usepkgname = name;
|
||||
if (u.usealias.len > 0) { u.str = u.usealias; }
|
||||
else { u.str = 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";
|
||||
@@ -205,8 +401,10 @@ fn bindimportnames(list: *syntax.node, asts: []*syntax.node, paths: []*u8,
|
||||
// A closure-only import may have no declarations in this
|
||||
// interface. Its canonical path must never become a leaf
|
||||
// qualifier by fallback.
|
||||
if (u.usealias.len > 0) { u.str = u.usealias; }
|
||||
else { u.str = u.usepath; };
|
||||
if (u.useblank == 0) {
|
||||
if (u.usealias.len > 0) { u.str = u.usealias; }
|
||||
else { u.str = u.usepath; };
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
@@ -225,6 +423,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
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)
|
||||
@@ -302,6 +502,22 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
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) {
|
||||
@@ -352,12 +568,17 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
return 2;
|
||||
};
|
||||
src = a;
|
||||
}; }; }; }; }; }; }; }; }; }; }; };
|
||||
}; }; }; }; }; }; }; }; }; }; }; }; }; };
|
||||
i += 1;
|
||||
};
|
||||
|
||||
if (src == nil) {
|
||||
let m: str = "usage: w6c_ww [-T|--test-package] [--command-package] [--entry] [--test-target-package path] [-c] [-I out.wwi] [--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n";
|
||||
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;
|
||||
};
|
||||
@@ -377,6 +598,22 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
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);
|
||||
@@ -595,30 +832,82 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
f.list = importhead;
|
||||
};
|
||||
|
||||
// cgen writes directly to fd 1; dup2 keeps that implementation private.
|
||||
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) {
|
||||
let ofd: i32 = os.open(pathstr(out),
|
||||
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (ofd < 0) {
|
||||
let m: str = "w6c: cannot open output\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
asmfd = anonymousfd(out, "anon.asm");
|
||||
if (asmfd < 0) {
|
||||
if (wwifd >= 0) { os.close(wwifd); };
|
||||
return 1;
|
||||
};
|
||||
if (os.dup2(ofd, 1i32) < 0) {
|
||||
if (os.dup2(asmfd, 1i32) < 0) {
|
||||
let m: str = "w6c: dup2 failed\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.close(ofd);
|
||||
os.close(asmfd);
|
||||
if (wwifd >= 0) { os.close(wwifd); };
|
||||
return 1;
|
||||
};
|
||||
os.close(ofd);
|
||||
};
|
||||
|
||||
let testmodule: str;
|
||||
if (testsupport != nil) { testmodule = pathstr(testsupport); };
|
||||
let testtargetmodule: str;
|
||||
if (testtarget != nil) { testtargetmodule = pathstr(testtarget); };
|
||||
let interfaceout: str;
|
||||
if (wwiout != nil) { interfaceout = pathstr(wwiout); };
|
||||
return wcc.compilefile(f, testmode, testpackage, testmodule,
|
||||
testtargetmodule, sepmode, interfaceout, entrymode);
|
||||
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,
|
||||
testtargetmodule, 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;
|
||||
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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user