Skip to content

[M1-DET-01] Fix Windows CI: quote the trait_compile fixture compiler invocation - #35

Merged
offdev merged 5 commits into
masterfrom
fix/windows-ci-trait-compile-msvc-path
Sep 16, 2026
Merged

offdev merged 5 commits into
masterfrom
fix/windows-ci-trait-compile-msvc-path

Conversation

@offdev

@offdev offdev commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

What fails

The Windows x64 (MSVC 2022) job on master (run 34984988547, commit 07fc79e) fails in the four G-R8 trait compile-check fixtures — trait_compile_ok, trait_compile_reject_double, trait_compile_reject_unmarked, trait_compile_reject_bad_mark — each reporting:

G-R8 compile check failed [exit code no such file or directory (expected 0 — the fixture must compile)]

with empty compiler output. no such file or directory is CMake's literal RESULT_VARIABLE for a failed process launch — the compiler never started.

Root cause (two independent defects, both Windows-only)

1. The compiler path is re-split on whitespace. expect-compile-result.cmake.in interpolates the compiler into an unquoted execute_process(COMMAND @CXX@ ...) line. configure_file substitutes plain text, and unquoted text in the generated script is re-split on whitespace by the CMake parser, so the MSVC compiler path

C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe

becomes the argument C:/Program — which does not exist. The Linux/macOS compilers (/usr/bin/g++, /usr/bin/clang++) contain no spaces, which is why only the Windows job fails.

2. cl has no MSVC environment. Once the path is quoted and cl actually launches, it runs outside the VS generator's toolchain setup (every other test in the job is a pre-built executable or an MSBuild invocation, which set up the toolchain themselves). The runner's plain shell has no INCLUDE/LIB/PATH, so cl dies with fatal error C1083: Cannot open include file: 'cstdint'.

Fix (per commit)

  1. Quote the compiler invocation. laige_add_compile_check builds the invocation as a CMake list (one process argument per element) and serializes each element quoted into the generated script; the script runs execute_process(COMMAND ${_cmd}), whose list expansion passes each element as exactly one argument regardless of whitespace. Flag sets are unchanged (same tokens, same order, same semantics). This also future-proofs the fixtures against local Windows checkouts whose path contains spaces.
  2. Give the fixtures the MSVC environment. The Windows Test (unit) step in both workflows now imports the VS development environment into the step before ctest: vswhere locates the installation, VsDevCmd.bat (the canonical vcvars, present in every VS 2017+ install under Common7\Tools; called by relative name from that directory, so no path quoting is involved) sets INCLUDE/LIB/PATH etc., and its set dump is imported variable by variable. ctest → cmake -P → cl inherit it. Everything else in the job is unchanged. (First attempt used LaunchVsDevEnv.ps1, which does not exist on this runner image — VsDevCmd.bat is the stable, version-proof equivalent.)
  3. Same latent defect, one class below. expect-lint-result.cmake.in / expect-det-lint-result.cmake.in interpolate @PYTHON@ (the find_program result, a full interpreter path) unquoted into the generated execute_process. CI never hit it because the runner's python lives in C:/hostedtoolcache (no spaces); a local Windows checkout with python under C:/Program Files/Python3xx would fail identically. Quoted, aligning with the api/detcheck/compile-check scripts.

Verified

  • cmake -P end-to-end simulation with a space-containing compiler path: every argument arrives intact (rc=0).
  • Local g++ and clang++ trees: full ctest 55/55; ctest -R trait_compile 4/4 (also 4/4 in the ASan and TSan trees); lint/api/detcheck families 27/27 in the g++ tree.
  • Windows proof: this PR's ci:windows job (label applied) — the earlier red run is exactly what exposed defect 2; the green target is the VsDevCmd.bat commit.

…invocation

The G-R8 trait compile-check fixtures (trait_compile_ok /
trait_compile_reject_double / trait_compile_reject_unmarked /
trait_compile_reject_bad_mark) fail on the Windows x64 (MSVC 2022)
job: every one reports 'exit code no such file or directory' with
empty compiler output, i.e. the compiler process never launched.

Root cause: expect-compile-result.cmake.in interpolates the compiler
path into an unquoted execute_process COMMAND line. configure_file
substitutes plain text, and unquoted text in a generated script is
re-split on whitespace by the CMake parser — so the MSVC compiler
path (C:/Program Files/Microsoft Visual Studio/2022/Enterprise/
VC/Tools/MSVC/.../cl.exe) becomes the argument 'C:/Program', a
failed process launch that CMake reports as the literal result
'no such file or directory'. The Linux/macOS compilers
(/usr/bin/g++, /usr/bin/clang++) contain no spaces, which is why
only the Windows job fails.

Fix: build the compiler invocation as a CMake list (one process
argument per element) in laige_add_compile_check and serialize each
element quoted into the generated script; the script runs
execute_process(COMMAND ${_cmd}), whose list expansion passes each
element as exactly one argument regardless of whitespace. The flag
sets themselves are unchanged (same tokens, same order, same
semantics as before).

Verified: cmake -P end-to-end simulation with a space-containing
compiler path (every argument arrives intact); ctest -R trait_compile
green in the local g++ and clang++ trees (4/4 each); full ctest
green in both trees (55/55); trait_compile green in the ASan and
TSan trees (4/4 each). The Windows proof is the PR's ci:windows job.
Same latent defect as the trait_compile fixture fix, one class below:
expect-lint-result.cmake.in and expect-det-lint-result.cmake.in
interpolate @python@ (the find_program result, a full interpreter
path) unquoted into the generated execute_process COMMAND. CI never
hit it because the windows-2022 runner's python lives in
C:/hostedtoolcache (no spaces); a local Windows checkout with python
under 'C:/Program Files/Python3xx' would fail the same way. Quoting
aligns both templates with the api/detcheck/compile-check scripts.

Verified: ctest include-lint-*, determinism-lint-*, api-*, detcheck-*,
trait_compile* green in the local g++ tree (27/27); the generated
scripts carry the quoted interpreter path.
…ronment

With the compiler path quoted (previous commit), cl.exe now launches
from the generated cmake -P fixture scripts — but it runs outside the
VS generator's toolchain setup, so the runner's plain shell has no
MSVC environment: cl cannot find the CRT/STL headers and every
fixture dies with 'fatal error C1083: Cannot open include file:
cstdint'.

The Windows Test step in both workflows now loads the VS 2022
development environment in the same step before ctest (vswhere
locates the installation; LaunchVsDevEnv.ps1 -Arch amd64 sets
INCLUDE/LIB/PATH etc. in the current PowerShell process). ctest,
the cmake -P fixture scripts, and cl all inherit it; every other
step and test in the job is unchanged (they already worked without
the environment — only direct cl invocation needs it).

Documented in the ci.yml Windows note and the fixture template
header.
…onment

LaunchVsDevEnv.ps1 (the first attempt at importing the VS development
environment in the Windows Test step) does not exist on the
windows-2022 runner image used by this project, so the step died with
'not recognized as a name of a cmdlet, function, script file'.

Replace it with the canonical, version-stable mechanism: vswhere
locates the VS installation, VsDevCmd.bat (the original vcvars,
present in every VS 2017+ install under Common7\Tools) is called by
relative name from that directory (no path-quoting involved), and its
'set' dump is imported variable by variable into the step's
PowerShell process. ctest, the cmake -P fixture scripts, and cl.exe
inherit it, so the trait_compile_* fixtures find the CRT/STL headers.
Everything else in the job is unchanged.
…uirement

DOC-003: building.md's test list now covers the trait_compile_*
CTest fixtures, including the fact that on Windows they invoke cl.exe
directly from a generated cmake -P script and therefore need the MSVC
environment (VS Developer shell) in a local run — the CI Windows Test
step imports it via VsDevCmd.bat.
@offdev
offdev merged commit b6a65d6 into master Sep 16, 2026
11 checks passed
offdev added a commit that referenced this pull request Sep 17, 2026
…tion; crash-handler write() under Release -Werror (#42)

macOS (Intel + arm64) jobs:

samples/hello/hello-baseline.cpp:179 — std::min(emitted, baseline_.size()) mixes std::uint64_t with std::size_t. On macOS uint64_t is unsigned long long and size_t is unsigned long, so the template deduction is a hard AppleClang error. On Linux/Windows the two types alias, which is why every other P0 job compiled the same source. Fix: cast the size_t operand to uint64_t so both arguments are one type on every platform.

Determinism check job:

src/laige-core/logging.cpp:500 — (void)write(...) does not suppress glibc's warn_unused_result (the attribute is attached under fortification, i.e. optimized builds), and the engine's -Werror makes it fatal. That is why only the job's Release g++ tree (build-det-release) failed while Debug g++ and every clang tree compiled the same TU. Fix: consume the result — the crash notice is best-effort and there is no async-signal-safe fallback if the write fails (LOG-007).

Verification (local: g++ 16.2.1 / clang++ 22.1.8, x86-64 Linux):
- logging.cpp compiles clean under -Wall -Werror in Debug, Release, and Release+_FORTIFY_SOURCE=2 (the CI trigger); hello sample TUs clean under all four compiler/flag sets.
- Full ctest: 82/82 (incl. hello_baseline_fpx/fp32 + the mismatch/truncated/malformed/missing fixtures, replay and detcheck suites).
- The CI detcheck flow reproduced end to end: four targeted builds (g++ Debug, clang++ Debug, clang++ Debug+ASan, g++ Release), reference-baseline sanity on both backends, synthetic self-check, and pairs A (g++ Debug vs clang++ Debug) and B (ASan Debug vs Release) on both backends — all result=OK ticks=301.
- The macOS deduction error cannot be reproduced off-AppleClang (uint64_t == size_t on Linux/Windows); the P0 macOS jobs are the regression guard for it, as for the prior macOS CI fixes (#33, #35, #37, #39).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant