import os
import re
import shutil
from pathlib import Path
from typing import Iterable, List
import ci_utils
TARGET = os.environ.get("CRT_VERIFY_TARGET", "x86_64-pc-windows-msvc")
TARGET_DIR = Path(os.environ.get("CARGO_TARGET_DIR", "target/ci-windows-static-crt"))
FORBIDDEN_DLL_PREFIXES = (
"api-ms-win-crt-",
"msvcp",
"msvcr",
"ucrtbase",
"vcruntime",
)
TARGET_ARCHES = {
"aarch64-pc-windows-msvc": "arm64",
"i686-pc-windows-msvc": "x86",
"x86_64-pc-windows-msvc": "x64",
}
def target_arch() -> str:
arch = TARGET_ARCHES.get(TARGET)
if not arch:
ci_utils.fail(f"Unsupported MSVC verification target: {TARGET}")
return arch
def find_dumpbin() -> Path:
dumpbin = shutil.which("dumpbin")
if dumpbin:
return Path(dumpbin)
program_files_x86 = Path(
os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")
)
vswhere = (
program_files_x86
/ "Microsoft Visual Studio"
/ "Installer"
/ "vswhere.exe"
)
if not vswhere.exists():
ci_utils.fail("vswhere.exe not found and dumpbin.exe is not on PATH")
install_path = ci_utils.run(
[
str(vswhere),
"-latest",
"-products",
"*",
"-requires",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"-property",
"installationPath",
],
capture_output=True,
).stdout.strip()
if not install_path:
ci_utils.fail("Could not locate a Visual Studio installation with VC tools")
candidates = sorted(
Path(install_path).glob(
f"VC/Tools/MSVC/*/bin/Hostx64/{target_arch()}/dumpbin.exe"
)
)
if not candidates:
ci_utils.fail("dumpbin.exe not found inside the Visual Studio installation")
return candidates[-1]
def cache_bool(cache: Path, key: str) -> bool:
pattern = re.compile(rf"^{re.escape(key)}:BOOL=(ON|OFF)$", re.MULTILINE)
match = pattern.search(cache.read_text(encoding="utf-8"))
if not match:
ci_utils.fail(f"{key}:BOOL was not found in {cache}")
return match.group(1) == "ON"
def build_test_executables() -> List[Path]:
result = ci_utils.run(
[
"cargo",
"test",
"--no-run",
"--all-targets",
"--message-format=json",
"--target",
TARGET,
"--target-dir",
str(TARGET_DIR),
],
capture_output=True,
)
build_script = ci_utils.cargo_root_build_script(result.stdout)
out_dir = Path(build_script["out_dir"])
cache = out_dir / "build" / "CMakeCache.txt"
if not cache.is_file():
ci_utils.fail(f"Bundled Opus CMake cache not found at {cache}")
if not cache_bool(cache, "OPUS_STATIC_RUNTIME"):
ci_utils.fail(f"Bundled Opus was not configured with a static CRT in {cache}")
print(f"Verified OPUS_STATIC_RUNTIME=ON in {cache}")
target_root = (TARGET_DIR / TARGET).resolve()
executables: List[Path] = []
for message in ci_utils.cargo_json_messages(result.stdout):
if message.get("reason") != "compiler-artifact":
continue
executable = message.get("executable")
if not executable:
continue
path = Path(executable).resolve()
if path.suffix.lower() != ".exe":
continue
if target_root not in path.parents:
continue
executables.append(path)
deduped = list(dict.fromkeys(executables))
if not deduped:
ci_utils.fail("No target test executables were produced for import inspection")
return deduped
def imported_dlls(dumpbin: Path, executable: Path) -> List[str]:
output = ci_utils.run(
[str(dumpbin), "/nologo", "/dependents", str(executable)],
capture_output=True,
).stdout
imports = []
for line in output.splitlines():
candidate = line.strip().lower()
if candidate.endswith(".dll"):
imports.append(candidate)
if not imports:
ci_utils.fail(
f"dumpbin reported no recognizable DLL imports for {executable}; "
"refusing to treat an empty parse as success"
)
return imports
def forbidden_imports(imports: Iterable[str]) -> List[str]:
return sorted(
{
dll
for dll in imports
if dll.startswith(FORBIDDEN_DLL_PREFIXES)
}
)
def main() -> None:
if os.name != "nt":
ci_utils.fail("verify_windows_static_crt.py must run on Windows")
dumpbin = find_dumpbin()
print(f"Using dumpbin at: {dumpbin}")
executables = build_test_executables()
print(f"Inspecting {len(executables)} test executable(s)")
failures = []
for executable in executables:
with ci_utils.group(f"Inspect {executable.name}"):
imports = imported_dlls(dumpbin, executable)
for dll in imports:
print(f" {dll}")
forbidden = forbidden_imports(imports)
if forbidden:
failures.append((executable, forbidden))
if failures:
for executable, forbidden in failures:
print(f"{executable} imports forbidden CRT DLLs: {', '.join(forbidden)}")
ci_utils.fail("Static CRT verification failed")
print("Static CRT verification passed: no dynamic CRT DLL imports found.")
if __name__ == "__main__":
main()