diff --git a/.github/workflows/ci-pull.yml b/.github/workflows/ci-pull.yml index 3203b11..384cfc2 100644 --- a/.github/workflows/ci-pull.yml +++ b/.github/workflows/ci-pull.yml @@ -198,7 +198,30 @@ jobs: - name: Build run: cmake --build build --config Debug -j - name: Test (unit) - run: ctest --test-dir build -C Debug --output-on-failure + # The trait_compile_* fixtures invoke cl.exe DIRECTLY from a + # generated cmake -P script (execute_process), outside the VS + # generator's toolchain setup, so — unlike every other test in + # this job — they need the MSVC environment (INCLUDE/LIB/PATH) + # to find the CRT/STL headers; the plain runner shell lacks it + # and cl fails with C1083 on etc. This step imports + # the VS development environment into the current PowerShell + # process: 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 it, and its `set` dump + # is imported variable by variable. ctest, cmake -P, and cl all + # inherit it. Everything else in this step is unchanged. + run: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property installationPath + Push-Location (Join-Path $vs "Common7\Tools") + $envDump = cmd /c "call VsDevCmd.bat -arch=amd64 -host_arch=amd64 >nul && set" + Pop-Location + foreach ($line in $envDump) { + if ($line -match '^([A-Za-z_][A-Za-z0-9_]*)=(.*)$') { + Set-Item -Path "env:$($matches[1])" -Value $matches[2] + } + } + ctest --test-dir build -C Debug --output-on-failure macos-arm64: name: macOS arm64 (AppleClang) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 810661a..f0b09ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,13 @@ # would build every configuration from the canonical `cmake --build build # -j`. The Windows job therefore pins the canonical Debug configuration # explicitly on the build and test steps (--config Debug / -C Debug); the -# single-config platforms run the canonical commands verbatim. +# single-config platforms run the canonical commands verbatim. The +# Windows Test step additionally imports the VS 2022 development +# environment (vswhere + VsDevCmd.bat, same step, before ctest) because +# the trait_compile_* fixtures invoke cl.exe directly from a generated +# cmake -P script — outside the VS generator's toolchain setup — and +# need the MSVC environment (INCLUDE/LIB/PATH) to find the CRT/STL +# headers. # # AC-6.1 (same source tree, same feature set, no per-OS feature flags): no # build job passes feature flags; every build job builds the identical @@ -221,7 +227,30 @@ jobs: - name: Build run: cmake --build build --config Debug -j - name: Test (unit) - run: ctest --test-dir build -C Debug --output-on-failure + # The trait_compile_* fixtures invoke cl.exe DIRECTLY from a + # generated cmake -P script (execute_process), outside the VS + # generator's toolchain setup, so — unlike every other test in + # this job — they need the MSVC environment (INCLUDE/LIB/PATH) + # to find the CRT/STL headers; the plain runner shell lacks it + # and cl fails with C1083 on etc. This step imports + # the VS development environment into the current PowerShell + # process: 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 it, and its `set` dump + # is imported variable by variable. ctest, cmake -P, and cl all + # inherit it. Everything else in this step is unchanged. + run: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property installationPath + Push-Location (Join-Path $vs "Common7\Tools") + $envDump = cmd /c "call VsDevCmd.bat -arch=amd64 -host_arch=amd64 >nul && set" + Pop-Location + foreach ($line in $envDump) { + if ($line -match '^([A-Za-z_][A-Za-z0-9_]*)=(.*)$') { + Set-Item -Path "env:$($matches[1])" -Value $matches[2] + } + } + ctest --test-dir build -C Debug --output-on-failure macos-arm64: name: macOS arm64 (AppleClang) diff --git a/docs/getting-started/building.md b/docs/getting-started/building.md index 9be4a34..243a92e 100644 --- a/docs/getting-started/building.md +++ b/docs/getting-started/building.md @@ -83,6 +83,15 @@ Notes: `TSAN_OPTIONS=halt_on_error=1` (wired in `tests//CMakeLists.txt` when `LAIGE_TSAN=ON`), so a data race makes `ctest` fail with a non-zero exit — no extra environment setup needed. +- G-R8 trait compile checks (`trait_compile_*`, `tests/laige-sim`): four + CTest fixtures that compile (not link, not run) one translation unit + each with the engine policy flags and assert both the exit code and — + for the negative fixtures — the actionable G-R8 message in the compiler + output. On Windows they invoke `cl.exe` directly from a generated + `cmake -P` script, outside the VS generator's toolchain setup, so run + them from a VS Developer shell (or any shell with the MSVC environment + loaded: `INCLUDE`/`LIB`/`PATH` set); CI's Windows Test step imports it + via `VsDevCmd.bat` (see `.github/workflows/ci.yml`). ## Build trees and artifacts diff --git a/tests/laige-sim/CMakeLists.txt b/tests/laige-sim/CMakeLists.txt index 92cfaad..975278a 100644 --- a/tests/laige-sim/CMakeLists.txt +++ b/tests/laige-sim/CMakeLists.txt @@ -215,23 +215,36 @@ add_test(NAME determinism_mode # expect-compile-result.cmake.in for the rationale. function(laige_add_compile_check name source expect_ok fragment) if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") - set(_flags "-std=c++20 -Wall -Werror -fno-exceptions -fno-rtti" - " -ffp-contract=off -fno-associative-math") - set(_includes "-I${CMAKE_SOURCE_DIR}/src/laige-sim/include" - "-I${CMAKE_SOURCE_DIR}/src/laige-core/include") + set(_flags -std=c++20 -Wall -Werror -fno-exceptions -fno-rtti + -ffp-contract=off -fno-associative-math) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(_flags "/std:c++20 /W4 /WX /EHs- /EHc- /GR-" - " /fp:precise /D _HAS_EXCEPTIONS=0") - set(_includes "/I${CMAKE_SOURCE_DIR}/src/laige-sim/include" - "/I${CMAKE_SOURCE_DIR}/src/laige-core/include") + set(_flags /std:c++20 /W4 /WX /EHs- /EHc- /GR- + /fp:precise /D _HAS_EXCEPTIONS=0) else() message(FATAL_ERROR "no compile-check flags for '${CMAKE_CXX_COMPILER_ID}'") endif() - set(CXX "${CMAKE_CXX_COMPILER}") - set(CXXFLAGS "${_flags} ${_includes}") + set(_includes "-I${CMAKE_SOURCE_DIR}/src/laige-sim/include" + "-I${CMAKE_SOURCE_DIR}/src/laige-core/include") + # The compiler invocation is a CMake list: exactly one process + # argument per element. The generated script quotes each element + # (CMD_TEXT below) because configure_file substitutes plain text and + # unquoted text in the generated execute_process call would be + # re-split on whitespace — which breaks any argument containing + # spaces. The MSVC compiler path on Windows + # ("C:/Program Files/Microsoft Visual Studio/.../cl.exe") is the + # canonical case: split at the first space it becomes the argument + # "C:/Program", a failed process launch that CMake reports as the + # result "no such file or directory" with no compiler output. + set(_cmd "${CMAKE_CXX_COMPILER}" ${_flags} ${_includes} + "-c" "${CMAKE_CURRENT_SOURCE_DIR}/compile_fail/${source}" + "-o" "${CMAKE_CURRENT_BINARY_DIR}/compile_check_${name}.o") + set(CMD_TEXT "") + foreach(_arg IN LISTS _cmd) + string(REPLACE "\"" "\\\"" _arg_quoted "${_arg}") + string(APPEND CMD_TEXT "\"${_arg_quoted}\" ") + endforeach() set(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/compile_fail/${source}") - set(OBJ "${CMAKE_CURRENT_BINARY_DIR}/compile_check_${name}.o") set(EXPECT_OK "${expect_ok}") set(FRAGMENT "${fragment}") configure_file( diff --git a/tests/laige-sim/expect-compile-result.cmake.in b/tests/laige-sim/expect-compile-result.cmake.in index a70d9a0..ad87bf8 100644 --- a/tests/laige-sim/expect-compile-result.cmake.in +++ b/tests/laige-sim/expect-compile-result.cmake.in @@ -15,9 +15,26 @@ # # The script exits non-zero (failing the CTest test) on any mismatch # and prints the full compiler output for diagnosis. +# +# _cmd is a quoted CMake list — one element per process argument. +# Quoting is load-bearing: configure_file substitutes plain text, and +# unquoted text in an execute_process call is re-split on whitespace, +# which would break any argument containing spaces (the MSVC compiler +# path "C:/Program Files/Microsoft Visual Studio/.../cl.exe" on +# Windows). The list expansion below passes each element as exactly +# one argument regardless of its content. +# +# MSVC note: cl launched this way runs outside the VS generator's +# toolchain setup, so the surrounding environment MUST already carry +# the MSVC INCLUDE/LIB/PATH (or cl fails with C1083 on etc.). +# The CI Windows Test step imports the VS development environment into +# the step before ctest (vswhere + VsDevCmd.bat — see +# .github/workflows/ci.yml); a local Windows run needs the same +# (e.g. a VS Developer PowerShell). +set(_cmd @CMD_TEXT@) execute_process( - COMMAND @CXX@ @CXXFLAGS@ -c "@SOURCE@" -o "@OBJ@" + COMMAND ${_cmd} RESULT_VARIABLE _rc OUTPUT_VARIABLE _out ERROR_VARIABLE _err diff --git a/tests/tools/expect-det-lint-result.cmake.in b/tests/tools/expect-det-lint-result.cmake.in index fa71bda..21c1ec3 100644 --- a/tests/tools/expect-det-lint-result.cmake.in +++ b/tests/tools/expect-det-lint-result.cmake.in @@ -13,7 +13,12 @@ # prints the full lint output for diagnosis. execute_process( - COMMAND @PYTHON@ "@LINT@" --root "@ROOT@" + # "@PYTHON@" is quoted like every other generated-script command (the + # rationale is in expect-lint-result.cmake.in): unquoted text in an + # execute_process call is re-split on whitespace, which would break a + # python interpreter path containing spaces on a local Windows + # checkout. + COMMAND "@PYTHON@" "@LINT@" --root "@ROOT@" RESULT_VARIABLE _rc OUTPUT_VARIABLE _out ERROR_VARIABLE _err diff --git a/tests/tools/expect-lint-result.cmake.in b/tests/tools/expect-lint-result.cmake.in index c31bbc1..6ebd14f 100644 --- a/tests/tools/expect-lint-result.cmake.in +++ b/tests/tools/expect-lint-result.cmake.in @@ -13,7 +13,13 @@ # prints the full lint output for diagnosis. execute_process( - COMMAND @PYTHON@ "@LINT@" --root "@ROOT@" + # "@PYTHON@" is quoted like every other generated-script command: + # configure_file substitutes plain text, and unquoted text in an + # execute_process call is re-split on whitespace, which would break a + # python interpreter path containing spaces (e.g. "C:/Program + # Files/Python3xx/python.exe" on a local Windows checkout; the CI + # runner's hostedtoolcache path has none, so this only bites locally). + COMMAND "@PYTHON@" "@LINT@" --root "@ROOT@" RESULT_VARIABLE _rc OUTPUT_VARIABLE _out ERROR_VARIABLE _err