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:
214
cmd/ww/main.c
214
cmd/ww/main.c
@@ -178,65 +178,73 @@ import_path_form(const char *name, char *out, size_t outsz)
|
|||||||
out[i] = '\0';
|
out[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Symmetric with wwstage locatein for byte-id driver output (rule 10).
|
/* An import path names one directory package. There is deliberately no
|
||||||
* The legacy <dir>/<name>/<name>.ww form was dropped in task #22 —
|
* <dir>/<path>.ww branch here: literal or searched single-file roots are a
|
||||||
* directory-as-module enumeration replaces it, mirroring
|
* CLI compatibility concern handled by locate_module, never an import edge. */
|
||||||
* ref/hare/hare/module/srcs.ha (Hare has no fallback matching
|
|
||||||
* `foo/foo.ha`; a module IS the directory). */
|
|
||||||
static int
|
static int
|
||||||
locate_import_in(const char *dir, const char *path_form, char *out,
|
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;
|
struct stat st;
|
||||||
if (want_dir) {
|
snprintf(out, outsz, "%s/%s", dir, path_form);
|
||||||
snprintf(out, outsz, "%s/%s", dir, path_form);
|
if (stat(out, &st) == 0 && S_ISDIR(st.st_mode))
|
||||||
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;
|
|
||||||
return 1;
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #98: "a module IS the directory" — a directory-package on ANY entry
|
/* CLI target compatibility: directory packages still win globally, then a
|
||||||
* wins over a same-named sibling FILE on an EARLIER entry. The driver
|
* bare target may resolve to <root>/<path>.ww. This function is never used
|
||||||
* builds the searchpath srcd-first; a co-located `lib/<mod>/<x>_test.ww`
|
* while loading a source import. */
|
||||||
* 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. */
|
|
||||||
static int
|
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)
|
size_t outsz, int *is_dir)
|
||||||
{
|
{
|
||||||
for (int want_dir = 1; want_dir >= 0; want_dir--) {
|
if (locate_import(dirs, path_form, out, outsz)) {
|
||||||
const char *p = dirs;
|
*is_dir = 1;
|
||||||
while (*p) {
|
return 1;
|
||||||
const char *e = strchr(p, ':');
|
}
|
||||||
size_t n = e ? (size_t)(e - p) : strlen(p);
|
const char *p = dirs;
|
||||||
if (n > 0 && n < outsz) {
|
while (*p) {
|
||||||
char dir[1024];
|
const char *e = strchr(p, ':');
|
||||||
if (n >= sizeof dir) n = sizeof dir - 1;
|
size_t n = e ? (size_t)(e - p) : strlen(p);
|
||||||
memcpy(dir, p, n);
|
if (n > 0 && n < outsz) {
|
||||||
dir[n] = '\0';
|
char dir[1024];
|
||||||
if (locate_import_in(dir, path_form, out,
|
if (n >= sizeof dir) n = sizeof dir - 1;
|
||||||
outsz, is_dir, want_dir)) return 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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -521,7 +529,7 @@ struct seppkg {
|
|||||||
int failed; /* discovery/compile failure reaches this action */
|
int failed; /* discovery/compile failure reaches this action */
|
||||||
int test_support; /* compiler-generated -T support package */
|
int test_support; /* compiler-generated -T support package */
|
||||||
int loaded; /* directory membership/name loaded exactly once */
|
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 */
|
unsigned char context_state[SEP_MAXCONTEXT]; /* 0 new, 1 active, 2 checked */
|
||||||
struct ImportSet bindings; /* first context's canonical import bindings */
|
struct ImportSet bindings; /* first context's canonical import bindings */
|
||||||
int deps[SEP_MAXPKG]; /* direct-dep indices into sepgraph.pkg */
|
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
|
/* Canonical bindings make a shared package independent of which selected
|
||||||
* root reaches it first. Directory, folded-file, and inline bindings are all
|
* root reaches it first. Directory bindings and the raw-file inline
|
||||||
* part of the package action's source meaning. */
|
* compatibility binding are part of the package action's source meaning. */
|
||||||
static int
|
static int
|
||||||
sep_binding_add(struct ImportSet *bindings, char kind, const char *name,
|
sep_binding_add(struct ImportSet *bindings, char kind, const char *name,
|
||||||
const char *target)
|
const char *target)
|
||||||
@@ -1019,28 +1027,27 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char ipath[1024];
|
char ipath[1024];
|
||||||
int is_dir = 0;
|
|
||||||
int external_production = sep_external_production_import(
|
int external_production = sep_external_production_import(
|
||||||
&g->pkg[pi], name);
|
&g->pkg[pi], name);
|
||||||
int located = 0;
|
int located = 0;
|
||||||
if (external_production) {
|
if (external_production) {
|
||||||
snprintf(ipath, sizeof ipath, "%s", g->pkg[pi].entry);
|
snprintf(ipath, sizeof ipath, "%s", g->pkg[pi].entry);
|
||||||
is_dir = 1;
|
|
||||||
located = 1;
|
located = 1;
|
||||||
} else {
|
} else {
|
||||||
located = locate_import(searchpath, path_form, ipath,
|
located = locate_import(searchpath, path_form, ipath,
|
||||||
sizeof ipath, &is_dir);
|
sizeof ipath);
|
||||||
}
|
}
|
||||||
if (!located) {
|
if (!located) {
|
||||||
const char *dot = strrchr(name, '.');
|
const char *dot = strrchr(name, '.');
|
||||||
const char *leaf = dot ? dot + 1 : name;
|
const char *leaf = dot ? dot + 1 : name;
|
||||||
int inline_package = 0;
|
int inline_package = 0;
|
||||||
for (Node *package = imports->body; package;
|
if (!g->pkg[pi].is_dir)
|
||||||
package = package->next)
|
for (Node *package = imports->body; package;
|
||||||
if (strcmp(package->module, leaf) == 0) {
|
package = package->next)
|
||||||
inline_package = 1;
|
if (strcmp(package->module, leaf) == 0) {
|
||||||
break;
|
inline_package = 1;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
if (inline_package) {
|
if (inline_package) {
|
||||||
if (sep_binding_add(bindings, 'I', name, NULL) < 0)
|
if (sep_binding_add(bindings, 'I', name, NULL) < 0)
|
||||||
rc = -1;
|
rc = -1;
|
||||||
@@ -1050,7 +1057,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
rc = -1;
|
rc = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (is_dir) {
|
{
|
||||||
char *canon = realpath(ipath, NULL);
|
char *canon = realpath(ipath, NULL);
|
||||||
if (canon == NULL) {
|
if (canon == NULL) {
|
||||||
errorf(u->pos, "cannot canonicalize package '%s'", name);
|
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; }
|
if (g->pkg[pi].ndeps >= SEP_MAXPKG) { rc = -1; break; }
|
||||||
g->pkg[pi].deps[g->pkg[pi].ndeps++] = di;
|
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);
|
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
|
/* 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
|
* //ww:module-reset primary boundary. Imports were already resolved to
|
||||||
* ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead of the
|
* directory-package edges and their direct `.wwi` files were prepended;
|
||||||
* body); FILE imports fold in (intra-package split). */
|
* no source outside pi's sorted owned-source set may enter this unit. */
|
||||||
static int
|
static int
|
||||||
sep_emit_body(FILE *out, const char *path, struct ImportSet *visited,
|
sep_emit_body(FILE *out, const char *path, const char *modpath)
|
||||||
const char *searchpath, const char *modpath, const struct seppkg *pkg)
|
|
||||||
{
|
{
|
||||||
if (import_seen(visited, path)) return 0;
|
|
||||||
import_add(visited, path);
|
|
||||||
char *buf;
|
char *buf;
|
||||||
u64 len;
|
u64 len;
|
||||||
if (sep_slurp(path, &buf, &len) < 0) {
|
if (sep_slurp(path, &buf, &len) < 0) {
|
||||||
fprintf(stderr, "ww: cannot read %s\n", path);
|
fprintf(stderr, "ww: cannot read %s\n", path);
|
||||||
return -1;
|
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
|
/* #57: tag the primary body by its full dotted import path so the
|
||||||
* definer mangles == the importer reference; a root build (path "")
|
* definer mangles == the importer reference; a root build (path "")
|
||||||
* stays a bare reset (keeps bare main). */
|
* 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
|
if (fwrite(buf, 1, (size_t)len, out) != (size_t)len
|
||||||
|| fputc('\n', out) == EOF)
|
|| fputc('\n', out) == EOF)
|
||||||
bad = 1;
|
bad = 1;
|
||||||
free(uses);
|
|
||||||
freearena(a);
|
|
||||||
free(buf);
|
free(buf);
|
||||||
if (bad) {
|
if (bad) {
|
||||||
fprintf(stderr, "ww: cannot write package unit\n");
|
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
|
if (g->pkg[pi].emit_context < 0
|
||||||
|| g->pkg[pi].emit_context >= g->ncontext)
|
|| g->pkg[pi].emit_context >= g->ncontext)
|
||||||
return -1;
|
return -1;
|
||||||
const char *searchpath =
|
|
||||||
g->context[g->pkg[pi].emit_context].searchpath;
|
|
||||||
FILE *u = fopen(unitf, "wb");
|
FILE *u = fopen(unitf, "wb");
|
||||||
if (u == NULL) {
|
if (u == NULL) {
|
||||||
fprintf(stderr, "ww: cannot open %s\n", unitf);
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct ImportSet bodyvisit = {0};
|
|
||||||
int bodyrc = 0;
|
int bodyrc = 0;
|
||||||
if (g->pkg[pi].is_dir) {
|
if (g->pkg[pi].is_dir) {
|
||||||
for (int i = 0; i < g->pkg[pi].nsources && bodyrc == 0; i++)
|
for (int i = 0; i < g->pkg[pi].nsources && bodyrc == 0; i++)
|
||||||
bodyrc = sep_emit_body(u, g->pkg[pi].sources[i], &bodyvisit,
|
bodyrc = sep_emit_body(u, g->pkg[pi].sources[i],
|
||||||
searchpath, g->pkg[pi].path, &g->pkg[pi]);
|
g->pkg[pi].path);
|
||||||
} else {
|
} else {
|
||||||
bodyrc = sep_emit_body(u, g->pkg[pi].entry, &bodyvisit, searchpath,
|
bodyrc = sep_emit_body(u, g->pkg[pi].entry, g->pkg[pi].path);
|
||||||
g->pkg[pi].path, &g->pkg[pi]);
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < bodyvisit.n; i++) free(bodyvisit.paths[i]);
|
|
||||||
free(bodyvisit.paths);
|
|
||||||
if (fclose(u) != 0) {
|
if (fclose(u) != 0) {
|
||||||
fprintf(stderr, "ww: cannot close package unit %s\n", unitf);
|
fprintf(stderr, "ww: cannot close package unit %s\n", unitf);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1821,7 +1759,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
if (is_test) {
|
if (is_test) {
|
||||||
char tpath[1024];
|
char tpath[1024];
|
||||||
int tdir = 0;
|
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,
|
g->support_context = sep_context_for(g, toolsrcdir, NULL,
|
||||||
toolsrcdir);
|
toolsrcdir);
|
||||||
if (g->support_context < 0) return 1;
|
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];
|
char userpath[1024];
|
||||||
int userdir = 0;
|
int userdir = 0;
|
||||||
if (locate_import(g->context[products[i].context].searchpath,
|
if (locate_import(g->context[products[i].context].searchpath,
|
||||||
"test", userpath, sizeof userpath, &userdir)) {
|
"test", userpath, sizeof userpath)) {
|
||||||
|
userdir = 1;
|
||||||
(void)userdir;
|
(void)userdir;
|
||||||
char *uc = realpath(userpath, NULL);
|
char *uc = realpath(userpath, NULL);
|
||||||
if (tc != NULL && uc != 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);
|
search_path(incs, sp, sizeof sp);
|
||||||
char path_form[256];
|
char path_form[256];
|
||||||
import_path_form(name, path_form, sizeof path_form);
|
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
|
/* Returns the index past the last arg consumed for positionals (so callers
|
||||||
|
|||||||
@@ -2792,13 +2792,16 @@ package main;
|
|||||||
import lib.math;
|
import lib.math;
|
||||||
```
|
```
|
||||||
|
|
||||||
An import is translated from dots to path separators and resolved, with
|
An import is translated from dots to path separators and resolved only as a
|
||||||
directory packages preferred, through the entry package's directory, explicit
|
directory package through the entry package's directory, explicit `-I` roots in
|
||||||
`-I` roots in command order, and the toolchain source-library root. There is no
|
command order, and the toolchain source-library root. A same-named `.ww` file is
|
||||||
network or manifest fallback. The loader uses the compiler frontend's
|
neither a match nor a shadow for an import, so a later root containing the
|
||||||
imports-only parser, unions duplicate imports, byte-sorts direct edges, interns
|
directory wins over an earlier file decoy. There is no network, manifest, or
|
||||||
resolved directories by filesystem identity, and reports self-imports and
|
imported-file fallback. Explicit single-file CLI roots retain their raw-unit
|
||||||
stable cycle chains before compilation.
|
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,
|
A directory package consists of its immediate regular non-symlink `.ww` files,
|
||||||
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
|
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
|
command order, then the toolchain source root. Same and external variants of
|
||||||
one directory share that context; unrelated directory roots never acquire
|
one directory share that context; unrelated directory roots never acquire
|
||||||
lookup precedence from their request order. When multiple contexts reach one
|
lookup precedence from their request order. When multiple contexts reach one
|
||||||
canonical production package, the loader verifies that every directory,
|
canonical production package, the loader verifies that every directory import
|
||||||
folded-file, and inline import binding is identical before reusing its compile
|
binding is identical before reusing its compile action. A different binding is
|
||||||
action. A different binding is a deterministic package-resolution failure for
|
a deterministic package-resolution failure for the roots that reach it, rather
|
||||||
the roots that reach it, rather than a first-root-wins build.
|
than a first-root-wins build.
|
||||||
|
|
||||||
A production action failure blocks exactly the roots that reach it. A
|
A production action failure blocks exactly the roots that reach it. A
|
||||||
root-local compile or link failure does not suppress a successfully built
|
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,
|
workdir. It does not add build IDs, hashes, a CAS, an action graph, a scheduler,
|
||||||
or a manifest.
|
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
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
Five candidates were developed as coherent systems, not as feature bins.
|
||||||
|
|||||||
@@ -284,12 +284,9 @@ fn importpathform(name: *u8, namelen: u64) *u8 = {
|
|||||||
return buf.ptr;
|
return buf.ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try <dir>/<path>/ as a directory (wantdir != 0), else <dir>/<path>.ww
|
// Try <dir>/<path>/ as a directory (wantdir != 0), else
|
||||||
// as a file. Sets *isdir on hit. Symmetric with cstage locate_import_in
|
// <dir>/<path>.ww as a CLI target. Source-import loading calls only the
|
||||||
// for byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
// directory arm; the file arm belongs exclusively to locatemodule.
|
||||||
// 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).
|
|
||||||
fn locatein(dir: *u8, dirlen: u64,
|
fn locatein(dir: *u8, dirlen: u64,
|
||||||
pathform: *u8, pflen: u64, isdir: *i32, wantdir: i32) *u8 = {
|
pathform: *u8, pflen: u64, isdir: *i32, wantdir: i32) *u8 = {
|
||||||
let tail: u64 = 1u64; // NUL for the directory form
|
let tail: u64 = 1u64; // NUL for the directory form
|
||||||
@@ -333,44 +330,55 @@ fn locatein(dir: *u8, dirlen: u64,
|
|||||||
return nil;
|
return nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
// Walk every ordered root for <root>/<path>/ only. A sibling or earlier-root
|
||||||
// *isdir on hit.
|
// <path>.ww is neither a match nor a shadow: every source import identifies
|
||||||
//
|
// one canonical directory package.
|
||||||
// #98: "a module IS the directory" — a directory-package on ANY entry
|
fn locateimport(dirs: *u8, name: *u8, namelen: u64) *u8 = {
|
||||||
// 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 = {
|
|
||||||
let pathform: *u8 = importpathform(name, namelen);
|
let pathform: *u8 = importpathform(name, namelen);
|
||||||
let pflen: u64 = cstrlen(pathform);
|
let pflen: u64 = cstrlen(pathform);
|
||||||
let total: u64 = cstrlen(dirs);
|
let total: u64 = cstrlen(dirs);
|
||||||
let wantdir: i32 = 1i32;
|
let p: u64 = 0u64;
|
||||||
for (wantdir >= 0i32) {
|
for (p < total) {
|
||||||
let p: u64 = 0u64;
|
let q: u64 = p;
|
||||||
for (p < total) {
|
for (q < total) {
|
||||||
let q: u64 = p;
|
if (dirs[q] == ':') { break; };
|
||||||
for (q < total) {
|
q += 1u64;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
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 <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;
|
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");
|
cerr(": error: import path is too long (limit 255 bytes)\n");
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
let isdir: i32 = 0;
|
|
||||||
let externalproduction: bool = sepexternalproduction(
|
let externalproduction: bool = sepexternalproduction(
|
||||||
&g.pkg[pi], idp, idn);
|
&g.pkg[pi], idp, idn);
|
||||||
let ipath: *u8 = nil;
|
let ipath: *u8 = nil;
|
||||||
if (externalproduction) {
|
if (externalproduction) {
|
||||||
ipath = g.pkg[pi].entry;
|
ipath = g.pkg[pi].entry;
|
||||||
isdir = 1;
|
|
||||||
} else {
|
} else {
|
||||||
ipath = locateimport(searchpath, idp, idn, &isdir);
|
ipath = locateimport(searchpath, idp, idn);
|
||||||
};
|
};
|
||||||
if (ipath != nil) {
|
if (ipath != nil) {
|
||||||
if (isdir != 0) {
|
sepbindadd(bindings, 'D': u8, u.usepath, ipath);
|
||||||
sepbindadd(bindings, 'D': u8, u.usepath, ipath);
|
let self: bool = os.samefile(pathstr(ipath),
|
||||||
let self: bool = os.samefile(pathstr(ipath),
|
pathstr(g.pkg[pi].entry));
|
||||||
pathstr(g.pkg[pi].entry));
|
if (self && sepexternalname(&g.pkg[pi], idp, idn, true)) {
|
||||||
if (self && sepexternalname(&g.pkg[pi], idp, idn, true)) {
|
externalproduction = true;
|
||||||
externalproduction = true;
|
};
|
||||||
};
|
if (self && !externalproduction) {
|
||||||
if (self
|
cerrpos(u.file, u.line, u.col);
|
||||||
&& !externalproduction) {
|
cerr(": error: self-import: package '");
|
||||||
cerrpos(u.file, u.line, u.col);
|
if (g.pkg[pi].path[0u64] != 0u8) {
|
||||||
cerr(": error: self-import: package '");
|
cerr(pathstr(g.pkg[pi].path));
|
||||||
if (g.pkg[pi].path[0u64] != 0u8) {
|
} else { cerr(pathstr(g.pkg[pi].name)); };
|
||||||
cerr(pathstr(g.pkg[pi].path));
|
cerr("' cannot import itself\n");
|
||||||
} else { cerr(pathstr(g.pkg[pi].name)); };
|
return -1;
|
||||||
cerr("' cannot import itself\n");
|
};
|
||||||
return -1;
|
let runtimeproduction: bool = false;
|
||||||
};
|
if (externalproduction) {
|
||||||
let runtimeproduction: bool = false;
|
let gi: i32 = 0;
|
||||||
if (externalproduction) {
|
for (gi < g.n) {
|
||||||
let gi: i32 = 0;
|
if (g.pkg[gi].testsupport
|
||||||
for (gi < g.n) {
|
&& syntax.streq(pathstr(g.pkg[gi].path),
|
||||||
if (g.pkg[gi].testsupport
|
u.usepath)
|
||||||
&& syntax.streq(pathstr(g.pkg[gi].path),
|
&& os.samefile(pathstr(g.pkg[gi].entry),
|
||||||
u.usepath)
|
pathstr(ipath))) {
|
||||||
&& os.samefile(pathstr(g.pkg[gi].entry),
|
runtimeproduction = true;
|
||||||
pathstr(ipath))) {
|
|
||||||
runtimeproduction = true;
|
|
||||||
};
|
|
||||||
gi += 1;
|
|
||||||
};
|
};
|
||||||
|
gi += 1;
|
||||||
};
|
};
|
||||||
let nm: []u8 = alloc([], idn + 1u64)!;
|
};
|
||||||
let k: u64 = 0u64;
|
let nm: []u8 = alloc([], idn + 1u64)!;
|
||||||
for (k < idn) { nm[k] = idp[k]; k += 1u64; };
|
let k: u64 = 0u64;
|
||||||
nm[idn] = 0u8;
|
for (k < idn) { nm[k] = idp[k]; k += 1u64; };
|
||||||
let di: i32 = -1;
|
nm[idn] = 0u8;
|
||||||
if (externalproduction && !runtimeproduction) {
|
let di: i32 = -1;
|
||||||
let art: *u8 = appendlit(g.pkg[pi].artifact,
|
if (externalproduction && !runtimeproduction) {
|
||||||
"-production");
|
let art: *u8 = appendlit(g.pkg[pi].artifact,
|
||||||
di = sepfindoraddrole(g, nm.ptr, ipath, 1,
|
"-production");
|
||||||
SEP_ROLE_EXTERNAL_PRODUCTION, art);
|
di = sepfindoraddrole(g, nm.ptr, ipath, 1,
|
||||||
} else {
|
SEP_ROLE_EXTERNAL_PRODUCTION, art);
|
||||||
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 {
|
} else {
|
||||||
sepbindadd(bindings, 'F': u8, u.usepath, ipath);
|
di = sepfindoradd(g, nm.ptr, ipath, 1);
|
||||||
if (sepscanfile(g, pi, ipath, searchpath, fv,
|
};
|
||||||
bindings, 0) < 0) {
|
if (di < 0) { return -1; };
|
||||||
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 {
|
} else {
|
||||||
let lstart: u64 = 0u64;
|
let lstart: u64 = 0u64;
|
||||||
@@ -1324,11 +1321,13 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
|||||||
let leafp: *u8 = idp + lstart;
|
let leafp: *u8 = idp + lstart;
|
||||||
let leafn: u64 = idn - lstart;
|
let leafn: u64 = idn - lstart;
|
||||||
let inlinepackage: bool = false;
|
let inlinepackage: bool = false;
|
||||||
let pm: *syntax.node = imports.body;
|
if (g.pkg[pi].isdir == 0) {
|
||||||
for (pm != nil) {
|
let pm: *syntax.node = imports.body;
|
||||||
if (bytecmp(pm.nmod.ptr, pm.nmod.len: u64,
|
for (pm != nil) {
|
||||||
leafp, leafn) == 0) { inlinepackage = true; };
|
if (bytecmp(pm.nmod.ptr, pm.nmod.len: u64,
|
||||||
pm = pm.next;
|
leafp, leafn) == 0) { inlinepackage = true; };
|
||||||
|
pm = pm.next;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
if (!inlinepackage) {
|
if (!inlinepackage) {
|
||||||
cerrpos(u.file, u.line, u.col);
|
cerrpos(u.file, u.line, u.col);
|
||||||
@@ -1547,10 +1546,8 @@ fn sepvalidatemoduleclosure(g: *sepgraph, order: []i32, n: i32,
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Emit one of pi's own source files into the sep-unit under the
|
// Imported source never enters this body: its direct export data was
|
||||||
// //ww:module-reset primary boundary (so -c emits its decls, imported
|
// prepended above, while this package owns exactly its sorted source set.
|
||||||
// ==0). DIRECTORY imports are skipped (provided as `.wwi` ahead);
|
|
||||||
// FILE imports fold in (intra-package split).
|
|
||||||
fn sepwriteall(fd: i32, buf: *u8, n: u64) bool = {
|
fn sepwriteall(fd: i32, buf: *u8, n: u64) bool = {
|
||||||
match (os.writeall(fd, buf, n)) {
|
match (os.writeall(fd, buf, n)) {
|
||||||
case let wrote: i64 => return wrote == n: i64;
|
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,
|
fn sepemitbody(fd: i32, path: *u8, modpath: *u8) i32 = {
|
||||||
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);
|
|
||||||
let bufp: *u8;
|
let bufp: *u8;
|
||||||
let blen: u64;
|
let blen: u64;
|
||||||
bufp, blen = slurp(path);
|
bufp, blen = slurp(path);
|
||||||
@@ -1573,64 +1563,6 @@ fn sepemitbody(fd: i32, path: *u8, visit: *expctx, searchpath: *u8,
|
|||||||
cerr("ww: cannot read source\n");
|
cerr("ww: cannot read source\n");
|
||||||
return -1;
|
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
|
// #57: tag the primary body by its full dotted import path so the
|
||||||
// definer mangles == the importer reference; a root build (path "")
|
// definer mangles == the importer reference; a root build (path "")
|
||||||
// stays a bare reset (keeps bare main).
|
// stays a bare reset (keeps bare main).
|
||||||
@@ -1665,7 +1597,6 @@ fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8,
|
|||||||
unitf: *u8) i32 = {
|
unitf: *u8) i32 = {
|
||||||
if (g.pkg[pi].emitcontext < 0
|
if (g.pkg[pi].emitcontext < 0
|
||||||
|| g.pkg[pi].emitcontext >= g.ncontext) { return -1; };
|
|| 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
|
let u: i32 = os.open(pathstr(unitf), os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||||
if (u < 0) {
|
if (u < 0) {
|
||||||
cerr("ww: cannot open unit\n");
|
cerr("ww: cannot open unit\n");
|
||||||
@@ -1696,21 +1627,15 @@ fn sepcomposeunit(g: *sepgraph, pi: i32, scratch: *u8,
|
|||||||
};
|
};
|
||||||
k += 1;
|
k += 1;
|
||||||
};
|
};
|
||||||
let bv: expctx;
|
|
||||||
bv.out = u;
|
|
||||||
bv.dirs = searchpath;
|
|
||||||
bv.visit = nil;
|
|
||||||
let bodyrc: i32 = 0;
|
let bodyrc: i32 = 0;
|
||||||
if (g.pkg[pi].isdir != 0) {
|
if (g.pkg[pi].isdir != 0) {
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < g.pkg[pi].nsources && bodyrc == 0) {
|
for (i < g.pkg[pi].nsources && bodyrc == 0) {
|
||||||
bodyrc = sepemitbody(u, g.pkg[pi].sources[i], &bv, searchpath,
|
bodyrc = sepemitbody(u, g.pkg[pi].sources[i], g.pkg[pi].path);
|
||||||
g.pkg[pi].path, &g.pkg[pi]);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
bodyrc = sepemitbody(u, g.pkg[pi].entry, &bv, searchpath,
|
bodyrc = sepemitbody(u, g.pkg[pi].entry, g.pkg[pi].path);
|
||||||
g.pkg[pi].path, &g.pkg[pi]);
|
|
||||||
};
|
};
|
||||||
if (os.close(u) != 0) {
|
if (os.close(u) != 0) {
|
||||||
cerr("ww: cannot close package unit\n");
|
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
|
// when user source occupies that identity, the reserved graph alias keeps
|
||||||
// it distinct. The linker receives the same support archive closure.
|
// it distinct. The linker receives the same support archive closure.
|
||||||
if (istest != 0) {
|
if (istest != 0) {
|
||||||
let td: i32 = 0;
|
let td: i32 = 1;
|
||||||
let tp: *u8 = locateimport(toolsrcdir, "test".ptr,
|
let tp: *u8 = locateimport(toolsrcdir, "test".ptr,
|
||||||
"test".len: u64, &td);
|
"test".len: u64);
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
g.supportcontext = sepcontextfor(g, toolsrcdir, nil,
|
g.supportcontext = sepcontextfor(g, toolsrcdir, nil,
|
||||||
toolsrcdir);
|
toolsrcdir);
|
||||||
@@ -2250,11 +2175,10 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
};
|
};
|
||||||
producti = 0;
|
producti = 0;
|
||||||
for (producti < nproducts && !collision) {
|
for (producti < nproducts && !collision) {
|
||||||
let ud: i32 = 0;
|
|
||||||
let up: *u8 = locateimport(
|
let up: *u8 = locateimport(
|
||||||
g.context[products[producti].context].searchpath,
|
g.context[products[producti].context].searchpath,
|
||||||
"test".ptr,
|
"test".ptr,
|
||||||
"test".len: u64, &ud);
|
"test".len: u64);
|
||||||
if (up != nil && !os.samefile(pathstr(tp), pathstr(up))) {
|
if (up != nil && !os.samefile(pathstr(tp), pathstr(up))) {
|
||||||
collision = true;
|
collision = true;
|
||||||
};
|
};
|
||||||
@@ -2927,7 +2851,7 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let search: *u8 = buildsearchpath(selfdir, incs);
|
let search: *u8 = buildsearchpath(selfdir, incs);
|
||||||
return locateimport(search, name, nlen, isdir);
|
return locatemodule(search, name, nlen, isdir);
|
||||||
};
|
};
|
||||||
|
|
||||||
fn writeusage(fd: i32) void = {
|
fn writeusage(fd: i32) void = {
|
||||||
|
|||||||
Reference in New Issue
Block a user