|
45 | 45 |
|
46 | 46 | # ── parse `ninja -t commands` ─────────────────────────────────────────── |
47 | 47 | compiles = [] |
48 | | -for line in (BLD / "ninja-cmds.log").read_text().splitlines(): |
49 | | - line = line.strip() |
50 | | - if not line: |
| 48 | +DRIVE_ABS = re.compile(r"^[A-Za-z]:[\\/]") |
| 49 | +for raw in (BLD / "ninja-cmds.log").read_text().splitlines(): |
| 50 | + raw = raw.strip() |
| 51 | + if not raw: |
51 | 52 | continue |
| 53 | + # Windows (clang-cl / win64 nasm) lines carry backslash paths that shlex's |
| 54 | + # POSIX mode would treat as escapes; normalise to forward slashes first (no |
| 55 | + # meaningful backslash-escapes appear in these commands). Linux/macOS lines |
| 56 | + # have no backslashes so this is a no-op for them. |
| 57 | + is_win = ("clang-cl" in raw.lower()) or bool(re.search(r"[A-Za-z]:\\", raw)) |
| 58 | + line = raw.replace("\\", "/") if is_win else raw |
52 | 59 | try: |
53 | 60 | toks = shlex.split(line) |
54 | 61 | except ValueError: |
55 | 62 | continue |
56 | 63 | if not toks: |
57 | 64 | continue |
58 | | - tool = Path(toks[0]).name |
59 | | - if tool not in ("g++", "gcc", "cc", "c++", "clang", "clang++", "nasm"): |
| 65 | + tool_raw = Path(toks[0].strip('"')).name.lower() |
| 66 | + if tool_raw.endswith(".exe"): |
| 67 | + tool_raw = tool_raw[:-4] |
| 68 | + if tool_raw == "clang-cl": |
| 69 | + tool = "g++" # clang-cl builds .c and .cpp; source extension decides downstream |
| 70 | + elif tool_raw == "nasm": |
| 71 | + tool = "nasm" |
| 72 | + elif tool_raw in ("g++", "gcc", "cc", "c++", "clang", "clang++"): |
| 73 | + tool = "g++" if tool_raw in ("g++", "c++", "clang++") else "gcc" |
| 74 | + else: |
60 | 75 | continue |
61 | | - tool = "nasm" if tool == "nasm" else ("g++" if tool in ("g++", "c++", "clang++") else "gcc") |
62 | 76 | src = None; defs = set(); mflags = []; incs = []; std = None |
63 | 77 | skip = False |
64 | 78 | for i in range(1, len(toks)): |
65 | 79 | t = toks[i] |
66 | 80 | if skip: skip = False; continue |
67 | | - if t in ("-o", "-MF", "-MT", "-MQ"): skip = True; continue |
| 81 | + # gnu depfile/output flags that consume the next token (never on clang-cl, |
| 82 | + # where -MT is the *runtime* selector, not a depfile target) |
| 83 | + if not is_win and t in ("-o", "-MF", "-MT", "-MQ"): skip = True; continue |
| 84 | + if tool == "nasm" and t == "-o": skip = True; continue |
68 | 85 | if t == "-MD": |
69 | 86 | if tool == "nasm": skip = True |
70 | 87 | continue |
71 | 88 | if t == "-c": continue |
| 89 | + if is_win: |
| 90 | + if t.startswith(("/Fo", "/Fd", "/Fi", "/Fa", "/Fp")): continue # output artifacts |
| 91 | + if t.startswith("-clang:"): continue # depfile passthrough (-clang:-MD/-MF/-MT) |
| 92 | + if t in ("/D", "/I", "/U"): # msvc space-separated define/include |
| 93 | + if i + 1 < len(toks): |
| 94 | + nx = toks[i + 1].strip('"') |
| 95 | + if t == "/D": defs.add("-D" + nx) |
| 96 | + elif t == "/I": incs.append(nx) |
| 97 | + skip = True |
| 98 | + continue |
| 99 | + if t.startswith("/D"): defs.add("-D" + t[2:]); continue |
| 100 | + if t.startswith("/I"): incs.append(t[2:].strip('"')); continue |
| 101 | + if t.startswith("-imsvc"): # clang-cl system include |
| 102 | + d = t[len("-imsvc"):].strip('"') |
| 103 | + if d: incs.append(d) |
| 104 | + elif i + 1 < len(toks): incs.append(toks[i + 1].strip('"')); skip = True |
| 105 | + continue |
| 106 | + if t.startswith(("/std:", "-std:")): std = t; continue |
| 107 | + # msvc codegen/warning/runtime flags mcpp supplies itself — drop |
| 108 | + if t.startswith("/") or t in ("-TP", "-TC") or t.startswith(("-W", "-Q")): continue |
72 | 109 | if t == "-isystem": |
73 | | - # capture the dir (next token) as an ordinary include dir |
74 | 110 | if i + 1 < len(toks): incs.append(toks[i + 1].strip('"')) |
75 | 111 | skip = True; continue |
76 | 112 | if t.startswith("-std="): std = t; continue |
77 | 113 | if t.startswith("-D"): defs.add(t); continue |
78 | 114 | if t.startswith("-I"): incs.append(t[2:].strip('"')); continue |
79 | 115 | if t.startswith("-m") or t in ("-O3", "-O2"): mflags.append(t); continue |
80 | | - if re.search(r"\.(c|cc|cpp|cxx|asm|S)$", t) and not t.startswith("-"): |
| 116 | + if re.search(r"\.(c|cc|cpp|cxx|asm|S)$", t, re.I) and not t.startswith("-"): |
81 | 117 | src = t |
82 | 118 | if src is None: |
83 | 119 | continue |
84 | | - src_abs = src if src.startswith("/") else str(BLD / src) |
| 120 | + is_abs = src.startswith("/") or bool(DRIVE_ABS.match(src)) |
| 121 | + src_abs = src if is_abs else str(BLD / src) |
85 | 122 | compiles.append((tool, src_abs, frozenset(defs), tuple(mflags), incs, std)) |
86 | 123 | assert compiles, "no compile commands parsed" |
87 | 124 | print(f"parsed {len(compiles)} compiles") |
|
0 commit comments