mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 18:34:21 -10:00
The tree by what each part is, behind the top-level Makefile: - shims/ -> hosts/ (the browser, kernel, native and Wayland hosts) - web/ -> page/ (the page's source, build and serve), ports/mesa, ports/xwayland and ports/tint (the ports the page loads), site/ (static sites and the eject page) - tools/gexcc -> toolchain/gexcc, with the sysroot and busybox builds from tools/wine-wasm in toolchain/sysroot and toolchain/busybox, and one toolchain/pins (musl, the LP64 musl, LLVM, busybox) - tools/wine-wasm -> wine/: dist.sh (was wine64-dist.sh), wine.pins (64-bit Wine, and the 32-bit tree its wineserver builds from), libs.pins (the Unix libraries), the 32-bit-only pieces in wine/legacy32 (its own pins and legacy.mk, which wine/Makefile includes) - tools/*.py and tools/*.sh -> scripts/ (the GL generators in scripts/gl) - wine32/src.sh is scripts/fetch-pinned.sh, which takes the trees the old script marked as its own References follow the moves: paths from the tree's root by the map, paths from a script's own directory by where their target went. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_e0aa53aa95eb4b86910519b4d14812a5
106 lines
4.1 KiB
Python
Executable File
106 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Every script in the tree is reached from a make target, or is a public tool.
|
|
Called by: make lint.
|
|
|
|
scripts/lint-scripts.py [-v]
|
|
|
|
A script is a tracked file that is executable, ends in .sh, .py or .mjs, or is
|
|
a Makefile or .mk, outside third_party, crates/*/src, the page's and runtime's module sources
|
|
(page/src, page/runtime/src, hosts/browser/js's modules) and test/ (run.mjs's own
|
|
tree). It passes when one of these holds:
|
|
- the Makefile names it;
|
|
- a file that is reached names it (its path from the tree's root, its path
|
|
from the naming file's directory, or its file name when no other tracked
|
|
file has that name), transitively from the Makefile;
|
|
- scripts/public-tools lists it (a tool other repos or users call by path);
|
|
- a cargo test names it (crates/*/tests/*.rs: fixture builders).
|
|
A Makefile counts as named when its directory is (make -C DIR), and a Python
|
|
module when a script beside it imports it. Executables with binary content
|
|
(test fixtures) are not scripts.
|
|
Exit 1, with the list, when any script passes none. -v prints each script's
|
|
first caller.
|
|
"""
|
|
import os, re, subprocess, sys
|
|
|
|
root = subprocess.run(['git', 'rev-parse', '--show-toplevel'], capture_output=True, text=True,
|
|
check=True).stdout.strip()
|
|
os.chdir(root)
|
|
files = subprocess.run(['git', 'ls-files', '-s'], capture_output=True, text=True,
|
|
check=True).stdout.splitlines()
|
|
mode = {}
|
|
for line in files:
|
|
m, _, _, path = line.split(None, 3)
|
|
mode[path] = m
|
|
|
|
SOURCE = re.compile(r'^(third_party/|crates/[^/]+/src/|page/src/|page/runtime/src/|test/)')
|
|
def is_script(p):
|
|
if SOURCE.match(p):
|
|
return False
|
|
if p.startswith('hosts/browser/js/') and not os.path.basename(p).startswith('check-'):
|
|
return False
|
|
if p.endswith(('.sh', '.py', '.mjs', '.mk')) or (os.path.basename(p) == 'Makefile' and p != 'Makefile'):
|
|
return True
|
|
if mode[p] != '100755':
|
|
return False
|
|
with open(p, 'rb') as f:
|
|
return b'\0' not in f.read(4096) # an executable fixture (.exe, .elf) is no script
|
|
|
|
scripts = sorted(p for p in mode if is_script(p))
|
|
names = {}
|
|
for p in mode:
|
|
names.setdefault(os.path.basename(p), []).append(p)
|
|
|
|
def read(p):
|
|
try:
|
|
with open(p, encoding='utf-8', errors='replace') as f:
|
|
return f.read()
|
|
except (IsADirectoryError, FileNotFoundError):
|
|
return ''
|
|
|
|
def named_in(text, by, s):
|
|
if s in text:
|
|
return True
|
|
if os.path.basename(s) == 'Makefile' and re.search(r'(^|[\s/])' + re.escape(os.path.dirname(s)) + r'\b', text):
|
|
return True # make -C DIR
|
|
stem = os.path.basename(s)[:-3]
|
|
if s.endswith('.py') and by.endswith('.py') and os.path.dirname(s) == os.path.dirname(by) \
|
|
and re.search(r'^\s*(import|from)\s+' + re.escape(stem) + r'\b', text, re.M):
|
|
return True # a module beside its importer
|
|
rel = os.path.relpath(s, os.path.dirname(by) or '.')
|
|
if not rel.startswith('..') and re.search(r'(^|[\s"\'/=(])' + re.escape(rel) + r'\b', text):
|
|
return True
|
|
b = os.path.basename(s)
|
|
return len(names[b]) == 1 and len(b) > 4 and re.search(r'\b' + re.escape(b) + r'\b', text) is not None
|
|
|
|
public = [l.split()[0] for l in read('scripts/public-tools').splitlines()
|
|
if l.strip() and not l.startswith('#')]
|
|
reached = {}
|
|
todo = ['Makefile']
|
|
for p in public:
|
|
reached[p] = 'scripts/public-tools'
|
|
todo.append(p)
|
|
for p in mode:
|
|
if re.match(r'crates/[^/]+/tests/[^/]+\.rs$', p):
|
|
todo.append(p)
|
|
seen = set()
|
|
while todo:
|
|
by = todo.pop()
|
|
if by in seen:
|
|
continue
|
|
seen.add(by)
|
|
text = read(by)
|
|
for s in scripts:
|
|
if s not in reached and s != by and named_in(text, by, s):
|
|
reached[s] = by
|
|
todo.append(s)
|
|
|
|
bad = [s for s in scripts if s not in reached]
|
|
if '-v' in sys.argv:
|
|
for s in scripts:
|
|
print(f'{s}: {reached.get(s, "UNREACHED")}')
|
|
for s in bad:
|
|
print(f'lint-scripts: {s}: no make target, no caller reached from one, not a public tool')
|
|
print(f'lint-scripts: {len(scripts) - len(bad)} of {len(scripts)} scripts reached'
|
|
+ ('' if not bad else f', {len(bad)} not'))
|
|
sys.exit(1 if bad else 0)
|