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 = {