diff --git a/cmd/ww/main.c b/cmd/ww/main.c index ef276c52..b932309b 100644 --- a/cmd/ww/main.c +++ b/cmd/ww/main.c @@ -178,65 +178,73 @@ import_path_form(const char *name, char *out, size_t outsz) out[i] = '\0'; } -/* Symmetric with wwstage locatein for byte-id driver output (rule 10). - * The legacy //.ww form was dropped in task #22 — - * directory-as-module enumeration replaces it, mirroring - * ref/hare/hare/module/srcs.ha (Hare has no fallback matching - * `foo/foo.ha`; a module IS the directory). */ +/* An import path names one directory package. There is deliberately no + * /.ww branch here: literal or searched single-file roots are a + * CLI compatibility concern handled by locate_module, never an import edge. */ static int locate_import_in(const char *dir, const char *path_form, char *out, - size_t outsz, int *is_dir, int want_dir) + size_t outsz) { struct stat st; - if (want_dir) { - snprintf(out, outsz, "%s/%s", dir, path_form); - if (stat(out, &st) == 0 && S_ISDIR(st.st_mode)) { - *is_dir = 1; - return 1; - } - return 0; - } - snprintf(out, outsz, "%s/%s.ww", dir, path_form); - if (access(out, 0) == 0) { - *is_dir = 0; + snprintf(out, outsz, "%s/%s", dir, path_form); + if (stat(out, &st) == 0 && S_ISDIR(st.st_mode)) return 1; + return 0; +} + +/* Walk every ordered root for // only. A decoy + * /.ww is neither a match nor a shadow: source imports + * always create canonical directory-package nodes. */ +static int +locate_import(const char *dirs, const char *path_form, char *out, + size_t outsz) +{ + const char *p = dirs; + while (*p) { + const char *e = strchr(p, ':'); + size_t n = e ? (size_t)(e - p) : strlen(p); + if (n > 0 && n < outsz) { + char dir[1024]; + if (n >= sizeof dir) n = sizeof dir - 1; + memcpy(dir, p, n); + dir[n] = '\0'; + if (locate_import_in(dir, path_form, out, outsz)) + return 1; + } + if (!e) break; + p = e + 1; } return 0; } -/* #98: "a module IS the directory" — a directory-package on ANY entry - * wins over a same-named sibling FILE on an EARLIER entry. The driver - * builds the searchpath srcd-first; a co-located `lib//_test.ww` - * entry makes srcd = lib/, so a self-named `import ` would - * else file-hit the sibling lib//.ww and fold it - * inline under the wrong module-reset → "package does not match - * import path ". Two passes — directories first, files only - * if no directory matches anywhere — let lib// resolve as the dir - * while a genuine leaf package with no directory (e.g. lib/encoding/hex - * imported bare as `hex`, reachable only via its file in srcd) still - * resolves in the file pass. Latent: a dir-package now beats an - * earlier-entry same-named sibling FILE — loud-failing, none in the - * corpus; tracked as #101. */ +/* CLI target compatibility: directory packages still win globally, then a + * bare target may resolve to /.ww. This function is never used + * while loading a source import. */ static int -locate_import(const char *dirs, const char *path_form, char *out, +locate_module(const char *dirs, const char *path_form, char *out, size_t outsz, int *is_dir) { - for (int want_dir = 1; want_dir >= 0; want_dir--) { - const char *p = dirs; - while (*p) { - const char *e = strchr(p, ':'); - size_t n = e ? (size_t)(e - p) : strlen(p); - if (n > 0 && n < outsz) { - char dir[1024]; - if (n >= sizeof dir) n = sizeof dir - 1; - memcpy(dir, p, n); - dir[n] = '\0'; - if (locate_import_in(dir, path_form, out, - outsz, is_dir, want_dir)) return 1; + if (locate_import(dirs, path_form, out, outsz)) { + *is_dir = 1; + return 1; + } + const char *p = dirs; + while (*p) { + const char *e = strchr(p, ':'); + size_t n = e ? (size_t)(e - p) : strlen(p); + if (n > 0 && n < outsz) { + char dir[1024]; + if (n >= sizeof dir) n = sizeof dir - 1; + memcpy(dir, p, n); + dir[n] = '\0'; + snprintf(out, outsz, "%s/%s.ww", dir, path_form); + if (access(out, 0) == 0) { + *is_dir = 0; + return 1; } - if (!e) break; - p = e + 1; } + if (!e) break; + p = e + 1; } return 0; } @@ -521,7 +529,7 @@ struct seppkg { int failed; /* discovery/compile failure reaches this action */ int test_support; /* compiler-generated -T support package */ int loaded; /* directory membership/name loaded exactly once */ - int emit_context; /* resolution context used to compose file imports */ + int emit_context; /* first verified resolution context */ unsigned char context_state[SEP_MAXCONTEXT]; /* 0 new, 1 active, 2 checked */ struct ImportSet bindings; /* first context's canonical import bindings */ int deps[SEP_MAXPKG]; /* direct-dep indices into sepgraph.pkg */ @@ -882,8 +890,8 @@ sep_external_production_import(const struct seppkg *pkg, const char *path) } /* Canonical bindings make a shared package independent of which selected - * root reaches it first. Directory, folded-file, and inline bindings are all - * part of the package action's source meaning. */ + * root reaches it first. Directory bindings and the raw-file inline + * compatibility binding are part of the package action's source meaning. */ static int sep_binding_add(struct ImportSet *bindings, char kind, const char *name, const char *target) @@ -1019,28 +1027,27 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file, break; } char ipath[1024]; - int is_dir = 0; int external_production = sep_external_production_import( &g->pkg[pi], name); int located = 0; if (external_production) { snprintf(ipath, sizeof ipath, "%s", g->pkg[pi].entry); - is_dir = 1; located = 1; } else { located = locate_import(searchpath, path_form, ipath, - sizeof ipath, &is_dir); + sizeof ipath); } if (!located) { const char *dot = strrchr(name, '.'); const char *leaf = dot ? dot + 1 : name; int inline_package = 0; - for (Node *package = imports->body; package; - package = package->next) - if (strcmp(package->module, leaf) == 0) { - inline_package = 1; - break; - } + if (!g->pkg[pi].is_dir) + for (Node *package = imports->body; package; + package = package->next) + if (strcmp(package->module, leaf) == 0) { + inline_package = 1; + break; + } if (inline_package) { if (sep_binding_add(bindings, 'I', name, NULL) < 0) rc = -1; @@ -1050,7 +1057,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file, rc = -1; break; } - if (is_dir) { + { char *canon = realpath(ipath, NULL); if (canon == NULL) { errorf(u->pos, "cannot canonicalize package '%s'", name); @@ -1105,20 +1112,6 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file, if (g->pkg[pi].ndeps >= SEP_MAXPKG) { rc = -1; break; } g->pkg[pi].deps[g->pkg[pi].ndeps++] = di; } - } else { - char *canon = realpath(ipath, NULL); - if (canon == NULL - || sep_binding_add(bindings, 'F', name, canon) < 0) { - free(canon); - rc = -1; - break; - } - free(canon); - if (sep_scan_file(g, pi, ipath, searchpath, filevisit, - bindings, 0) < 0) { - rc = -1; - break; - } } } free(uses); @@ -1322,65 +1315,18 @@ sep_validate_module_closure(struct sepgraph *g, const int *order, int n, } /* Emit one of pi's own source files into the sep-unit under the - * //ww:module-reset primary boundary (so -c emits its decls, imported - * ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead of the - * body); FILE imports fold in (intra-package split). */ + * //ww:module-reset primary boundary. Imports were already resolved to + * directory-package edges and their direct `.wwi` files were prepended; + * no source outside pi's sorted owned-source set may enter this unit. */ static int -sep_emit_body(FILE *out, const char *path, struct ImportSet *visited, - const char *searchpath, const char *modpath, const struct seppkg *pkg) +sep_emit_body(FILE *out, const char *path, const char *modpath) { - if (import_seen(visited, path)) return 0; - import_add(visited, path); char *buf; u64 len; if (sep_slurp(path, &buf, &len) < 0) { fprintf(stderr, "ww: cannot read %s\n", path); return -1; } - Arena *a = newarena(); - Lex l; - Parser p; - lexinit(&l, a, path, buf, len); - parserinit(&p, a, &l); - Node *imports = parseimports(&p); - if (l.errs || p.errs) { - freearena(a); - free(buf); - return -1; - } - int nuse = 0; - for (Node *u = imports->list; u; u = u->next) - if (u->kind == N_USE) nuse++; - Node **uses = nuse ? malloc((size_t)nuse * sizeof *uses) : NULL; - if (nuse && uses == NULL) { - freearena(a); - free(buf); - return -1; - } - int ui = 0; - for (Node *u = imports->list; u; u = u->next) - if (u->kind == N_USE) uses[ui++] = u; - if (nuse > 1) qsort(uses, (size_t)nuse, sizeof *uses, use_node_cmp); - for (int i = 0; i < nuse; i++) { - Node *u = uses[i]; - const char *name = u->usepath ? u->usepath : u->str; - if (sep_external_production_import(pkg, name)) - continue; - char path_form[1024]; - import_path_form(name, path_form, sizeof path_form); - char ipath[1024]; - int is_dir = 0; - if (!locate_import(searchpath, path_form, ipath, sizeof ipath, - &is_dir)) - continue; - if (!is_dir && sep_emit_body(out, ipath, visited, searchpath, - modpath, pkg) < 0) { - free(uses); - freearena(a); - free(buf); - return -1; - } - } /* #57: tag the primary body by its full dotted import path so the * definer mangles == the importer reference; a root build (path "") * stays a bare reset (keeps bare main). */ @@ -1394,8 +1340,6 @@ sep_emit_body(FILE *out, const char *path, struct ImportSet *visited, if (fwrite(buf, 1, (size_t)len, out) != (size_t)len || fputc('\n', out) == EOF) bad = 1; - free(uses); - freearena(a); free(buf); if (bad) { fprintf(stderr, "ww: cannot write package unit\n"); @@ -1415,8 +1359,6 @@ sep_compose_unit(struct sepgraph *g, int pi, const char *scratch, if (g->pkg[pi].emit_context < 0 || g->pkg[pi].emit_context >= g->ncontext) return -1; - const char *searchpath = - g->context[g->pkg[pi].emit_context].searchpath; FILE *u = fopen(unitf, "wb"); if (u == NULL) { fprintf(stderr, "ww: cannot open %s\n", unitf); @@ -1444,18 +1386,14 @@ sep_compose_unit(struct sepgraph *g, int pi, const char *scratch, return -1; } } - struct ImportSet bodyvisit = {0}; int bodyrc = 0; if (g->pkg[pi].is_dir) { for (int i = 0; i < g->pkg[pi].nsources && bodyrc == 0; i++) - bodyrc = sep_emit_body(u, g->pkg[pi].sources[i], &bodyvisit, - searchpath, g->pkg[pi].path, &g->pkg[pi]); + bodyrc = sep_emit_body(u, g->pkg[pi].sources[i], + g->pkg[pi].path); } else { - bodyrc = sep_emit_body(u, g->pkg[pi].entry, &bodyvisit, searchpath, - g->pkg[pi].path, &g->pkg[pi]); + bodyrc = sep_emit_body(u, g->pkg[pi].entry, g->pkg[pi].path); } - for (int i = 0; i < bodyvisit.n; i++) free(bodyvisit.paths[i]); - free(bodyvisit.paths); if (fclose(u) != 0) { fprintf(stderr, "ww: cannot close package unit %s\n", unitf); return -1; @@ -1821,7 +1759,8 @@ build_one_sep_impl(const char *src, int entry_is_dir, if (is_test) { char tpath[1024]; int tdir = 0; - if (locate_import(toolsrcdir, "test", tpath, sizeof tpath, &tdir)) { + if (locate_import(toolsrcdir, "test", tpath, sizeof tpath)) { + tdir = 1; g->support_context = sep_context_for(g, toolsrcdir, NULL, toolsrcdir); if (g->support_context < 0) return 1; @@ -1840,7 +1779,8 @@ build_one_sep_impl(const char *src, int entry_is_dir, char userpath[1024]; int userdir = 0; if (locate_import(g->context[products[i].context].searchpath, - "test", userpath, sizeof userpath, &userdir)) { + "test", userpath, sizeof userpath)) { + userdir = 1; (void)userdir; char *uc = realpath(userpath, NULL); if (tc != NULL && uc != NULL @@ -2342,7 +2282,7 @@ resolve_module(const char *name, const char *incs, char *out, size_t outsz, search_path(incs, sp, sizeof sp); char path_form[256]; import_path_form(name, path_form, sizeof path_form); - return locate_import(sp, path_form, out, outsz, is_dir); + return locate_module(sp, path_form, out, outsz, is_dir); } /* Returns the index past the last arg consumed for positionals (so callers diff --git a/docs/build-system.md b/docs/build-system.md index 62a43b97..c6076556 100644 --- a/docs/build-system.md +++ b/docs/build-system.md @@ -2792,13 +2792,16 @@ package main; import lib.math; ``` -An import is translated from dots to path separators and resolved, with -directory packages preferred, through the entry package's directory, explicit -`-I` roots in command order, and the toolchain source-library root. There is no -network or manifest fallback. The loader uses the compiler frontend's -imports-only parser, unions duplicate imports, byte-sorts direct edges, interns -resolved directories by filesystem identity, and reports self-imports and -stable cycle chains before compilation. +An import is translated from dots to path separators and resolved only as a +directory package through the entry package's directory, explicit `-I` roots in +command order, and the toolchain source-library root. A same-named `.ww` file is +neither a match nor a shadow for an import, so a later root containing the +directory wins over an earlier file decoy. There is no network, manifest, or +imported-file fallback. Explicit single-file CLI roots retain their raw-unit +compatibility path. The loader uses the compiler frontend's imports-only parser, +unions duplicate imports, byte-sorts direct edges, interns resolved directories +by filesystem identity, and reports self-imports and stable cycle chains before +compilation. A directory package consists of its immediate regular non-symlink `.ww` files, excluding `*_test.ww`, in byte-sorted filename order. Every selected file must @@ -2896,10 +2899,10 @@ context from the local package slice: its directory, explicit `-I` roots in command order, then the toolchain source root. Same and external variants of one directory share that context; unrelated directory roots never acquire lookup precedence from their request order. When multiple contexts reach one -canonical production package, the loader verifies that every directory, -folded-file, and inline import binding is identical before reusing its compile -action. A different binding is a deterministic package-resolution failure for -the roots that reach it, rather than a first-root-wins build. +canonical production package, the loader verifies that every directory import +binding is identical before reusing its compile action. A different binding is +a deterministic package-resolution failure for the roots that reach it, rather +than a first-root-wins build. A production action failure blocks exactly the roots that reach it. A root-local compile or link failure does not suppress a successfully built @@ -3123,6 +3126,72 @@ WW adopts only the correctness boundary in its existing inspectable `cmp`-based workdir. It does not add build IDs, hashes, a CAS, an action graph, a scheduler, or a manifest. +### 11.11 Implemented directory-only source-import slice + +Cstage and WWstage now use a directory-only locator for every parsed source +import, including imports selected only by a package-test variant and the +compiler-generated test-support edge. The loader makes one ordered pass for +`//`; it never probes `/.ww`. Unit +composition consequently writes only the owning package's byte-sorted source +files after its direct dependency interfaces and never recursively folds an +imported source body. Missing imports retain the importing source position and +the same stable diagnostic in both stages. + +Root selection remains a separate compatibility boundary. A literal `.ww` CLI +target, or a bare CLI target found as `/.ww` after the global +directory search, can still create one raw single-file root. Its historical +inline package clauses may satisfy compiler-fixture bindings inside that raw +unit. Directory roots cannot use that exemption, and no filesystem source +import can reach it. This preserves low-level compiler fixtures without +weakening package-graph identity. + +The self-hosted tools no longer depend on the removed behavior. `w6a/` and +`w6l/` are executable `package main` directories whose sorted source sets are +compiled once. The compiler backend is one `wcc/` directory package with a +narrow exported check/codegen façade; `w6c` and `wwdump` import that package +from its parent search root instead of importing its implementation files. +Legacy test fixtures were converted to directories, except for one intentional +compiler leaf-collision probe that now invokes `w6c` on an explicitly composed +raw unit. The Lisp example likewise imports a `lispcore/` directory package. + +The focused native regression puts only `example/foo.ww` in an earlier import +root and a two-source `example/foo/` package in a later root. Both stages select +the directory, emit the exact sorted package-owned unit, consume its direct +dependency export, produce byte-identical deterministic `.wwi` and `.a` +artifacts, link and run a transitive archive closure, and repeat the resolution +through a real directory-package test. With only the file root present, both +stages reject the import with byte-identical package-attributed stderr. The +existing exact-tool observer remains the non-duplicated proof that each +canonical production action compiles once, compiler units contain only direct +`.wwi` inputs, and linker argument vectors contain the complete reachable `.a` +closure and no `.wwi` path. + +This follows Go 1.26.5's concrete directory ownership. `ImportDir` is defined +as importing the package in a named directory; import lookup accepts directory +candidates, then reads and sorts that directory's immediate entries +([`go/build/build.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#521), +[`go/build/build.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#725), +[`go/build/build.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/go/build/build.go#859)). +Repeated loads reuse the canonical package object, and `PackageList` performs a +pointer-deduplicated postorder walk +([`cmd/go/internal/load/pkg.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/pkg.go#633), +[`cmd/go/internal/load/pkg.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/pkg.go#2776)). + +Go's build action is interned by mode and package identity and receives only +the package's direct imports, while linking explicitly expands all transitive +link dependencies +([`cmd/go/internal/work/action.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#437), +[`cmd/go/internal/work/action.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#646), +[`cmd/go/internal/work/action.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go#1034)). +Its compiler export writer finalizes self-contained public data in sorted index +order, which is why direct compiler artifacts suffice +([`cmd/compile/internal/noder/unified.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/compile/internal/noder/unified.go#463)). +Finally, Go constructs distinct ordinary, internal-test, external-test, and +generated-main package variants through the same import loader +([`cmd/go/internal/load/test.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go#85)). +WW adopts these package-ownership boundaries without adding modules, manifests, +network lookup, a generalized action graph, or a cache protocol. + ## 12. Candidate architectures and hard-gate decision Five candidates were developed as coherent systems, not as feature bins. diff --git a/selfhost/cmd/ww/main.ww b/selfhost/cmd/ww/main.ww index 57a61bb5..86ab5857 100644 --- a/selfhost/cmd/ww/main.ww +++ b/selfhost/cmd/ww/main.ww @@ -284,12 +284,9 @@ fn importpathform(name: *u8, namelen: u64) *u8 = { return buf.ptr; }; -// Try // as a directory (wantdir != 0), else /.ww -// as a file. Sets *isdir on hit. Symmetric with cstage locate_import_in -// for byte-id driver output (rule 10). The legacy //.ww -// form was dropped in task #22 — directory-as-module enumeration -// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no -// `foo/foo.ha` fallback; a module IS the directory). +// Try // as a directory (wantdir != 0), else +// /.ww as a CLI target. Source-import loading calls only the +// directory arm; the file arm belongs exclusively to locatemodule. fn locatein(dir: *u8, dirlen: u64, pathform: *u8, pflen: u64, isdir: *i32, wantdir: i32) *u8 = { let tail: u64 = 1u64; // NUL for the directory form @@ -333,44 +330,55 @@ fn locatein(dir: *u8, dirlen: u64, return nil; }; -// Walk a colon-separated dirlist, return first hit or nil. Sets -// *isdir on hit. -// -// #98: "a module IS the directory" — a directory-package on ANY entry -// wins over a same-named sibling FILE on an EARLIER entry. The driver -// builds the searchpath srcd-first; a co-located `lib//test.ww` -// entry makes srcd = lib/, so a self-named `import ` would -// else file-hit the sibling lib//.ww and fold it -// inline under the wrong module-reset. Two passes — directories first, -// files only if no directory matches anywhere — let lib// resolve -// as the dir while a genuine leaf package with no directory (e.g. -// lib/encoding/hex imported bare as `hex`, reachable only via its file -// in srcd) still resolves in the file pass. Latent: a dir-package now -// beats an earlier-entry same-named sibling FILE — loud-failing, none -// in the corpus; tracked as #101. -fn locateimport(dirs: *u8, name: *u8, namelen: u64, - isdir: *i32) *u8 = { +// Walk every ordered root for // only. A sibling or earlier-root +// .ww is neither a match nor a shadow: every source import identifies +// one canonical directory package. +fn locateimport(dirs: *u8, name: *u8, namelen: u64) *u8 = { let pathform: *u8 = importpathform(name, namelen); let pflen: u64 = cstrlen(pathform); let total: u64 = cstrlen(dirs); - let wantdir: i32 = 1i32; - for (wantdir >= 0i32) { - let p: u64 = 0u64; - for (p < total) { - let q: u64 = p; - for (q < total) { - if (dirs[q] == ':') { break; }; - q += 1u64; - }; - let seglen: u64 = q - p; - if (seglen > 0u64) { - let hit: *u8 = locatein(dirs + p, seglen, - pathform, pflen, isdir, wantdir); - if (hit != nil) { return hit; }; - }; - p = q + 1u64; + let p: u64 = 0u64; + for (p < total) { + let q: u64 = p; + for (q < total) { + if (dirs[q] == ':') { break; }; + q += 1u64; }; - wantdir -= 1i32; + let seglen: u64 = q - p; + if (seglen > 0u64) { + let isdir: i32 = 0; + let hit: *u8 = locatein(dirs + p, seglen, + pathform, pflen, &isdir, 1i32); + if (hit != nil) { return hit; }; + }; + p = q + 1u64; + }; + return nil; +}; + +// CLI target compatibility: directory packages win globally, then a bare +// target may resolve to /.ww. Never called for a source import. +fn locatemodule(dirs: *u8, name: *u8, namelen: u64, + isdir: *i32) *u8 = { + let hit: *u8 = locateimport(dirs, name, namelen); + if (hit != nil) { *isdir = 1; return hit; }; + let pathform: *u8 = importpathform(name, namelen); + let pflen: u64 = cstrlen(pathform); + let total: u64 = cstrlen(dirs); + let p: u64 = 0u64; + for (p < total) { + let q: u64 = p; + for (q < total) { + if (dirs[q] == ':') { break; }; + q += 1u64; + }; + let seglen: u64 = q - p; + if (seglen > 0u64) { + hit = locatein(dirs + p, seglen, pathform, pflen, + isdir, 0i32); + if (hit != nil) { return hit; }; + }; + p = q + 1u64; }; return nil; }; @@ -1240,79 +1248,68 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8, cerr(": error: import path is too long (limit 255 bytes)\n"); return -1; }; - let isdir: i32 = 0; let externalproduction: bool = sepexternalproduction( &g.pkg[pi], idp, idn); let ipath: *u8 = nil; if (externalproduction) { ipath = g.pkg[pi].entry; - isdir = 1; } else { - ipath = locateimport(searchpath, idp, idn, &isdir); + ipath = locateimport(searchpath, idp, idn); }; if (ipath != nil) { - if (isdir != 0) { - sepbindadd(bindings, 'D': u8, u.usepath, ipath); - let self: bool = os.samefile(pathstr(ipath), - pathstr(g.pkg[pi].entry)); - if (self && sepexternalname(&g.pkg[pi], idp, idn, true)) { - externalproduction = true; - }; - if (self - && !externalproduction) { - cerrpos(u.file, u.line, u.col); - cerr(": error: self-import: package '"); - if (g.pkg[pi].path[0u64] != 0u8) { - cerr(pathstr(g.pkg[pi].path)); - } else { cerr(pathstr(g.pkg[pi].name)); }; - cerr("' cannot import itself\n"); - return -1; - }; - let runtimeproduction: bool = false; - if (externalproduction) { - let gi: i32 = 0; - for (gi < g.n) { - if (g.pkg[gi].testsupport - && syntax.streq(pathstr(g.pkg[gi].path), - u.usepath) - && os.samefile(pathstr(g.pkg[gi].entry), - pathstr(ipath))) { - runtimeproduction = true; - }; - gi += 1; + sepbindadd(bindings, 'D': u8, u.usepath, ipath); + let self: bool = os.samefile(pathstr(ipath), + pathstr(g.pkg[pi].entry)); + if (self && sepexternalname(&g.pkg[pi], idp, idn, true)) { + externalproduction = true; + }; + if (self && !externalproduction) { + cerrpos(u.file, u.line, u.col); + cerr(": error: self-import: package '"); + if (g.pkg[pi].path[0u64] != 0u8) { + cerr(pathstr(g.pkg[pi].path)); + } else { cerr(pathstr(g.pkg[pi].name)); }; + cerr("' cannot import itself\n"); + return -1; + }; + let runtimeproduction: bool = false; + if (externalproduction) { + let gi: i32 = 0; + for (gi < g.n) { + if (g.pkg[gi].testsupport + && syntax.streq(pathstr(g.pkg[gi].path), + u.usepath) + && os.samefile(pathstr(g.pkg[gi].entry), + pathstr(ipath))) { + runtimeproduction = true; }; + gi += 1; }; - let nm: []u8 = alloc([], idn + 1u64)!; - let k: u64 = 0u64; - for (k < idn) { nm[k] = idp[k]; k += 1u64; }; - nm[idn] = 0u8; - let di: i32 = -1; - if (externalproduction && !runtimeproduction) { - let art: *u8 = appendlit(g.pkg[pi].artifact, - "-production"); - di = sepfindoraddrole(g, nm.ptr, ipath, 1, - SEP_ROLE_EXTERNAL_PRODUCTION, art); - } else { - di = sepfindoradd(g, nm.ptr, ipath, 1); - }; - if (di < 0) { return -1; }; - let seen: bool = false; - let m: i32 = 0; - for (m < g.pkg[pi].ndeps) { - if (g.pkg[pi].deps[m] == di) { seen = true; }; - m += 1; - }; - if (!seen) { - if (g.pkg[pi].ndeps >= SEP_MAXPKG) { return -1; }; - g.pkg[pi].deps[g.pkg[pi].ndeps] = di; - g.pkg[pi].ndeps += 1; - }; + }; + let nm: []u8 = alloc([], idn + 1u64)!; + let k: u64 = 0u64; + for (k < idn) { nm[k] = idp[k]; k += 1u64; }; + nm[idn] = 0u8; + let di: i32 = -1; + if (externalproduction && !runtimeproduction) { + let art: *u8 = appendlit(g.pkg[pi].artifact, + "-production"); + di = sepfindoraddrole(g, nm.ptr, ipath, 1, + SEP_ROLE_EXTERNAL_PRODUCTION, art); } else { - sepbindadd(bindings, 'F': u8, u.usepath, ipath); - if (sepscanfile(g, pi, ipath, searchpath, fv, - bindings, 0) < 0) { - return -1; - }; + di = sepfindoradd(g, nm.ptr, ipath, 1); + }; + if (di < 0) { return -1; }; + let seen: bool = false; + let m: i32 = 0; + for (m < g.pkg[pi].ndeps) { + if (g.pkg[pi].deps[m] == di) { seen = true; }; + m += 1; + }; + if (!seen) { + if (g.pkg[pi].ndeps >= SEP_MAXPKG) { return -1; }; + g.pkg[pi].deps[g.pkg[pi].ndeps] = di; + g.pkg[pi].ndeps += 1; }; } else { let lstart: u64 = 0u64; @@ -1324,11 +1321,13 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8, let leafp: *u8 = idp + lstart; let leafn: u64 = idn - lstart; let inlinepackage: bool = false; - let pm: *syntax.node = imports.body; - for (pm != nil) { - if (bytecmp(pm.nmod.ptr, pm.nmod.len: u64, - leafp, leafn) == 0) { inlinepackage = true; }; - pm = pm.next; + if (g.pkg[pi].isdir == 0) { + let pm: *syntax.node = imports.body; + for (pm != nil) { + if (bytecmp(pm.nmod.ptr, pm.nmod.len: u64, + leafp, leafn) == 0) { inlinepackage = true; }; + pm = pm.next; + }; }; if (!inlinepackage) { cerrpos(u.file, u.line, u.col); @@ -1547,10 +1546,8 @@ fn sepvalidatemoduleclosure(g: *sepgraph, order: []i32, n: i32, return 0; }; -// Emit one of pi's own source files into the sep-unit under the -// //ww:module-reset primary boundary (so -c emits its decls, imported -// ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead); -// FILE imports fold in (intra-package split). +// Imported source never enters this body: its direct export data was +// prepended above, while this package owns exactly its sorted source set. fn sepwriteall(fd: i32, buf: *u8, n: u64) bool = { match (os.writeall(fd, buf, n)) { case let wrote: i64 => return wrote == n: i64; @@ -1558,14 +1555,7 @@ fn sepwriteall(fd: i32, buf: *u8, n: u64) bool = { }; }; -fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8, - modpath: *u8, pkg: *seppkg) i32 = { - let pview: str; - pview.ptr = path; - pview.len = cstrlen(path): i32; - let pdup: str = strings.dup(pview); - if (visitseen(visit, pdup)) { return 0; }; - visitadd(visit, pdup); +fn sepemitbody(fd: i32, path: *u8, modpath: *u8) i32 = { let bufp: *u8; let blen: u64; bufp, blen = slurp(path); @@ -1573,64 +1563,6 @@ fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8, cerr("ww: cannot read source\n"); return -1; }; - let l: syntax.lex; - syntax.lexinit(&l, pdup, bufp, blen); - let ps: syntax.parser; - syntax.parserinit(&ps, &l); - let imports: *syntax.node = syntax.parseimports(&ps); - if (l.errs > 0 || ps.errs > 0) { return -1; }; - let nuse: i32 = 0; - let u: *syntax.node = imports.list; - for (u != nil) { - if (u.kind == syntax.nkind.N_USE) { nuse += 1; }; - u = u.next; - }; - let uses: []*syntax.node = []; - if (nuse > 0) { - let allocated: []*syntax.node = alloc([], nuse: u64)!; - uses = allocated; - uses.len = nuse; - }; - let ui: i32 = 0; - u = imports.list; - for (u != nil) { - if (u.kind == syntax.nkind.N_USE) { uses[ui] = u; ui += 1; }; - u = u.next; - }; - let si: i32 = 1; - for (si < nuse) { - let sj: i32 = si; - for (sj > 0) { - if (strings.compare(uses[sj - 1].usepath, - uses[sj].usepath) <= 0) { sj = 0; } - else { - let t: *syntax.node = uses[sj]; - uses[sj] = uses[sj - 1]; - uses[sj - 1] = t; - sj -= 1; - }; - }; - si += 1; - }; - ui = 0; - for (ui < nuse) { - u = uses[ui]; - let idp: *u8 = u.usepath.ptr; - let idn: u64 = u.usepath.len: u64; - if (sepexternalproduction(pkg, idp, idn)) { - ui += 1; - continue; - }; - let isdir: i32 = 0; - let ipath: *u8 = locateimport(searchpath, idp, idn, &isdir); - if (ipath != nil) { - if (isdir == 0) { - if (sepemitbody(fd, ipath, visit, searchpath, - modpath, pkg) < 0) { return -1; }; - }; - }; - ui += 1; - }; // #57: tag the primary body by its full dotted import path so the // definer mangles == the importer reference; a root build (path "") // stays a bare reset (keeps bare main). @@ -1665,7 +1597,6 @@ fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8, unitf: *u8) i32 = { if (g.pkg[pi].emitcontext < 0 || g.pkg[pi].emitcontext >= g.ncontext) { return -1; }; - let searchpath: *u8 = g.context[g.pkg[pi].emitcontext].searchpath; let u: i32 = os.open(pathstr(unitf), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644 if (u < 0) { cerr("ww: cannot open unit\n"); @@ -1696,21 +1627,15 @@ fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8, }; k += 1; }; - let bv: expctx; - bv.out = u; - bv.dirs = searchpath; - bv.visit = nil; let bodyrc: i32 = 0; if (g.pkg[pi].isdir != 0) { let i: i32 = 0; for (i < g.pkg[pi].nsources && bodyrc == 0) { - bodyrc = sepemitbody(u, g.pkg[pi].sources[i], &bv, searchpath, - g.pkg[pi].path, &g.pkg[pi]); + bodyrc = sepemitbody(u, g.pkg[pi].sources[i], g.pkg[pi].path); i += 1; }; } else { - bodyrc = sepemitbody(u, g.pkg[pi].entry, &bv, searchpath, - g.pkg[pi].path, &g.pkg[pi]); + bodyrc = sepemitbody(u, g.pkg[pi].entry, g.pkg[pi].path); }; if (os.close(u) != 0) { cerr("ww: cannot close package unit\n"); @@ -2226,9 +2151,9 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, // when user source occupies that identity, the reserved graph alias keeps // it distinct. The linker receives the same support archive closure. if (istest != 0) { - let td: i32 = 0; + let td: i32 = 1; let tp: *u8 = locateimport(toolsrcdir, "test".ptr, - "test".len: u64, &td); + "test".len: u64); if (tp != nil) { g.supportcontext = sepcontextfor(g, toolsrcdir, nil, toolsrcdir); @@ -2250,11 +2175,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, }; producti = 0; for (producti < nproducts && !collision) { - let ud: i32 = 0; let up: *u8 = locateimport( g.context[products[producti].context].searchpath, "test".ptr, - "test".len: u64, &ud); + "test".len: u64); if (up != nil && !os.samefile(pathstr(tp), pathstr(up))) { collision = true; }; @@ -2927,7 +2851,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = { }; let search: *u8 = buildsearchpath(selfdir, incs); - return locateimport(search, name, nlen, isdir); + return locatemodule(search, name, nlen, isdir); }; fn writeusage(fd: i32) void = {