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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
#!/usr/bin/env python3
"""Write the wsap_vtcore core as one wsap_vtcore.c and one wsap_vtcore.h.
wsap_vtcore.h joins the public headers. wsap_vtcore.c joins every source
file and puts each private header in at its first include. A host compiles
wsap_vtcore.c once, with any C11 compiler, and links libm and the thread
library.
amalgamate.py WSAP_VTCORE_DIR OUT_DIR [DEPFILE]
WSAP_VTCORE_DIR is the directory with src/ and include/. DEPFILE lists every file
read, for the build system.
"""
import re
import sys
from pathlib import Path
INCLUDE = re.compile(r'^\s*#\s*include\s+"([^"]+)"')
PUBLIC = ("wsap_vtcore_types.h", "wsap_vtcore_input.h", "wsap_vtcore.h")
class Joiner:
def __init__(self, root: Path):
self.root = root
# The sources find the public headers without the wsap_vtcore/ prefix.
self.search = (root / "src", root / "include" / "wsap_vtcore")
self.seen: set[Path] = set()
def resolve(self, name: str, base: Path) -> Path:
for d in (base,) + self.search:
p = (d / name).resolve()
if p.is_file():
return p
raise SystemExit(f"{base}: cannot find {name}")
def emit(self, path: Path, out: list[str]) -> None:
"""Copy path to out. Replace each local include with the file,
the first time only."""
self.seen.add(path)
rel = path.relative_to(self.root)
out.append(f"/* ---- {rel} ---- */\n")
for line in path.read_text().splitlines(keepends=True):
m = INCLUDE.match(line)
if not m:
out.append(line)
continue
dep = self.resolve(m.group(1), path.parent)
if dep not in self.seen:
self.emit(dep, out)
out.append(f"/* ---- end {rel} ---- */\n")
def sources(src: Path) -> list[Path]:
files = sorted(src.glob("*.c")) + sorted((src / "osc").glob("*.c"))
return [f.resolve() for f in files]
def main() -> int:
if len(sys.argv) not in (3, 4):
print(__doc__, file=sys.stderr)
return 2
root = Path(sys.argv[1]).resolve()
public = root / "include" / "wsap_vtcore"
out_dir = Path(sys.argv[2])
out_dir.mkdir(parents=True, exist_ok=True)
banner = "/* Generated by tools/amalgamate.py. Do not edit. */\n"
header = Joiner(root)
text = [banner]
header.emit(public / "wsap_vtcore.h", text)
(out_dir / "wsap_vtcore.h").write_text("".join(text))
body = Joiner(root)
text = [banner,
"/* The private headers have helpers that only some platforms or\n"
" * callers outside the core use. Clang warns about them here. */\n"
"#if defined(__clang__)\n"
'#pragma clang diagnostic ignored "-Wunused-function"\n'
"#endif\n",
'#include "wsap_vtcore.h"\n']
# The public headers are in wsap_vtcore.h, so the sources skip them.
body.seen.update((public / name).resolve() for name in PUBLIC)
for src in sources(root / "src"):
body.emit(src, text)
(out_dir / "wsap_vtcore.c").write_text("".join(text))
if len(sys.argv) == 4:
read = sorted(header.seen | body.seen)
deps = " ".join(str(p).replace(" ", "\\ ") for p in read)
Path(sys.argv[3]).write_text(f"{out_dir / 'wsap_vtcore.c'}: {deps}\n")
return 0
if __name__ == "__main__":
sys.exit(main())
|