From 826e8bc97f3abad490b4e131d0ee39f61b32ed0c Mon Sep 17 00:00:00 2001 From: John Coffey Date: Fri, 18 Sep 2026 10:20:18 -0700 Subject: [PATCH] Strip tooling: remove dangling mod declarations left by ossify.py --- SPEC.md | 5 +++++ tools/fork/strip.py | 43 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/SPEC.md b/SPEC.md index ba41964..5876874 100644 --- a/SPEC.md +++ b/SPEC.md @@ -76,6 +76,11 @@ The wrapper is `tools/fork/strip.py`. Beyond `ossify.py` it: `v0.16.22` that's `crates/main`'s default features and 13 dependencies in `tests/Cargo.toml`: 14 edits. `ossify.py` doesn't touch manifests, so without this the stripped tree can't build; +- removes `mod` declarations left pointing at deleted Enterprise files. They + sit just outside the snippets `ossify.py` removes, so they survive it. At + `v0.16.22` there are 5: one in `common`, behind the test features, and four + in the integration tests. The normal build never meets them, but the tests + don't compile until they're gone; - verifies the result across every text file, not just Rust, and reports what was removed, the Cargo edits, upstream's Enterprise flags, and the feature gates left for §2.3 to replace. diff --git a/tools/fork/strip.py b/tools/fork/strip.py index 5d1123b..595eb74 100755 --- a/tools/fork/strip.py +++ b/tools/fork/strip.py @@ -15,7 +15,8 @@ What it does, in order (SPEC.md §2.2): word, so a malformed marker fails the run here instead. 3. Runs upstream's own remover, `resources/scripts/ossify.py` from the same ref, over every directory that holds Rust. -4. Turns the `enterprise` Cargo feature off wherever it's switched on: a +4. Removes `mod` declarations left pointing at deleted Enterprise files, then + turns the `enterprise` Cargo feature off wherever it's switched on: a crate's `default` list, and dependency `features` lists. Definitions of the feature are left alone. They're inert once nothing turns them on, and leaving them keeps each sync's diff small. @@ -178,6 +179,37 @@ def deactivate_enterprise(tree): return edits +def remove_dangling_mods(tree): + """ + Remove `mod x;` declarations whose file ossify.py deleted. + + An Enterprise-only file can be declared from shared code just outside the + snippet that ossify.py removes, so the declaration survives and points at + nothing. Only shared (AGPL) code is read here, and only for `mod` lines. A + declaration goes with the attribute lines directly above it, e.g. its + `#[cfg(...)]`. + """ + decl = re.compile(r'^\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;\s*(?://.*)?$') + removed = [] + for path in sorted(tree.rglob('*.rs')): + base = path.parent if path.name in ('mod.rs', 'lib.rs', 'main.rs') else path.parent / path.stem + lines = path.read_text(encoding='utf-8').split('\n') + keep = [] + for n, line in enumerate(lines, 1): + m = decl.match(line) + if m and not (base / f'{m.group(1)}.rs').is_file() and not (base / m.group(1) / 'mod.rs').is_file(): + dropped = [line] + while keep and keep[-1].strip().startswith('#['): + dropped.insert(0, keep.pop()) + removed.append({'file': str(path.relative_to(tree)), 'line': n, 'module': m.group(1), + 'lines': [l.strip() for l in dropped]}) + continue + keep.append(line) + if len(keep) != len(lines): + path.write_text('\n'.join(keep), encoding='utf-8') + return removed + + def verify(tree): """Independent of ossify.py. Returns a list of problems; empty means clean.""" problems = [] @@ -242,6 +274,7 @@ def write_report(out_dir, report): f'- Enterprise-only files removed or emptied: **{len(r["removed_files"])}**', f'- Enterprise-only snippets removed: **{sum(r["removed_snippets"].values())}** in {len(r["removed_snippets"])} files', f'- Cargo edits turning `enterprise` off: **{len(r["cargo_edits"])}**', + f'- Dangling module declarations removed: **{len(r["dangling_mods"])}**', f'- Verification: **{"clean" if not r["problems"] else f"{len(r["problems"])} problems"}**', f'- Left for the rebuilt features to replace: {sum(r["feature_gates"].values())} `enterprise` feature gates ' f'in {len(r["feature_gates"])} files; {sum(r["edition_checks"].values())} `is_enterprise_edition()` checks ' @@ -251,6 +284,7 @@ def write_report(out_dir, report): md.append(f'- Upstream schema flags {len(r["schema"]["objects"])} objects and {len(r["schema"]["fields"])} fields as Enterprise') md += ['', '## Removed files', ''] + [f'- `{f}`' for f in r['removed_files']] md += ['', '## Removed snippets', ''] + [f'- `{f}`: {n}' for f, n in r['removed_snippets'].items()] + md += ['', '## Dangling module declarations removed', ''] + [f'- `{d["file"]}:{d["line"]}`: `mod {d["module"]}` ({" / ".join(d["lines"])})' for d in r['dangling_mods']] md += ['', '## Cargo edits', ''] + [f'- `{e["file"]}:{e["line"]}`: `{e["before"]}` → `{e["after"]}`' for e in r['cargo_edits']] if r['schema']: md += ['', '## Flagged Enterprise in upstream\'s schema', '', '**Objects:** ' + ', '.join(f'`{o}`' for o in r['schema']['objects']), @@ -275,7 +309,7 @@ def main(): malformed = check_snippets_wellformed(tree) if malformed: write_report(args.out, {'ref': args.ref, 'commit': commit, 'removed_files': [], 'removed_snippets': {}, - 'cargo_edits': [], 'problems': malformed, 'feature_gates': {}, 'edition_checks': {}, + 'cargo_edits': [], 'dangling_mods': [], 'problems': malformed, 'feature_gates': {}, 'edition_checks': {}, 'schema': None, 'ossify_log': ''}) print('\n'.join(malformed), file=sys.stderr) fail('malformed snippet markers; nothing stripped', code=1) @@ -283,19 +317,20 @@ def main(): removed_files, removed_snippets = sel_inventory(tree) log = run_ossify(tree, rust_roots(tree)) edits = deactivate_enterprise(tree) + dangling = remove_dangling_mods(tree) problems = verify(tree) gates, checks = remaining_hooks(tree) report = { 'ref': args.ref, 'commit': commit, 'removed_files': removed_files, 'removed_snippets': removed_snippets, - 'cargo_edits': edits, 'problems': problems, + 'cargo_edits': edits, 'dangling_mods': dangling, 'problems': problems, 'feature_gates': gates, 'edition_checks': checks, 'schema': schema_flags(tree), 'ossify_log': log, } write_report(args.out, report) print(f'{args.ref} ({commit[:12]}): removed {len(removed_files)} files and ' - f'{sum(removed_snippets.values())} snippets, {len(edits)} Cargo edits, ' + f'{sum(removed_snippets.values())} snippets, {len(dangling)} dangling mods, {len(edits)} Cargo edits, ' f'{"verified clean" if not problems else f"{len(problems)} PROBLEMS"}. Report: {args.out}/STRIP-REPORT.md') sys.exit(1 if problems else 0)