cmd: resolve source imports only as directories

Separate import lookup from raw CLI target compatibility in both production drivers. Package scans and test-support edges now create directory nodes only, while unit composition emits only the owning package's sorted sources after direct export data. Document the Go 1.26.5 ownership evidence and retained single-file root boundary.
This commit is contained in:
2026-08-12 17:12:49 +09:00
parent c7d9dc92de
commit 0af13ea501
3 changed files with 272 additions and 339 deletions

View File

@@ -178,50 +178,27 @@ 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 <dir>/<name>/<name>.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
* <dir>/<path>.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;
if (stat(out, &st) == 0 && S_ISDIR(st.st_mode))
return 1;
}
return 0;
}
snprintf(out, outsz, "%s/%s.ww", dir, path_form);
if (access(out, 0) == 0) {
*is_dir = 0;
return 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/<mod>/<x>_test.ww`
* entry makes srcd = lib/<mod>, so a self-named `import <mod>` would
* else file-hit the sibling lib/<mod>/<mod>.ww and fold it
* inline under the wrong module-reset → "package <mod> does not match
* import path <importer>". Two passes — directories first, files only
* if no directory matches anywhere — let lib/<mod>/ 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. */
/* Walk every ordered root for <root>/<path>/ only. A decoy
* <earlier-root>/<path>.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, int *is_dir)
size_t outsz)
{
for (int want_dir = 1; want_dir >= 0; want_dir--) {
const char *p = dirs;
while (*p) {
const char *e = strchr(p, ':');
@@ -231,12 +208,43 @@ locate_import(const char *dirs, const char *path_form, char *out,
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_in(dir, path_form, out, outsz))
return 1;
}
if (!e) break;
p = e + 1;
}
return 0;
}
/* CLI target compatibility: directory packages still win globally, then a
* bare target may resolve to <root>/<path>.ww. This function is never used
* while loading a source import. */
static int
locate_module(const char *dirs, const char *path_form, char *out,
size_t outsz, int *is_dir)
{
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;
}
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,22 +1027,21 @@ 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;
if (!g->pkg[pi].is_dir)
for (Node *package = imports->body; package;
package = package->next)
if (strcmp(package->module, leaf) == 0) {
@@ -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

View File

@@ -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
`<root>/<import-path>/`; it never probes `<root>/<import-path>.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 `<root>/<name>.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.

View File

@@ -284,12 +284,9 @@ fn importpathform(name: *u8, namelen: u64) *u8 = {
return buf.ptr;
};
// Try <dir>/<path>/ as a directory (wantdir != 0), else <dir>/<path>.ww
// as a file. Sets *isdir on hit. Symmetric with cstage locate_import_in
// for byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.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 <dir>/<path>/ as a directory (wantdir != 0), else
// <dir>/<path>.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,28 +330,13 @@ 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/<mod>/<mod>test.ww`
// entry makes srcd = lib/<mod>, so a self-named `import <mod>` would
// else file-hit the sibling lib/<mod>/<mod>.ww and fold it
// inline under the wrong module-reset. Two passes — directories first,
// files only if no directory matches anywhere — let lib/<mod>/ 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 <root>/<path>/ only. A sibling or earlier-root
// <path>.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;
@@ -364,13 +346,39 @@ fn locateimport(dirs: *u8, name: *u8, namelen: u64,
};
let seglen: u64 = q - p;
if (seglen > 0u64) {
let isdir: i32 = 0;
let hit: *u8 = locatein(dirs + p, seglen,
pathform, pflen, isdir, wantdir);
pathform, pflen, &isdir, 1i32);
if (hit != nil) { return hit; };
};
p = q + 1u64;
};
wantdir -= 1i32;
return nil;
};
// CLI target compatibility: directory packages win globally, then a bare
// target may resolve to <root>/<path>.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,26 +1248,22 @@ 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) {
if (self && !externalproduction) {
cerrpos(u.file, u.line, u.col);
cerr(": error: self-import: package '");
if (g.pkg[pi].path[0u64] != 0u8) {
@@ -1307,13 +1311,6 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
g.pkg[pi].deps[g.pkg[pi].ndeps] = di;
g.pkg[pi].ndeps += 1;
};
} else {
sepbindadd(bindings, 'F': u8, u.usepath, ipath);
if (sepscanfile(g, pi, ipath, searchpath, fv,
bindings, 0) < 0) {
return -1;
};
};
} else {
let lstart: u64 = 0u64;
let lk: u64 = 0u64;
@@ -1324,12 +1321,14 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
let leafp: *u8 = idp + lstart;
let leafn: u64 = idn - lstart;
let inlinepackage: bool = false;
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);
cerr(": error: cannot find package ");
@@ -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 = {