Strip tooling: remove dangling mod declarations left by ossify.py
This commit is contained in:
@@ -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
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user