From 1a5a3ae929d938cb6961828b945c7166a068bfba Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:21:19 +0200 Subject: [PATCH 1/8] Let a native toolchain configure --- CMakeLists.txt | 34 +++++++++++++++++++++++----------- code/CMakeLists.txt | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac8022f7..c2e7cbfc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ option(OPENTS_OFFICIAL_BUILD "Build as an official release of the declared versi option(OPENTS_EXPERIMENTAL_CLANG_CL "Build with clang-cl using the MSVC ABI" OFF) option(OPENTS_EXPERIMENTAL_X64 "Configure an unsupported 64-bit Windows build" OFF) +option(OPENTS_EXPERIMENTAL_NATIVE "Configure an unsupported native build for the host platform" OFF) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") @@ -25,23 +26,34 @@ elseif(MSVC) if(MSVC_VERSION LESS 1930) message(FATAL_ERROR "OpenTS requires MSVC 19.30 or newer.") endif() - if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4) - # A save records pointer identities at a fixed width, but the members and raw - # structures around them still travel at the build's own widths, and the packed - # version stamp that saves and network packets carry is the same either way. - message(WARNING - "OpenTS: this build has ${CMAKE_SIZEOF_VOID_P}-byte pointers. Saved games it " - "writes are not interchangeable with a supported 32-bit build's, and nothing in " - "the version stamp distinguishes them.") - endif() +elseif(OPENTS_EXPERIMENTAL_NATIVE) + message(STATUS "OpenTS: configuring an unsupported native build for the host platform.") + + # The tree declares imports and exports with __declspec throughout, which clang only + # accepts with the Microsoft extensions enabled. + add_compile_options(-fms-extensions) else() message(FATAL_ERROR "OpenTS requires the Visual Studio 2022 MSVC toolchain. " "For the unsupported clang-cl experiment, configure with " - "-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-cl-msvc.cmake.") + "-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-cl-msvc.cmake. " + "For the unsupported native build, configure with " + "-DOPENTS_EXPERIMENTAL_NATIVE=ON.") endif() -enable_language(RC) +if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4) + # A save records pointer identities at a fixed width, but the members and raw + # structures around them still travel at the build's own widths, and the packed + # version stamp that saves and network packets carry is the same either way. + message(WARNING + "OpenTS: this build has ${CMAKE_SIZEOF_VOID_P}-byte pointers. Saved games it " + "writes are not interchangeable with a supported 32-bit build's, and nothing in " + "the version stamp distinguishes them.") +endif() + +if(WIN32) + enable_language(RC) +endif() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 137d06855..e64f0c139 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -2,7 +2,7 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) # OpenTS supports 32-bit (x86) builds. A 64-bit build is an unsupported experiment. -if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND NOT OPENTS_EXPERIMENTAL_X64) +if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND NOT OPENTS_EXPERIMENTAL_X64 AND NOT OPENTS_EXPERIMENTAL_NATIVE) message(FATAL_ERROR "OpenTS must be built as 32-bit x86. Reconfigure with -A Win32. " "For the unsupported 64-bit experiment, configure with " From 11e74fe23915e1e75bbc47abf0edf9ebac3a23e5 Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:21:19 +0200 Subject: [PATCH 2/8] Build the test harnesses with a native toolchain --- tests/CMakeLists.txt | 51 ++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a7d3ef23a..8591cdbdc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,11 +8,16 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test-bin") # names them per test, relative to code/. SOURCES are relative to the test's own directory. # FLOAT adds the float semantics the engine builds with, so a harness measuring numeric # results matches it. UTF8 is for a harness whose own source holds non-ASCII text. STAMP is -# for a harness that reaches the generated version headers. +# for a harness that reaches the generated version headers. WINDOWS is for a harness that +# reaches a Windows API, which is built nowhere else. function(opents_add_test target) - cmake_parse_arguments(TEST "FLOAT;UTF8;STAMP" "NAME" + cmake_parse_arguments(TEST "FLOAT;UTF8;STAMP;WINDOWS" "NAME" "SOURCES;ENGINE;INCLUDES;DEFINITIONS;LIBRARIES" ${ARGN}) + if(TEST_WINDOWS AND NOT WIN32) + return() + endif() + set(sources "") foreach(source IN LISTS TEST_SOURCES) list(APPEND sources "${CMAKE_CURRENT_SOURCE_DIR}/${source}") @@ -33,26 +38,44 @@ function(opents_add_test target) target_compile_definitions(${target} PRIVATE ${TEST_DEFINITIONS}) endif() - target_compile_options(${target} PRIVATE - $<$:/MTd> - $<$:/MT> - /EHsc - /Zc:__cplusplus - ) - if(TEST_UTF8) - target_compile_options(${target} PRIVATE /utf-8) + if(MSVC) + target_compile_options(${target} PRIVATE + $<$:/MTd> + $<$:/MT> + /EHsc + /Zc:__cplusplus + ) + if(TEST_UTF8) + target_compile_options(${target} PRIVATE /utf-8) + endif() + else() + # A native compiler reads UTF-8 sources and enables exceptions already, and has no + # runtime library to choose. The Microsoft extensions the engine headers need come + # from the top-level build. endif() # /arch:SSE2 is the default beyond 32-bit x86 and is rejected as an unknown option # there, so it is only passed where it applies. if(TEST_FLOAT) - target_compile_options(${target} PRIVATE /fp:precise) - if(CMAKE_SIZEOF_VOID_P EQUAL 4) - target_compile_options(${target} PRIVATE /arch:SSE2) + if(MSVC) + target_compile_options(${target} PRIVATE /fp:precise) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + target_compile_options(${target} PRIVATE /arch:SSE2) + endif() + else() + # /fp:precise forbids contracting the engine's accumulations into fused + # operations. A native compiler contracts them within a statement by default. + target_compile_options(${target} PRIVATE -ffp-contract=off) + if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "[xi][3-8]86") + target_compile_options(${target} PRIVATE -msse2 -mfpmath=sse) + endif() endif() endif() - target_link_libraries(${target} PRIVATE kernel32 user32 shell32 ${TEST_LIBRARIES}) + if(WIN32) + target_link_libraries(${target} PRIVATE kernel32 user32 shell32) + endif() + target_link_libraries(${target} PRIVATE ${TEST_LIBRARIES}) if(TEST_STAMP) add_dependencies(${target} OpenTSBuildStamp) From 129083d9eede00dea2450b3dac80f8eb1139f40b Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:22:27 +0200 Subject: [PATCH 3/8] Skip the harnesses that need a Windows API --- tests/CMakeLists.txt | 9 +++++---- tests/autosave/CMakeLists.txt | 1 + tests/cpudetect/CMakeLists.txt | 1 + tests/cstream/CMakeLists.txt | 1 + tests/deploymentconfig/CMakeLists.txt | 1 + tests/gamedirs/CMakeLists.txt | 1 + tests/ini/CMakeLists.txt | 1 + tests/lcwblock/CMakeLists.txt | 1 + tests/logstress/CMakeLists.txt | 1 + tests/lzoblock/CMakeLists.txt | 1 + tests/netpacket/CMakeLists.txt | 1 + tests/scenfile/CMakeLists.txt | 1 + tests/shapefacing/CMakeLists.txt | 1 + tests/socketudp/CMakeLists.txt | 1 + tests/soundini/CMakeLists.txt | 1 + tests/spawner/CMakeLists.txt | 1 + tests/spawnhouse/CMakeLists.txt | 1 + tests/tutorial/CMakeLists.txt | 1 + tests/unvqdelta/CMakeLists.txt | 1 + tests/utf8/CMakeLists.txt | 1 + tests/voxeldraw/CMakeLists.txt | 1 + tests/zbufring/CMakeLists.txt | 1 + 22 files changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8591cdbdc..1bc980351 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,13 +8,14 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test-bin") # names them per test, relative to code/. SOURCES are relative to the test's own directory. # FLOAT adds the float semantics the engine builds with, so a harness measuring numeric # results matches it. UTF8 is for a harness whose own source holds non-ASCII text. STAMP is -# for a harness that reaches the generated version headers. WINDOWS is for a harness that -# reaches a Windows API, which is built nowhere else. +# for a harness that reaches the generated version headers. UNPORTED is for a harness that +# does not build off Windows yet, whether because it reaches a Windows API or because +# something it compiles still names a Windows header; it is configured on Windows alone. function(opents_add_test target) - cmake_parse_arguments(TEST "FLOAT;UTF8;STAMP;WINDOWS" "NAME" + cmake_parse_arguments(TEST "FLOAT;UTF8;STAMP;UNPORTED" "NAME" "SOURCES;ENGINE;INCLUDES;DEFINITIONS;LIBRARIES" ${ARGN}) - if(TEST_WINDOWS AND NOT WIN32) + if(TEST_UNPORTED AND NOT WIN32) return() endif() diff --git a/tests/autosave/CMakeLists.txt b/tests/autosave/CMakeLists.txt index 9181c4eb3..b2b1a895e 100644 --- a/tests/autosave/CMakeLists.txt +++ b/tests/autosave/CMakeLists.txt @@ -8,4 +8,5 @@ opents_add_test(Autosave ENGINE autosave.cpp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX + UNPORTED ) diff --git a/tests/cpudetect/CMakeLists.txt b/tests/cpudetect/CMakeLists.txt index bdb0567a9..0e09594f8 100644 --- a/tests/cpudetect/CMakeLists.txt +++ b/tests/cpudetect/CMakeLists.txt @@ -9,4 +9,5 @@ opents_add_test(CpuDetect mpu.cpp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX FLOAT + UNPORTED ) diff --git a/tests/cstream/CMakeLists.txt b/tests/cstream/CMakeLists.txt index c996e004c..128a4445e 100644 --- a/tests/cstream/CMakeLists.txt +++ b/tests/cstream/CMakeLists.txt @@ -7,4 +7,5 @@ opents_add_test(CStreamContract DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX LIBRARIES lzo FLOAT + UNPORTED ) diff --git a/tests/deploymentconfig/CMakeLists.txt b/tests/deploymentconfig/CMakeLists.txt index 7405e28d0..22b063b4d 100644 --- a/tests/deploymentconfig/CMakeLists.txt +++ b/tests/deploymentconfig/CMakeLists.txt @@ -31,4 +31,5 @@ opents_add_test(DeploymentConfig DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/gamedirs/CMakeLists.txt b/tests/gamedirs/CMakeLists.txt index d34cd8743..90b709f2c 100644 --- a/tests/gamedirs/CMakeLists.txt +++ b/tests/gamedirs/CMakeLists.txt @@ -31,4 +31,5 @@ opents_add_test(GameDirs DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/ini/CMakeLists.txt b/tests/ini/CMakeLists.txt index 225312e4b..787defc79 100644 --- a/tests/ini/CMakeLists.txt +++ b/tests/ini/CMakeLists.txt @@ -26,4 +26,5 @@ opents_add_test(IniContract DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/lcwblock/CMakeLists.txt b/tests/lcwblock/CMakeLists.txt index 8c3b9b66e..3b0740679 100644 --- a/tests/lcwblock/CMakeLists.txt +++ b/tests/lcwblock/CMakeLists.txt @@ -16,4 +16,5 @@ opents_add_test(LcwBlock xstraw.cpp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX FLOAT + UNPORTED ) diff --git a/tests/logstress/CMakeLists.txt b/tests/logstress/CMakeLists.txt index 3476855a0..180a53b66 100644 --- a/tests/logstress/CMakeLists.txt +++ b/tests/logstress/CMakeLists.txt @@ -9,4 +9,5 @@ opents_add_test(LogStress INCLUDES ${OPENTS_GENERATED_DIR} DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX STAMP + UNPORTED ) diff --git a/tests/lzoblock/CMakeLists.txt b/tests/lzoblock/CMakeLists.txt index cffa227e1..f7b4f435b 100644 --- a/tests/lzoblock/CMakeLists.txt +++ b/tests/lzoblock/CMakeLists.txt @@ -16,4 +16,5 @@ opents_add_test(LzoBlock DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX LIBRARIES lzo FLOAT + UNPORTED ) diff --git a/tests/netpacket/CMakeLists.txt b/tests/netpacket/CMakeLists.txt index 064d73099..3ed1c341e 100644 --- a/tests/netpacket/CMakeLists.txt +++ b/tests/netpacket/CMakeLists.txt @@ -15,4 +15,5 @@ opents_add_test(NetContract INCLUDES ${OPENTS_GENERATED_DIR} DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX STAMP + UNPORTED ) diff --git a/tests/scenfile/CMakeLists.txt b/tests/scenfile/CMakeLists.txt index 474db3300..e3d24b1d3 100644 --- a/tests/scenfile/CMakeLists.txt +++ b/tests/scenfile/CMakeLists.txt @@ -8,4 +8,5 @@ opents_add_test(ScenFile ENGINE scenfile.cpp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX + UNPORTED ) diff --git a/tests/shapefacing/CMakeLists.txt b/tests/shapefacing/CMakeLists.txt index 832e426df..72e9f3462 100644 --- a/tests/shapefacing/CMakeLists.txt +++ b/tests/shapefacing/CMakeLists.txt @@ -5,4 +5,5 @@ opents_add_test(ShapeFacing SOURCES shapefacing.cpp INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} DEFINITIONS WIN32 _WINDOWS _MBCS + UNPORTED ) diff --git a/tests/socketudp/CMakeLists.txt b/tests/socketudp/CMakeLists.txt index e7bc27797..b9a113b33 100644 --- a/tests/socketudp/CMakeLists.txt +++ b/tests/socketudp/CMakeLists.txt @@ -15,4 +15,5 @@ opents_add_test(SocketUdp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX LIBRARIES ws2_32 STAMP + UNPORTED ) diff --git a/tests/soundini/CMakeLists.txt b/tests/soundini/CMakeLists.txt index c37221b29..8330ae9ec 100644 --- a/tests/soundini/CMakeLists.txt +++ b/tests/soundini/CMakeLists.txt @@ -28,4 +28,5 @@ opents_add_test(SoundIni DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/spawner/CMakeLists.txt b/tests/spawner/CMakeLists.txt index 76e56b975..9f5b83cde 100644 --- a/tests/spawner/CMakeLists.txt +++ b/tests/spawner/CMakeLists.txt @@ -28,4 +28,5 @@ opents_add_test(SpawnContract DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/spawnhouse/CMakeLists.txt b/tests/spawnhouse/CMakeLists.txt index 8afb4cc80..2334c0b27 100644 --- a/tests/spawnhouse/CMakeLists.txt +++ b/tests/spawnhouse/CMakeLists.txt @@ -8,4 +8,5 @@ opents_add_test(SpawnHouse ENGINE spawnhouse.cpp DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX + UNPORTED ) diff --git a/tests/tutorial/CMakeLists.txt b/tests/tutorial/CMakeLists.txt index 804769cd8..5b3aaa395 100644 --- a/tests/tutorial/CMakeLists.txt +++ b/tests/tutorial/CMakeLists.txt @@ -28,4 +28,5 @@ opents_add_test(TutorialText DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX UTF8 STAMP + UNPORTED ) diff --git a/tests/unvqdelta/CMakeLists.txt b/tests/unvqdelta/CMakeLists.txt index 0ef548484..9d4856d42 100644 --- a/tests/unvqdelta/CMakeLists.txt +++ b/tests/unvqdelta/CMakeLists.txt @@ -10,4 +10,5 @@ opents_add_test(UnvqDelta INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/code/vqalib DEFINITIONS WIN32 _WINDOWS _MBCS FLOAT + UNPORTED ) diff --git a/tests/utf8/CMakeLists.txt b/tests/utf8/CMakeLists.txt index c282e1cee..7e939375b 100644 --- a/tests/utf8/CMakeLists.txt +++ b/tests/utf8/CMakeLists.txt @@ -8,4 +8,5 @@ opents_add_test(UTF8Contract utf8.cpp DEFINITIONS WIN32 _WINDOWS NOMINMAX UTF8 + UNPORTED ) diff --git a/tests/voxeldraw/CMakeLists.txt b/tests/voxeldraw/CMakeLists.txt index 8dec9f171..eb9f98472 100644 --- a/tests/voxeldraw/CMakeLists.txt +++ b/tests/voxeldraw/CMakeLists.txt @@ -9,4 +9,5 @@ opents_add_test(VoxelDraw INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} DEFINITIONS WIN32 _WINDOWS _MBCS FLOAT + UNPORTED ) diff --git a/tests/zbufring/CMakeLists.txt b/tests/zbufring/CMakeLists.txt index 5ce5d6ece..1ab1c948f 100644 --- a/tests/zbufring/CMakeLists.txt +++ b/tests/zbufring/CMakeLists.txt @@ -14,4 +14,5 @@ opents_add_test(ZBufRing _zbuffer.cpp INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} DEFINITIONS WIN32 _WINDOWS _MBCS NOMINMAX + UNPORTED ) From 40f472bd220ba94bc2d42f68bac7817cbbe65c2a Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:22:39 +0200 Subject: [PATCH 4/8] Document building the harnesses natively --- docs/BUILDING.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 445516b3c..08c83e864 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -136,6 +136,34 @@ version stamp that saves and network packets carry is the same for both, so nothing rejects a save or a peer on that basis. Configuring the build warns about it. +## Test harnesses on a native toolchain + +The test harnesses build with a native compiler, which is how portability work is +checked without a Windows machine. The engine target does not link: most of its +translation units compile, and the rest stop on the Windows headers they name. + +```bash +cmake -S . -B build/native -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DOPENTS_EXPERIMENTAL_NATIVE=ON +cmake --build build/native --target LzoComp AudioRing +ctest --test-dir build/native +``` + +A harness that does not build off Windows yet declares `UNPORTED` in its +`opents_add_test` call and is configured on Windows alone, so a native build +registers the rest and nothing else. Sixteen of the thirty-seven build and pass +on macOS at the time of writing. The other twenty-one reach `windows.h`, +`io.h`, `comdef.h` or the MSVC spellings of the C library, most of them through +an include and not by their own subject, as do about a third of the engine's own +sources. Each one that loses the keyword is a subsystem that has been ported. + +The options the engine builds with are MSVC's, and the native build passes the +equivalents rather than the same spellings: `-O0` and `-O2` for `/Od` and `/O2`, +`-ffp-contract=off` for `/fp:precise`, and `-msse2 -mfpmath=sse` for `/arch:SSE2` +where the target is 32-bit x86. `/RTC1` and the static runtime library have no +equivalent and are not replaced. + ## Build from Visual Studio Code With the recommended extensions installed, the repository provides: From 18a6df686961c0544dde653c2dcec12f5f85ff8b Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:36:23 +0200 Subject: [PATCH 5/8] Give the engine's compiler options a native equivalent --- code/CMakeLists.txt | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e64f0c139..5d5ea6bd7 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -95,6 +95,8 @@ set_target_properties(OpenTS PROPERTIES # message(STATUS "${PROJECT_NAME}: Applying compiler flags...") +if(MSVC) + set(OPENTS_COMPILE_OPTIONS # ================= DEBUG ================= @@ -132,6 +134,29 @@ set(OPENTS_COMPILE_OPTIONS > ) +else() + +# The native equivalents of the options above. /RTC1 and the static runtime library have +# none, and a native compiler needs no help with UTF-8 sources or with __cplusplus. +set(OPENTS_COMPILE_OPTIONS + -g + $<$:-O0> + $<$:-fno-omit-frame-pointer> + $<$:-O2> + + # /fp:precise forbids contracting the engine's accumulations into fused operations, + # which a native compiler does within a statement by default. + -ffp-contract=off +) + +# 32-bit x86 keeps x87 excess precision unless the SSE unit is selected, which is what +# /arch:SSE2 is doing above. +if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "[xi][3-8]86") + list(APPEND OPENTS_COMPILE_OPTIONS -msse2 -mfpmath=sse) +endif() + +endif() + target_compile_options(OpenTS PRIVATE ${OPENTS_COMPILE_OPTIONS}) # @@ -160,7 +185,7 @@ set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/bgfxbackend.cpp" PROPER "${BGFX_ROOT}/include;${CMAKE_SOURCE_DIR}/thirdparty/bgfx.cmake/bx/include;${BGFX_ROOT}/examples/common/imgui" COMPILE_DEFINITIONS "BX_CONFIG_DEBUG=$,1,$>" - COMPILE_OPTIONS "/Zc:preprocessor" + COMPILE_OPTIONS "$<$:/Zc:preprocessor>" ) # bx rewrites __stdcall while its headers are being parsed by clang-cl. Force the @@ -174,8 +199,8 @@ endif() message(STATUS "${PROJECT_NAME}: Adding compilier definitions...") target_compile_definitions(OpenTS PRIVATE - WIN32 - _WINDOWS + $<$:WIN32> + $<$:_WINDOWS> NOMINMAX # Compiles Blowfish into the binary instead of reaching it through the COM object in From 4b7e614e840e3dc7bef1c393792da63ab205fb33 Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:36:23 +0200 Subject: [PATCH 6/8] Leave the platform definitions off a native harness --- tests/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1bc980351..3ce31287a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,6 +36,11 @@ function(opents_add_test target) ) if(TEST_DEFINITIONS) + if(NOT WIN32) + # These three name the platform a harness is built for, which a native build + # is not. NOMINMAX only reaches a Windows header and costs nothing here. + list(REMOVE_ITEM TEST_DEFINITIONS WIN32 _WINDOWS _MBCS) + endif() target_compile_definitions(${target} PRIVATE ${TEST_DEFINITIONS}) endif() From 4d19e951ffc9d22471ef8740c81a81fbc938d7cd Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:46:09 +0200 Subject: [PATCH 7/8] Carry the harnesses in a target of their own --- tests/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ce31287a..086518cd3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,10 @@ # runs. Set before the subdirectories so every target below inherits it. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test-bin") +# Everything opents_add_test registers, so a build that cannot finish the engine can still +# ask for the harnesses by name. +add_custom_target(harnesses) + # Builds one test harness and registers it with CTest. # # A harness compiles the engine sources it exercises instead of linking the game, so ENGINE @@ -87,6 +91,7 @@ function(opents_add_test target) add_dependencies(${target} OpenTSBuildStamp) endif() + add_dependencies(harnesses ${target}) add_test(NAME ${TEST_NAME} COMMAND ${target}) endfunction() From dc0164f131b2c51f9839d03465cf7119c6a371dc Mon Sep 17 00:00:00 2001 From: Marcel Bierling Date: Wed, 9 Sep 2026 12:46:09 +0200 Subject: [PATCH 8/8] Run the native harnesses on Linux in CI --- .github/workflows/harness-native.yml | 71 ++++++++++++++++++++++++++++ docs/BUILDING.md | 5 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/harness-native.yml diff --git a/.github/workflows/harness-native.yml b/.github/workflows/harness-native.yml new file mode 100644 index 000000000..435393cf8 --- /dev/null +++ b/.github/workflows/harness-native.yml @@ -0,0 +1,71 @@ +name: Native harnesses + +# The test harnesses build with a native compiler, which is how a portability change is +# checked on a second platform. The engine target is not built: most of its sources still +# name Windows headers, and docs/BUILDING.md is the authority on what is supported. +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "code/**" + - "cmake/**" + - "tests/**" + - "thirdparty/**" + - "CMakeLists.txt" + - ".gitmodules" + - ".github/workflows/harness-native.yml" + push: + branches: [main] + paths: + - "code/**" + - "cmake/**" + - "tests/**" + - "thirdparty/**" + - "CMakeLists.txt" + - ".gitmodules" + - ".github/workflows/harness-native.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: harness-native-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + harnesses: + name: Linux + # A draft pull request builds nothing; the ready_for_review trigger above starts this + # when the pull request is marked ready. + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-24.04 + # Clang is what the native options were written against. GCC accepts them too but + # reads __declspec differently, so it is a separate step to take, not an assumption. + env: + CC: clang + CXX: clang++ + steps: + - name: Check out OpenTS + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + submodules: recursive + + - name: Install Ninja + run: | + sudo apt-get update + sudo apt-get install --yes ninja-build + + - name: Configure + run: > + cmake -S . -B build -G Ninja + -DCMAKE_BUILD_TYPE=Release + -DOPENTS_EXPERIMENTAL_NATIVE=ON + + # The harnesses target carries whatever opents_add_test registered, which off Windows + # is the harnesses that do not name a Windows API. + - name: Build the harnesses + run: cmake --build build --target harnesses + + - name: Test + run: ctest --test-dir build --output-on-failure diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 08c83e864..b503bd875 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -146,10 +146,13 @@ translation units compile, and the rest stop on the Windows headers they name. cmake -S . -B build/native -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DOPENTS_EXPERIMENTAL_NATIVE=ON -cmake --build build/native --target LzoComp AudioRing +cmake --build build/native --target harnesses ctest --test-dir build/native ``` +Continuous integration runs exactly that on Linux, so a change that breaks the +native harnesses is caught rather than discovered later. + A harness that does not build off Windows yet declares `UNPORTED` in its `opents_add_test` call and is configured on Windows alone, so a native build registers the rest and nothing else. Sixteen of the thirty-seven build and pass