Strip tooling: remove dangling mod declarations left by ossify.py

This commit is contained in:
2026-09-18 10:20:18 -07:00
parent e79036d097
commit 826e8bc97f
2 changed files with 44 additions and 4 deletions
+5
View File
@@ -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 `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 `tests/Cargo.toml`: 14 edits. `ossify.py` doesn't touch manifests, so without
this the stripped tree can't build; 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 - 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 was removed, the Cargo edits, upstream's Enterprise flags, and the feature
gates left for §2.3 to replace. gates left for §2.3 to replace.
+39 -4
View File
@@ -15,7 +15,8 @@ What it does, in order (SPEC.md §2.2):
word, so a malformed marker fails the run here instead. word, so a malformed marker fails the run here instead.
3. Runs upstream's own remover, `resources/scripts/ossify.py` from the same 3. Runs upstream's own remover, `resources/scripts/ossify.py` from the same
ref, over every directory that holds Rust. 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 crate's `default` list, and dependency `features` lists. Definitions of the
feature are left alone. They're inert once nothing turns them on, and feature are left alone. They're inert once nothing turns them on, and
leaving them keeps each sync's diff small. leaving them keeps each sync's diff small.
@@ -178,6 +179,37 @@ def deactivate_enterprise(tree):
return edits 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): def verify(tree):
"""Independent of ossify.py. Returns a list of problems; empty means clean.""" """Independent of ossify.py. Returns a list of problems; empty means clean."""
problems = [] 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 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'- 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'- 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'- 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'- 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 ' 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.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 files', ''] + [f'- `{f}`' for f in r['removed_files']]
md += ['', '## Removed snippets', ''] + [f'- `{f}`: {n}' for f, n in r['removed_snippets'].items()] 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']] md += ['', '## Cargo edits', ''] + [f'- `{e["file"]}:{e["line"]}`: `{e["before"]}` → `{e["after"]}`' for e in r['cargo_edits']]
if r['schema']: if r['schema']:
md += ['', '## Flagged Enterprise in upstream\'s schema', '', '**Objects:** ' + ', '.join(f'`{o}`' for o in r['schema']['objects']), 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) malformed = check_snippets_wellformed(tree)
if malformed: if malformed:
write_report(args.out, {'ref': args.ref, 'commit': commit, 'removed_files': [], 'removed_snippets': {}, 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': ''}) 'schema': None, 'ossify_log': ''})
print('\n'.join(malformed), file=sys.stderr) print('\n'.join(malformed), file=sys.stderr)
fail('malformed snippet markers; nothing stripped', code=1) fail('malformed snippet markers; nothing stripped', code=1)
@@ -283,19 +317,20 @@ def main():
removed_files, removed_snippets = sel_inventory(tree) removed_files, removed_snippets = sel_inventory(tree)
log = run_ossify(tree, rust_roots(tree)) log = run_ossify(tree, rust_roots(tree))
edits = deactivate_enterprise(tree) edits = deactivate_enterprise(tree)
dangling = remove_dangling_mods(tree)
problems = verify(tree) problems = verify(tree)
gates, checks = remaining_hooks(tree) gates, checks = remaining_hooks(tree)
report = { report = {
'ref': args.ref, 'commit': commit, 'ref': args.ref, 'commit': commit,
'removed_files': removed_files, 'removed_snippets': removed_snippets, '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, 'feature_gates': gates, 'edition_checks': checks,
'schema': schema_flags(tree), 'ossify_log': log, 'schema': schema_flags(tree), 'ossify_log': log,
} }
write_report(args.out, report) write_report(args.out, report)
print(f'{args.ref} ({commit[:12]}): removed {len(removed_files)} files and ' 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') f'{"verified clean" if not problems else f"{len(problems)} PROBLEMS"}. Report: {args.out}/STRIP-REPORT.md')
sys.exit(1 if problems else 0) sys.exit(1 if problems else 0)