1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
"""Require the shared library to export exactly the public header functions."""
import argparse
from pathlib import Path
import re
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--allow-export', action='append', default=[])
parser.add_argument('nm')
parser.add_argument('library')
parser.add_argument('headers', nargs='+')
args = parser.parse_args()
declarations = re.compile(
r"^[^\n;{}]*\b(yasl_\w+)\s*\([^;{}]*\);", re.MULTILINE
)
expected = set()
for header in args.headers:
source = re.sub(r"/\*.*?\*/|//[^\n]*", "", Path(header).read_text(), flags=re.DOTALL)
expected.update(declarations.findall(source))
if not expected:
raise SystemExit("No public function declarations found")
output = subprocess.check_output(
[args.nm, "-D", "--defined-only", "--format=posix", args.library], text=True
)
exported = {line.split()[0] for line in output.splitlines() if line.strip()}
missing = expected - exported
leaked = exported - expected - set(args.allow_export)
if missing or leaked:
if missing:
print("Missing public exports:", ", ".join(sorted(missing)))
if leaked:
print("Unexpected exports:", ", ".join(sorted(leaked)))
raise SystemExit(1)
print(f"Verified {len(expected)} public exports")
if __name__ == "__main__":
main()
|