From ee97f9e61c36262a8fcec68248d41688e2ee17a6 Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:16:11 +0300 Subject: [PATCH 1/2] Add experimental clang-cl cross-build --- CMakeLists.txt | 24 +++++++++- cmake/clang-compat.h | 15 ++++++ cmake/toolchains/clang-cl-msvc.cmake | 72 ++++++++++++++++++++++++++++ code/CMakeLists.txt | 43 +++++++++++++---- code/language/CMakeLists.txt | 8 +++- docs/BUILDING.md | 25 ++++++++++ thirdparty/CMakeLists.txt | 17 +++++++ 7 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 cmake/clang-compat.h create mode 100644 cmake/toolchains/clang-cl-msvc.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 33878dba..2763905a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.23) project(OpenTS VERSION 0.2.0 LANGUAGES C CXX ASM_MASM) +enable_language(RC) # A SemVer prerelease label, which project() cannot carry because it takes numbers only. Set it # to "beta1" and the version reads 0.1.0-beta1; leave it empty outside a prerelease series. The @@ -11,8 +12,27 @@ set(OPENTS_VERSION_PRERELEASE "") # alone. Every other build names the commit it came from as well. option(OPENTS_OFFICIAL_BUILD "Build as an official release of the declared version" OFF) -if(NOT MSVC OR MSVC_VERSION LESS 1930) - message(FATAL_ERROR "OpenTS requires the Visual Studio 2022 MSVC toolchain.") +option(OPENTS_EXPERIMENTAL_CLANG_CL "Build with clang-cl using the MSVC ABI" OFF) + +set(OPENTS_MSVC_ABI_COMPILER OFF) +if(MSVC) + if(MSVC_VERSION LESS 1930) + message(FATAL_ERROR "OpenTS requires MSVC 19.30 or newer.") + endif() + set(OPENTS_MSVC_ABI_COMPILER ON) +elseif(OPENTS_EXPERIMENTAL_CLANG_CL + AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(OPENTS_MSVC_ABI_COMPILER ON) + set(CMAKE_DEPFILE_FLAGS_RC "" CACHE STRING "" FORCE) + set(CMAKE_RC_COMPILE_OBJECT + " /C 1252 /fo " + CACHE STRING "" FORCE) +else() + message(FATAL_ERROR + "OpenTS requires the Visual Studio 2022 MSVC toolchain. " + "The unsupported clang-cl experiment must be configured with " + "-DOPENTS_EXPERIMENTAL_CLANG_CL=ON.") endif() set(CMAKE_CXX_STANDARD 20) diff --git a/cmake/clang-compat.h b/cmake/clang-compat.h new file mode 100644 index 00000000..35124010 --- /dev/null +++ b/cmake/clang-compat.h @@ -0,0 +1,15 @@ +/******************************************************************************* + * O P E N T S + ****************************************************************************** + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright 2026 OpenTS contributors + ******************************************************************************/ + +#pragma once + +#include + +#if defined(__clang__) && defined(_M_IX86) +#undef __stdcall +#define __stdcall __attribute__((stdcall)) +#endif diff --git a/cmake/toolchains/clang-cl-msvc.cmake b/cmake/toolchains/clang-cl-msvc.cmake new file mode 100644 index 00000000..e3b366e7 --- /dev/null +++ b/cmake/toolchains/clang-cl-msvc.cmake @@ -0,0 +1,72 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86) + +set(OPENTS_EXPERIMENTAL_CLANG_CL ON CACHE BOOL "" FORCE) +set(OPENTS_MSVC_ROOT "" CACHE PATH "Path to the MSVC and Windows SDK files") +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES OPENTS_MSVC_ROOT) + +if(NOT OPENTS_MSVC_ROOT AND DEFINED ENV{OPENTS_MSVC_ROOT}) + set(OPENTS_MSVC_ROOT "$ENV{OPENTS_MSVC_ROOT}" CACHE PATH "" FORCE) +endif() + +if(NOT OPENTS_MSVC_ROOT) + message(FATAL_ERROR + "Set OPENTS_MSVC_ROOT to the directory containing MSVC and the Windows SDK.") +endif() + +set(_opents_msvc_version "14.44.35207") +set(_opents_sdk_version "10.0.26100.0") +set(_opents_msvc_dir "${OPENTS_MSVC_ROOT}/VC/Tools/MSVC/${_opents_msvc_version}") +set(_opents_sdk_dir "${OPENTS_MSVC_ROOT}/Windows Kits/10") + +foreach(_required_path + "${_opents_msvc_dir}/include" + "${_opents_sdk_dir}/Include/${_opents_sdk_version}/um") + if(NOT EXISTS "${_required_path}") + message(FATAL_ERROR "Required MSVC component not found: ${_required_path}") + endif() +endforeach() + +find_program(_opents_clang_cl clang-cl REQUIRED) +find_program(_opents_lld_link lld-link REQUIRED) +find_program(_opents_llvm_lib llvm-lib REQUIRED) +find_program(_opents_llvm_mt llvm-mt REQUIRED) +find_program(_opents_llvm_rc llvm-rc REQUIRED) +find_program(_opents_uasm uasm REQUIRED) + +set(CMAKE_C_COMPILER "${_opents_clang_cl}") +set(CMAKE_CXX_COMPILER "${_opents_clang_cl}") +set(CMAKE_C_COMPILER_TARGET i686-pc-windows-msvc) +set(CMAKE_CXX_COMPILER_TARGET i686-pc-windows-msvc) +set(CMAKE_C_FLAGS_INIT "/clang:-fms-compatibility-version=19.44") +set(CMAKE_CXX_FLAGS_INIT "/clang:-fms-compatibility-version=19.44") +set(CMAKE_LINKER "${_opents_lld_link}") +set(CMAKE_AR "${_opents_llvm_lib}") +set(CMAKE_RC_COMPILER "${_opents_llvm_rc}" CACHE FILEPATH "" FORCE) +set(CMAKE_MT "${_opents_llvm_mt}") +set(CMAKE_ASM_MASM_COMPILER "${_opents_uasm}" CACHE FILEPATH "" FORCE) + +set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES + "${_opents_msvc_dir}/atlmfc/include" + "${_opents_msvc_dir}/include" + "${_opents_sdk_dir}/Include/${_opents_sdk_version}/shared" + "${_opents_sdk_dir}/Include/${_opents_sdk_version}/ucrt" + "${_opents_sdk_dir}/Include/${_opents_sdk_version}/um" + "${_opents_sdk_dir}/Include/${_opents_sdk_version}/winrt") +set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_C_STANDARD_INCLUDE_DIRECTORIES}) + +set(_opents_rc_flags "") +foreach(_include_dir IN LISTS CMAKE_C_STANDARD_INCLUDE_DIRECTORIES) + string(APPEND _opents_rc_flags " /I\"${_include_dir}\"") +endforeach() +set(CMAKE_RC_FLAGS_INIT "${_opents_rc_flags}") + +set(_opents_linker_paths + "/libpath:\"${_opents_msvc_dir}/atlmfc/lib/x86\"" + "/libpath:\"${_opents_msvc_dir}/lib/x86\"" + "/libpath:\"${_opents_sdk_dir}/Lib/${_opents_sdk_version}/ucrt/x86\"" + "/libpath:\"${_opents_sdk_dir}/Lib/${_opents_sdk_version}/um/x86\"") +string(JOIN " " _opents_linker_flags ${_opents_linker_paths}) +set(CMAKE_EXE_LINKER_FLAGS_INIT "${_opents_linker_flags}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "${_opents_linker_flags}") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "${_opents_linker_flags}") diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 8e90291c..63e1661e 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -57,6 +57,12 @@ endforeach() # message(STATUS "${PROJECT_NAME}: Creating '${PROJECT_NAME}' executable...") +if(OPENTS_EXPERIMENTAL_CLANG_CL) + set(CMAKE_DEPFILE_FLAGS_RC "") + set(CMAKE_RC_COMPILE_OBJECT + " /C 1252 /fo ") +endif() + add_executable(OpenTS WIN32 ${OPENTS_SRC}) target_compile_features(OpenTS PRIVATE cxx_std_20) @@ -92,6 +98,16 @@ set_target_properties(OpenTS PROPERTIES # message(STATUS "${PROJECT_NAME}: Applying compiler flags...") +if(OPENTS_EXPERIMENTAL_CLANG_CL) + set(OPENTS_ASM_MASM_OPTIONS -Zi -c -coff -Cx -safeseh) + set(OPENTS_ASM_MASM_SOURCE_OPTIONS "-Zi -c -coff -Cx -safeseh") + set(OPENTS_PARALLEL_COMPILE_OPTION "") +else() + set(OPENTS_ASM_MASM_OPTIONS /Zi /c /coff /Cx /safeseh) + set(OPENTS_ASM_MASM_SOURCE_OPTIONS "/Zi /c /coff /Cx /safeseh") + set(OPENTS_PARALLEL_COMPILE_OPTION /MP) +endif() + set(OPENTS_COMPILE_OPTIONS # ================= DEBUG ================= @@ -101,17 +117,17 @@ set(OPENTS_COMPILE_OPTIONS # /arch:SSE2 -- IEEE-754 single precision, no x87 excess precision. # /fp:precise -- no reassociation of the engine's accumulations. $<$: - /Zi /Od /RTC1 /GR /MP /EHsc /Oy- /MTd /Zc:__cplusplus /arch:SSE2 /fp:precise + /Zi /Od /RTC1 /GR ${OPENTS_PARALLEL_COMPILE_OPTION} /EHsc /Oy- /MTd /Zc:__cplusplus /arch:SSE2 /fp:precise > # ---------- C ---------- $<$: - /Zi /Od /RTC1 /MP /Oy- /arch:SSE2 /fp:precise + /Zi /Od /RTC1 ${OPENTS_PARALLEL_COMPILE_OPTION} /Oy- /arch:SSE2 /fp:precise > # ---------- MASM ---------- $<$: - /Zi /c /coff /Cx /safeseh + ${OPENTS_ASM_MASM_OPTIONS} > > @@ -122,17 +138,17 @@ set(OPENTS_COMPILE_OPTIONS # /arch:SSE2 -- IEEE-754 single precision, no x87 excess precision. # /fp:precise -- no reassociation of the engine's accumulations. $<$: - /Zi /O2 /GF /GR /MP /EHsc /MT /Zc:__cplusplus /arch:SSE2 /fp:precise + /Zi /O2 /GF /GR ${OPENTS_PARALLEL_COMPILE_OPTION} /EHsc /MT /Zc:__cplusplus /arch:SSE2 /fp:precise > # ---------- C ---------- $<$: - /Zi /O2 /GF /MP /arch:SSE2 /fp:precise + /Zi /O2 /GF ${OPENTS_PARALLEL_COMPILE_OPTION} /arch:SSE2 /fp:precise > # ---------- MASM ---------- $<$: - /Zi /c /coff /Cx /safeseh + ${OPENTS_ASM_MASM_OPTIONS} > > ) @@ -168,6 +184,15 @@ set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/bgfxbackend.cpp" PROPER COMPILE_OPTIONS "/Zc:preprocessor" ) +# bx rewrites __stdcall while its headers are being parsed by clang-cl. Force the +# compatibility header into the renderer translation unit as well as bx/bgfx so the +# MSVC standard-library headers that follow still see the Win32 calling convention. +if(OPENTS_EXPERIMENTAL_CLANG_CL AND CMAKE_SIZEOF_VOID_P EQUAL 4) + set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/bgfxbackend.cpp" APPEND PROPERTY + COMPILE_OPTIONS "/FI${CMAKE_SOURCE_DIR}/cmake/clang-compat.h" + ) +endif() + message(STATUS "${PROJECT_NAME}: Adding compilier definitions...") target_compile_definitions(OpenTS PRIVATE WIN32 @@ -220,7 +245,7 @@ target_link_libraries(OpenTS PRIVATE ole32 oleaut32 uuid odbc32 odbccp32 ) -if (MSVC) +if (OPENTS_MSVC_ABI_COMPILER) target_link_options(OpenTS PRIVATE /SUBSYSTEM:WINDOWS /DEBUG @@ -245,12 +270,12 @@ message(STATUS "${PROJECT_NAME}: Adding MASM support...") enable_language(ASM_MASM) -# All .asm files compile automatically using ML.EXE with appropriate flags. +# The experimental clang-cl toolchain uses native UASM; Visual Studio uses ML.EXE. foreach(f ${OPENTS_SRC}) if(f MATCHES "\\.asm$") set_source_files_properties(${f} PROPERTIES LANGUAGE ASM_MASM - COMPILE_FLAGS "-Zi -c -coff -Cx" + COMPILE_FLAGS "${OPENTS_ASM_MASM_SOURCE_OPTIONS}" ) endif() endforeach() diff --git a/code/language/CMakeLists.txt b/code/language/CMakeLists.txt index 4d1feabe..2c6fcceb 100644 --- a/code/language/CMakeLists.txt +++ b/code/language/CMakeLists.txt @@ -3,6 +3,12 @@ file(GLOB_RECURSE LANG_SRC CONFIGURE_DEPENDS "*.h" "*.hpp" "*.hm" ) +if(OPENTS_EXPERIMENTAL_CLANG_CL) + set(CMAKE_DEPFILE_FLAGS_RC "") + set(CMAKE_RC_COMPILE_OBJECT + " /C 1252 /fo ") +endif() + add_library(Language SHARED ${LANG_SRC}) target_compile_features(Language PRIVATE cxx_std_20) target_compile_definitions(Language PRIVATE NOMINMAX) @@ -12,7 +18,7 @@ target_compile_definitions(Language PRIVATE NOMINMAX) target_include_directories(Language PRIVATE "${OPENTS_GENERATED_DIR}") add_dependencies(Language OpenTSBuildStamp) -if (MSVC) +if (OPENTS_MSVC_ABI_COMPILER) target_link_options(Language PRIVATE "/NOENTRY" "/NODEFAULTLIB") endif() diff --git a/docs/BUILDING.md b/docs/BUILDING.md index f9078a61..48e882ec 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -23,6 +23,31 @@ supported by the current tree. Install Visual Studio 2022 with the **Desktop development with C++** workload, a Windows SDK, CMake 3.23 or newer, and Git for Windows. +### Experimental clang-cl cross-build + +An unsupported Linux cross-build is available for compiler-portability work. It +uses native `clang-cl`, LLD, LLVM library and resource tools, and UASM with +the MSVC headers and libraries. It does not expand the supported build matrix +or establish runtime behavior. + +The reconstructed codebase may still contain undefined behavior that the +supported MSVC build happens not to expose. A successful clang-cl build may +therefore run incorrectly or fail at runtime; validate any result separately. + +Provide a directory containing MSVC 14.44.35207 and Windows SDK 10.0.26100.0, +then configure a single-configuration Ninja build: + +```bash +cmake -S . -B build/clang-cl -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-cl-msvc.cmake \ + -DOPENTS_MSVC_ROOT=/path/to/msvc +cmake --build build/clang-cl +``` + +The toolchain requires `clang-cl`, `lld-link`, `llvm-lib`, `llvm-mt`, +`llvm-rc`, and `uasm` on `PATH`. + ## Dependencies The renderer is built on [bgfx](https://github.com/bkaradzic/bgfx), vendored as the diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 603217a7..2e80372e 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -19,3 +19,20 @@ set(BGFX_CONFIG_RENDERER_WEBGPU OFF CACHE BOOL "" FORCE) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") add_subdirectory(bgfx.cmake) + +if(OPENTS_EXPERIMENTAL_CLANG_CL AND CMAKE_SIZEOF_VOID_P EQUAL 4) + # bx treats Clang with the MSVC CRT like a non-x86 compiler and erases + # __stdcall. Restore it before Windows declarations are parsed. + foreach(target bx bimg bgfx) + target_compile_options(${target} PRIVATE + "$<$:/FI${PROJECT_SOURCE_DIR}/cmake/clang-compat.h>" + ) + endforeach() + + # The ASTC encoder selects a 64-bit popcount intrinsic when SSE4.2 is enabled, + # but that intrinsic is unavailable for the Win32 target. Its SSE2 path is portable. + target_compile_definitions(bimg PRIVATE ASTCENC_POPCNT=0 ASTCENC_SSE=20) + + # These libraries support the disabled texture tools and are not linked into OpenTS. + set_target_properties(bimg_decode bimg_encode PROPERTIES EXCLUDE_FROM_ALL TRUE) +endif() From 36f68380f0a5cca06780d8f98e9664365b334b30 Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:16:11 +0300 Subject: [PATCH 2/2] Update c_cpp_properties.clang.example.json --- .vscode/c_cpp_properties.clang.example.json | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .vscode/c_cpp_properties.clang.example.json diff --git a/.vscode/c_cpp_properties.clang.example.json b/.vscode/c_cpp_properties.clang.example.json new file mode 100644 index 00000000..6cf637ba --- /dev/null +++ b/.vscode/c_cpp_properties.clang.example.json @@ -0,0 +1,51 @@ +{ + // Copy this file to c_cpp_properties.json and adjust these paths if needed. + "env": { + // LLVM installed with its default Windows installer location. + "CLANG_CL_PATH": "${env:ProgramFiles}/LLVM/bin/clang-cl.exe", + // Visual Studio 2022 Community. Change the edition or installation path as needed. + "MSVC_ROOT": "${env:ProgramFiles}/Microsoft Visual Studio/2022/Community", + "MSVC_VERSION": "14.44.35207", + // The Windows SDK is normally installed under Program Files (x86). + "WINDOWS_SDK_ROOT": "${env:ProgramFiles(x86)}/Windows Kits/10", + "WINDOWS_SDK_VERSION": "10.0.26100.0", + // Set this to the CMake binary directory used for the clang-cl build. + "CLANG_BUILD_DIR": "${workspaceFolder}/build/clang-cl" + }, + "configurations": [ + { + "name": "Win32 clang-cl with MSVC headers", + "compilerPath": "${CLANG_CL_PATH}", + "compilerArgs": [ + "--target=i686-pc-windows-msvc", + "/clang:-fms-compatibility-version=19.44" + ], + "intelliSenseMode": "windows-clang-x86", + "cStandard": "c17", + "cppStandard": "c++20", + "includePath": [ + "${workspaceFolder}/code", + "${workspaceFolder}/code/vqalib", + "${workspaceFolder}/thirdparty/**", + "${CLANG_BUILD_DIR}/generated", + "${MSVC_ROOT}/VC/Tools/MSVC/${MSVC_VERSION}/include", + "${MSVC_ROOT}/VC/Tools/MSVC/${MSVC_VERSION}/atlmfc/include", + "${WINDOWS_SDK_ROOT}/Include/${WINDOWS_SDK_VERSION}/ucrt", + "${WINDOWS_SDK_ROOT}/Include/${WINDOWS_SDK_VERSION}/shared", + "${WINDOWS_SDK_ROOT}/Include/${WINDOWS_SDK_VERSION}/um", + "${WINDOWS_SDK_ROOT}/Include/${WINDOWS_SDK_VERSION}/winrt", + "${WINDOWS_SDK_ROOT}/Include/${WINDOWS_SDK_VERSION}/cppwinrt" + ], + "defines": [ + "WIN32", + "_WIN32", + "_WINDOWS", + "_MBCS", + "NOMINMAX", + "NO_BLOWFISH_DLL", + "BX_CONFIG_DEBUG=0" + ] + } + ], + "version": 4 +}