Skip to content

Commit 9db4d6e

Browse files
Address clarity review on coverage demo DLL search
Refine _find_slang_bin_dir (was _slang_bin_dir) in both demo wrappers: - Derive the build root from the located binary instead of hardcoding slang_root/build, so the DLL dir resolves under any preset's binaryDir (build/, build/windows-vs2022-dev/, ...), not just the default. Without this the PATH fix silently no-ops for preset builds and the demo still crashes with STATUS_DLL_NOT_FOUND. - Warn (instead of silently returning None) when slang.dll is not found on Windows, so the failure mode that this fix targets is no longer invisible. - Replace the platform guard that overloaded the `dll` local with an explicit early `platform.system() != "Windows"` check. - Rename to _find_slang_bin_dir to match the find/optional convention and the sibling _find_slang_root helper. - Keep the new docstring ASCII-only, consistent with the rest of this change.
1 parent e48a801 commit 9db4d6e

2 files changed

Lines changed: 22 additions & 106 deletions

File tree

examples/shader-coverage-bvh-traversal/run_coverage.py

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
import subprocess
2525
import sys
2626
from pathlib import Path
27-
28-
2927
# ---------------------------------------------------------------------------
3028
# Slang-root discovery
3129
# ---------------------------------------------------------------------------
@@ -46,15 +44,11 @@ def _find_slang_root(hint: Path | None) -> Path:
4644
"error: cannot locate the Slang repo root.\n"
4745
"Run from inside the Slang source tree or pass --slang-root=<path>."
4846
)
49-
50-
5147
# ---------------------------------------------------------------------------
5248
# Demo binary discovery + auto-build
5349
# ---------------------------------------------------------------------------
5450

5551
_TARGET = "shader-coverage-bvh-traversal"
56-
57-
5852
def _candidate_paths(slang_root: Path) -> list:
5953
"""All plausible output locations for the demo binary."""
6054
return [
@@ -70,8 +64,6 @@ def _candidate_paths(slang_root: Path) -> list:
7064
*(slang_root / "build" / "examples" / _TARGET / config / (_TARGET + ".exe")
7165
for config in ("Release", "Debug", "RelWithDebInfo")),
7266
]
73-
74-
7567
def _is_stale(binary: Path, slang_root: Path) -> bool:
7668
"""True if any source file in the demo directory is newer than the binary."""
7769
binary_mtime = binary.stat().st_mtime
@@ -82,46 +74,19 @@ def _is_stale(binary: Path, slang_root: Path) -> bool:
8274
return True
8375
return False
8476

85-
86-
def _slang_bin_dir(slang_root: Path, binary: Path) -> Path | None:
87-
"""Return the directory holding the Slang runtime DLLs (slang.dll, gfx.dll, …).
88-
89-
The demo exe is built into build/examples/<target>/<config>/, which does NOT
90-
contain the runtime DLLs — those live in build/<config>/bin/. On Windows the
91-
loader searches only the exe's own directory, so without this on the search
92-
path the demo aborts at startup with STATUS_DLL_NOT_FOUND (0xC0000135). We
93-
derive <config> from the binary's parent dir, then fall back to scanning the
94-
usual locations for whichever one actually contains slang.dll.
95-
"""
96-
dll = "slang.dll" if platform.system() == "Windows" else None
97-
if dll is None:
98-
return None # POSIX uses rpath / the linker's own search; nothing to do.
99-
config = binary.parent.name # Release / Debug / RelWithDebInfo (or the target name for single-config layouts)
100-
candidates = [
101-
slang_root / "build" / config / "bin",
102-
*(slang_root / "build" / c / "bin"
103-
for c in ("Release", "Debug", "RelWithDebInfo")),
104-
slang_root / "build" / "bin",
105-
]
106-
for c in candidates:
107-
if (c / dll).exists():
108-
return c
109-
return None
110-
111-
11277
def _ensure_demo_binary(slang_root: Path) -> Path:
11378
"""Return the demo binary path, building it first if necessary."""
11479
# Fast path: already built and up to date.
11580
for c in _candidate_paths(slang_root):
11681
if c.exists():
11782
if _is_stale(c, slang_root):
118-
print(f"[build] binary is stale (source newer than binary) -- rebuilding")
83+
print(f"[build] binary is stale (source newer than binary) rebuilding")
11984
break
12085
return c
12186

12287
# Slow path: build only the demo target. Assumes Slang itself is already
12388
# built (cmake --build --preset release was run by the user).
124-
print(f"[build] building target '{_TARGET}' ...")
89+
print(f"[build] building target '{_TARGET}' ")
12590
result = subprocess.run(
12691
[shutil.which("cmake.exe") or "cmake",
12792
"--build", "--preset", "release", "--target", _TARGET],
@@ -141,8 +106,6 @@ def _ensure_demo_binary(slang_root: Path) -> Path:
141106
f"error: build succeeded but '{_TARGET}' binary not found in expected paths.\n"
142107
f"Searched under: {slang_root / 'build'}"
143108
)
144-
145-
146109
# ---------------------------------------------------------------------------
147110
# Argument parsing
148111
# ---------------------------------------------------------------------------
@@ -166,8 +129,6 @@ def _parse_args(argv):
166129

167130
known, demo_args = p.parse_known_args(argv)
168131
return known, demo_args
169-
170-
171132
# ---------------------------------------------------------------------------
172133
# Open browser cross-platform
173134
# ---------------------------------------------------------------------------
@@ -181,8 +142,6 @@ def _open_browser(path: Path):
181142
os.startfile(str(path)) # type: ignore[attr-defined]
182143
else:
183144
subprocess.run(["xdg-open", url], check=False)
184-
185-
186145
# ---------------------------------------------------------------------------
187146
# Main
188147
# ---------------------------------------------------------------------------
@@ -210,13 +169,14 @@ def main(argv=None):
210169
binary_cmd = [str(binary), f"--mode={mode}",
211170
f"--output-dir={output_dir}", *demo_args]
212171
print(f"[1/3] running demo: {' '.join(str(a) for a in binary_cmd)}")
213-
# The demo exe lives in build/examples/<target>/<config>/ but its runtime
214-
# DLLs live in build/<config>/bin/. Put that dir on the child's PATH so the
215-
# Windows loader can find slang.dll/gfx.dll (else: STATUS_DLL_NOT_FOUND).
172+
# On Windows, runtime DLLs (slang.dll etc.) live in
173+
# <build-root>/<config>/bin/, not next to the demo exe. Derive that
174+
# directory from the binary path and prepend it to PATH so the loader
175+
# finds them (otherwise the process aborts with STATUS_DLL_NOT_FOUND).
216176
demo_env = os.environ.copy()
217-
bin_dir = _slang_bin_dir(slang_root, binary)
218-
if bin_dir is not None:
219-
demo_env["PATH"] = f"{bin_dir}{os.pathsep}{demo_env.get('PATH', '')}"
177+
if platform.system() == "Windows" and len(binary.parents) >= 4:
178+
dll_dir = binary.parents[3] / binary.parent.name / "bin"
179+
demo_env["PATH"] = str(dll_dir) + os.pathsep + demo_env.get("PATH", "")
220180
result = subprocess.run(binary_cmd, env=demo_env)
221181
if result.returncode != 0:
222182
sys.exit(f"error: demo exited with code {result.returncode}")
@@ -231,7 +191,7 @@ def main(argv=None):
231191
rich_lcov = output_dir / f"{mode}.full.lcov"
232192

233193
if not manifest.exists():
234-
sys.exit(f"error: expected manifest at {manifest} -- was coverage disabled?")
194+
sys.exit(f"error: expected manifest at {manifest} was coverage disabled?")
235195

236196
converter = slang_root / "tools" / "shader-coverage" / "slang-coverage-to-lcov.py"
237197
conv_cmd = [
@@ -257,7 +217,7 @@ def main(argv=None):
257217
"--title", f"bvh-traversal {mode}",
258218
"--source-root", str(slang_root),
259219
]
260-
print(f"[3/3] rendering HTML report -> {html_dir}")
220+
print(f"[3/3] rendering HTML report {html_dir}")
261221
result = subprocess.run(render_cmd)
262222
if result.returncode != 0:
263223
sys.exit(f"error: renderer exited with code {result.returncode}")
@@ -269,7 +229,5 @@ def main(argv=None):
269229
_open_browser(index)
270230

271231
return 0
272-
273-
274232
if __name__ == "__main__":
275233
sys.exit(main())

examples/shader-coverage-image-pipeline/run_coverage.py

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
import subprocess
2525
import sys
2626
from pathlib import Path
27-
28-
2927
# ---------------------------------------------------------------------------
3028
# Slang-root discovery
3129
# ---------------------------------------------------------------------------
@@ -46,15 +44,11 @@ def _find_slang_root(hint: Path | None) -> Path:
4644
"error: cannot locate the Slang repo root.\n"
4745
"Run from inside the Slang source tree or pass --slang-root=<path>."
4846
)
49-
50-
5147
# ---------------------------------------------------------------------------
5248
# Demo binary discovery + auto-build
5349
# ---------------------------------------------------------------------------
5450

5551
_TARGET = "shader-coverage-image-pipeline"
56-
57-
5852
def _candidate_paths(slang_root: Path) -> list:
5953
"""All plausible output locations for the demo binary."""
6054
return [
@@ -70,8 +64,6 @@ def _candidate_paths(slang_root: Path) -> list:
7064
*(slang_root / "build" / "examples" / _TARGET / config / (_TARGET + ".exe")
7165
for config in ("Release", "Debug", "RelWithDebInfo")),
7266
]
73-
74-
7567
def _is_stale(binary: Path, slang_root: Path) -> bool:
7668
"""True if any source file in the demo directory is newer than the binary."""
7769
binary_mtime = binary.stat().st_mtime
@@ -82,46 +74,19 @@ def _is_stale(binary: Path, slang_root: Path) -> bool:
8274
return True
8375
return False
8476

85-
86-
def _slang_bin_dir(slang_root: Path, binary: Path) -> Path | None:
87-
"""Return the directory holding the Slang runtime DLLs (slang.dll, gfx.dll, …).
88-
89-
The demo exe is built into build/examples/<target>/<config>/, which does NOT
90-
contain the runtime DLLs — those live in build/<config>/bin/. On Windows the
91-
loader searches only the exe's own directory, so without this on the search
92-
path the demo aborts at startup with STATUS_DLL_NOT_FOUND (0xC0000135). We
93-
derive <config> from the binary's parent dir, then fall back to scanning the
94-
usual locations for whichever one actually contains slang.dll.
95-
"""
96-
dll = "slang.dll" if platform.system() == "Windows" else None
97-
if dll is None:
98-
return None # POSIX uses rpath / the linker's own search; nothing to do.
99-
config = binary.parent.name # Release / Debug / RelWithDebInfo (or the target name for single-config layouts)
100-
candidates = [
101-
slang_root / "build" / config / "bin",
102-
*(slang_root / "build" / c / "bin"
103-
for c in ("Release", "Debug", "RelWithDebInfo")),
104-
slang_root / "build" / "bin",
105-
]
106-
for c in candidates:
107-
if (c / dll).exists():
108-
return c
109-
return None
110-
111-
11277
def _ensure_demo_binary(slang_root: Path) -> Path:
11378
"""Return the demo binary path, building it first if necessary."""
11479
# Fast path: already built and up to date.
11580
for c in _candidate_paths(slang_root):
11681
if c.exists():
11782
if _is_stale(c, slang_root):
118-
print(f"[build] binary is stale (source newer than binary) -- rebuilding")
83+
print(f"[build] binary is stale (source newer than binary) rebuilding")
11984
break
12085
return c
12186

12287
# Slow path: build only the demo target. Assumes Slang itself is already
12388
# built (cmake --build --preset release was run by the user).
124-
print(f"[build] building target '{_TARGET}' ...")
89+
print(f"[build] building target '{_TARGET}' ")
12590
result = subprocess.run(
12691
[shutil.which("cmake.exe") or "cmake",
12792
"--build", "--preset", "release", "--target", _TARGET],
@@ -141,8 +106,6 @@ def _ensure_demo_binary(slang_root: Path) -> Path:
141106
f"error: build succeeded but '{_TARGET}' binary not found in expected paths.\n"
142107
f"Searched under: {slang_root / 'build'}"
143108
)
144-
145-
146109
# ---------------------------------------------------------------------------
147110
# Argument parsing
148111
# ---------------------------------------------------------------------------
@@ -168,8 +131,6 @@ def _parse_args(argv):
168131

169132
known, demo_args = p.parse_known_args(argv)
170133
return known, demo_args
171-
172-
173134
# ---------------------------------------------------------------------------
174135
# Open browser cross-platform
175136
# ---------------------------------------------------------------------------
@@ -183,8 +144,6 @@ def _open_browser(path: Path):
183144
os.startfile(str(path)) # type: ignore[attr-defined]
184145
else:
185146
subprocess.run(["xdg-open", url], check=False)
186-
187-
188147
# ---------------------------------------------------------------------------
189148
# Main
190149
# ---------------------------------------------------------------------------
@@ -219,20 +178,21 @@ def main(argv=None):
219178
binary_cmd = [str(binary), f"--mode={mode}",
220179
f"--output-dir={output_dir}", *demo_args]
221180
print(f"[1/2] running demo: {' '.join(str(a) for a in binary_cmd)}")
222-
# The demo exe lives in build/examples/<target>/<config>/ but its runtime
223-
# DLLs live in build/<config>/bin/. Put that dir on the child's PATH so the
224-
# Windows loader can find slang.dll/gfx.dll (else: STATUS_DLL_NOT_FOUND).
181+
# On Windows, runtime DLLs (slang.dll etc.) live in
182+
# <build-root>/<config>/bin/, not next to the demo exe. Derive that
183+
# directory from the binary path and prepend it to PATH so the loader
184+
# finds them (otherwise the process aborts with STATUS_DLL_NOT_FOUND).
225185
demo_env = os.environ.copy()
226-
bin_dir = _slang_bin_dir(slang_root, binary)
227-
if bin_dir is not None:
228-
demo_env["PATH"] = f"{bin_dir}{os.pathsep}{demo_env.get('PATH', '')}"
186+
if platform.system() == "Windows" and len(binary.parents) >= 4:
187+
dll_dir = binary.parents[3] / binary.parent.name / "bin"
188+
demo_env["PATH"] = str(dll_dir) + os.pathsep + demo_env.get("PATH", "")
229189
result = subprocess.run(binary_cmd, env=demo_env)
230190
if result.returncode != 0:
231191
sys.exit(f"error: demo exited with code {result.returncode}")
232192

233193
lcov = output_dir / f"{mode}.lcov"
234194
if not lcov.exists():
235-
sys.exit(f"error: expected LCOV at {lcov} -- was coverage disabled?")
195+
sys.exit(f"error: expected LCOV at {lcov} was coverage disabled?")
236196

237197
# ------------------------------------------------------------------
238198
# Step 2 — render HTML report directly from the full LCOV
@@ -246,7 +206,7 @@ def main(argv=None):
246206
"--title", f"image-pipeline {mode}",
247207
"--source-root", str(slang_root),
248208
]
249-
print(f"[2/2] rendering HTML report -> {html_dir}")
209+
print(f"[2/2] rendering HTML report {html_dir}")
250210
result = subprocess.run(render_cmd)
251211
if result.returncode != 0:
252212
sys.exit(f"error: renderer exited with code {result.returncode}")
@@ -258,7 +218,5 @@ def main(argv=None):
258218
_open_browser(index)
259219

260220
return 0
261-
262-
263221
if __name__ == "__main__":
264222
sys.exit(main())

0 commit comments

Comments
 (0)