Skip to content

Commit bc5a13f

Browse files
committed
Remove orphaned version-bootstrap tooling
Continues the script-sprawl cleanup by dropping the "rootstrap module update" cluster that only the per-module ffi/dart split ever needed. - Delete update_modules.py: an undocumented, over-engineered bootstrapper for a new version's modules.yaml (token-vote category prediction plus multi-tier header synthesis). The documented workflow copies the previous version's modules.yaml by hand; this parallel path was never referenced by the guide and produced entries that had to be reviewed anyway. - Slim rootstrap_index.py (258 -> 186 lines) down to what resolve_type_dups actually uses -- pkg-config Requires direction and include-graph reachability. Drop the manifest persistence (to_manifest / from_manifest / open) and the update-only queries (header_exists / expand_glob / includedir_owners); the rootstrap_manifest.yaml baseline they served was never committed. - resolve_type_dups.py now builds the ownership index only from a live rootstrap (the manifest fallback had no producer left); it still degrades cleanly to dependency in-degree / file order when no rootstrap is present. - Remove modules_yaml naming helpers (lib_to_module_name, soname_of) that only update_modules / import_configs used. Verified: rootstrap_index's kept queries and resolve_type_dups' Ownership.decide run correctly against the Tizen 6.0 rootstrap; the generate path is unaffected -- 6.0 regeneration is byte-identical, dart analyze is clean, and check_consistency passes for all six versions.
1 parent 31b4a6d commit bc5a13f

4 files changed

Lines changed: 13 additions & 534 deletions

File tree

scripts/modules_yaml.py

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# Provides an ordered, trailing-comment-preserving load/dump cycle that
55
# reproduces the committed files byte-for-byte, plus small naming helpers
6-
# shared by update_modules.py and resolve_type_dups.py.
6+
# used by build_configs.py, generate_tizen.py and resolve_type_dups.py.
77

88
from __future__ import annotations
99

@@ -121,36 +121,3 @@ def to_upper_camel(name: str) -> str:
121121
return ''.join(p[:1].upper() + p[1:] for p in name.split('_') if p)
122122

123123

124-
def lib_to_module_name(libname: str) -> str:
125-
"""libcapi-appfw-app-common.so.0 -> capi_appfw_app_common"""
126-
name = libname
127-
if name.startswith('lib'):
128-
name = name[3:]
129-
name = re.sub(r'(\.so)(\.[0-9]+)*$', '', name)
130-
return name.replace('-', '_')
131-
132-
133-
def soname_of(base: str, files: list[str]) -> str:
134-
"""Pick the soname to record in modules.yaml for a library.
135-
136-
base is the unversioned name ('libfoo.so'); files are all matching
137-
filenames in the rootstrap. Committed entries use the runtime soname:
138-
the highest major version, in its shortest form ('libfoo.so.1' over
139-
'libfoo.so.1.3.3', but 'libfoo.so.0.1' when no single-major file
140-
exists). Falls back to base when only the unversioned symlink exists.
141-
"""
142-
versioned = []
143-
for f in files:
144-
m = re.fullmatch(re.escape(base) + r'((?:\.\d+)+)', f)
145-
if m:
146-
parts = tuple(int(p) for p in m.group(1)[1:].split('.'))
147-
versioned.append((parts, f))
148-
if not versioned:
149-
return base
150-
top_major = max(v[0][0] for v in versioned)
151-
in_major = [v for v in versioned if v[0][0] == top_major]
152-
# shortest version tuple wins (the actual soname); tie-break on value
153-
in_major.sort(key=lambda v: (len(v[0]), v[0]))
154-
return in_major[0][1]
155-
156-

scripts/resolve_type_dups.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
# python3 scripts/resolve_type_dups.py <version> [--assume-generated]
66
#
77
# Requires a local rootstrap, dart, and llvm (ffigen) — this is a maintainer
8-
# tool, not a CI check. Typical use right after update_modules.py +
9-
# generate_bindings.sh on a new version.
8+
# tool, not a CI check. Typical use right after adding a new version's
9+
# modules.yaml and running generate_bindings.sh.
1010
#
1111
# What it fixes automatically (dart analyze AMBIGUOUS_EXPORT):
1212
# struct/union/typedef duplicates -> deps entry on the non-owning module
@@ -237,14 +237,12 @@ def main():
237237
cfg_path = os.path.join(ROOT, 'configs', args.version, 'modules.yaml')
238238
cfg, comments = modules_yaml.load(cfg_path)
239239

240+
# The rootstrap index sharpens ownership decisions (pkg-config Requires and
241+
# include-graph direction); without it, decide() falls back to dependency
242+
# in-degree and file order.
240243
rootstrap = os.path.join(ROOT, 'rootstraps', args.version)
241-
manifest = os.path.join(ROOT, 'configs', args.version,
242-
'rootstrap_manifest.yaml')
243-
index = None
244-
if os.path.isdir(rootstrap):
245-
index = RootstrapIndex.scan(args.version, rootstrap)
246-
elif os.path.isfile(manifest):
247-
index = RootstrapIndex.from_manifest(manifest)
244+
index = RootstrapIndex.scan(args.version, rootstrap) \
245+
if os.path.isdir(rootstrap) else None
248246

249247
if not args.assume_generated and not args.analyze_only and not args.dry_run:
250248
print('Running initial full generation (generate_bindings.sh)...')

scripts/rootstrap_index.py

Lines changed: 5 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
#!/usr/bin/env python3
2-
# Rootstrap scanner and index used by update_modules.py / resolve_type_dups.py.
2+
# Rootstrap scanner used by resolve_type_dups.py to decide type ownership.
33
#
44
# Scans rootstraps/<version>/usr for shared libraries, headers, and pkg-config
5-
# files, and can persist the result as configs/<version>/rootstrap_manifest.yaml
6-
# so future bootstraps can diff against a version whose SDK is no longer
7-
# installed (rootstraps/ itself is gitignored).
5+
# files, and answers the two questions the duplicate-type resolver needs: does
6+
# one pkg-config Require another, and does one header transitively #include
7+
# another (include-graph reachability).
88

99
from __future__ import annotations
1010

1111
import os
1212
import re
1313
from collections import deque
1414

15-
import yaml
16-
1715
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
1816

1917
_LIB_RE = re.compile(r'^(lib[^/]+?\.so)((?:\.\d+)*)$')
@@ -25,21 +23,8 @@ class PcInfo:
2523
def __init__(self, name):
2624
self.name = name
2725
self.libs = [] # bare -l names, e.g. 'capi-appfw-application'
28-
self.includedirs = [] # relative to rootstrap root, e.g. 'usr/include/appfw'
2926
self.requires = [] # pc names from Requires:
3027

31-
def to_dict(self):
32-
return {'libs': self.libs, 'includedirs': self.includedirs,
33-
'requires': self.requires}
34-
35-
@classmethod
36-
def from_dict(cls, name, d):
37-
pc = cls(name)
38-
pc.libs = d.get('libs') or []
39-
pc.includedirs = d.get('includedirs') or []
40-
pc.requires = d.get('requires') or []
41-
return pc
42-
4328

4429
def _parse_pc(path: str, name: str) -> PcInfo:
4530
pc = PcInfo(name)
@@ -66,10 +51,6 @@ def expand(s: str, depth=0) -> str:
6651
for tok in expand(fields.get('Libs', '')).split():
6752
if tok.startswith('-l'):
6853
pc.libs.append(tok[2:])
69-
for tok in expand(fields.get('Cflags', '')).split():
70-
if tok.startswith('-I'):
71-
d = tok[2:].lstrip('/')
72-
pc.includedirs.append(d)
7354
for tok in re.split(r'[,\s]+', expand(fields.get('Requires', ''))):
7455
# skip version constraints like '>=' '1.0'
7556
if tok and not re.match(r'^[<>=!]|^\d', tok):
@@ -122,60 +103,7 @@ def scan(cls, version: str, rootstrap_dir: str) -> 'RootstrapIndex':
122103
idx.pc_by_lib.setdefault(base, name)
123104
return idx
124105

125-
def to_manifest(self, path: str):
126-
data = {
127-
'version': self.version,
128-
'libs': {b: sorted(fs) for b, fs in sorted(self.lib_files.items())},
129-
'headers': sorted(self.headers),
130-
'pkgconfig': {n: pc.to_dict() for n, pc in sorted(self.pcs.items())},
131-
}
132-
with open(path, 'w') as f:
133-
yaml.safe_dump(data, f, sort_keys=True, width=200)
134-
135-
@classmethod
136-
def from_manifest(cls, path: str) -> 'RootstrapIndex':
137-
with open(path) as f:
138-
data = yaml.safe_load(f)
139-
idx = cls(str(data['version']))
140-
idx.lib_files = {b: list(fs) for b, fs in (data.get('libs') or {}).items()}
141-
idx.headers = set(data.get('headers') or [])
142-
for name, d in (data.get('pkgconfig') or {}).items():
143-
idx.pcs[name] = PcInfo.from_dict(name, d)
144-
for name, pc in idx.pcs.items():
145-
for l in pc.libs:
146-
base = f'lib{l}.so'
147-
if base in idx.lib_files:
148-
idx.pc_by_lib.setdefault(base, name)
149-
return idx
150-
151-
@classmethod
152-
def open(cls, version: str, path: str) -> 'RootstrapIndex':
153-
"""Load from a rootstrap directory or a manifest file."""
154-
if os.path.isdir(path):
155-
return cls.scan(version, path)
156-
return cls.from_manifest(path)
157-
158-
# -- queries ---------------------------------------------------------------
159-
160-
def header_exists(self, ref: str) -> bool:
161-
"""ref as written in modules.yaml (basename or subpath)."""
162-
if ref in self.headers:
163-
return True
164-
suffix = '/' + ref
165-
return any(h.endswith(suffix) for h in self.headers)
166-
167-
def expand_glob(self, pattern: str) -> list[str]:
168-
"""Expand a ffigen include-directive glob like '**/AL/*.h'."""
169-
pat = pattern.lstrip('/')
170-
if pat.startswith('**/'):
171-
pat = pat[3:]
172-
rx = re.compile(
173-
'(^|/)' + re.escape(pat).replace(r'\*\*', '.*').replace(r'\*', '[^/]*') + '$')
174-
return sorted(h for h in self.headers if rx.search(h))
175-
176-
def includedir_owners(self, d: str) -> list[str]:
177-
"""pc names whose Cflags mention include dir d (relative path)."""
178-
return [n for n, pc in self.pcs.items() if d in pc.includedirs]
106+
# -- ownership queries -----------------------------------------------------
179107

180108
def pc_requires_transitively(self, a: str, b: str, depth: int = 2) -> bool:
181109
"""True if pc `a` requires pc `b` within `depth` hops."""

0 commit comments

Comments
 (0)