Skip to content

Commit 23982ef

Browse files
committed
gen_descriptor: windows clang-cl ninja-cmds parser support
clang-cl.exe emits MSVC-driver tokens (/nologo /Fo -TP, /D + -D mixed, -imsvc system-includes, -clang: depfile passthrough) + backslash paths that shlex posix corrupts. Add a windows branch: normalize \->/, recognize clang-cl(.exe), map /D->-D, /I & -imsvc -> include, drop /Fo /Fd MSVC codegen/warning flags, keep the identical -D/-I/-mavx2 SIMD data. gnu (linux/macOS) path byte-unchanged. Local test vs captured windows ninja-cmds: parsed 448 compiles, exact flag reconstruction OK.
1 parent 1d5191b commit 23982ef

2 files changed

Lines changed: 52 additions & 10 deletions

File tree

.github/workflows/snapshot-windows-opencv.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,14 @@ jobs:
7373
b0528f5a1d379d59d4701cb28c36e22214cc51cf64594e5b56f2d3e6c0233095 \
7474
compat.opencv.wingen.lua || echo "gen_descriptor needs windows tweaks — raw artifacts uploaded for local iteration"
7575
if [ -f compat.opencv.wingen.lua ]; then
76+
# relabel linux->windows (xpm key + per-OS block) and drop linux ldflags
77+
# (-lpthread/-ldl don't exist on windows; the build spike surfaces any
78+
# real LNK deps, added back per-OS afterwards).
7679
sed -e 's/^ linux = {/ windows = {/' \
7780
-e 's/name = "compat.opencv"/name = "compat.opencvwin"/' \
81+
-e 's/ldflags = { "-lpthread", "-ldl" }/ldflags = { }/' \
7882
compat.opencv.wingen.lua > compat.opencvwin.lua || true
83+
echo "=== descriptor head ==="; head -50 compat.opencvwin.lua || true
7984
fi
8085
- uses: actions/upload-artifact@v4
8186
if: always()

tools/compat-opencv/gen_descriptor.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,43 +45,80 @@
4545

4646
# ── parse `ninja -t commands` ───────────────────────────────────────────
4747
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:
5152
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
5259
try:
5360
toks = shlex.split(line)
5461
except ValueError:
5562
continue
5663
if not toks:
5764
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:
6075
continue
61-
tool = "nasm" if tool == "nasm" else ("g++" if tool in ("g++", "c++", "clang++") else "gcc")
6276
src = None; defs = set(); mflags = []; incs = []; std = None
6377
skip = False
6478
for i in range(1, len(toks)):
6579
t = toks[i]
6680
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
6885
if t == "-MD":
6986
if tool == "nasm": skip = True
7087
continue
7188
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
72109
if t == "-isystem":
73-
# capture the dir (next token) as an ordinary include dir
74110
if i + 1 < len(toks): incs.append(toks[i + 1].strip('"'))
75111
skip = True; continue
76112
if t.startswith("-std="): std = t; continue
77113
if t.startswith("-D"): defs.add(t); continue
78114
if t.startswith("-I"): incs.append(t[2:].strip('"')); continue
79115
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("-"):
81117
src = t
82118
if src is None:
83119
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)
85122
compiles.append((tool, src_abs, frozenset(defs), tuple(mflags), incs, std))
86123
assert compiles, "no compile commands parsed"
87124
print(f"parsed {len(compiles)} compiles")

0 commit comments

Comments
 (0)