Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ cmake_install.cmake
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/settings.json
!.vscode/c_cpp_properties.clang.example.json
.history/
/.cache/
/.clangd
Expand Down
74 changes: 74 additions & 0 deletions .vscode/c_cpp_properties.clang.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
// Copy this file to c_cpp_properties.json and launch VS Code from a Visual Studio
// Developer Command Prompt so the selected toolset and SDK are available.
"env": {
// LLVM installed with its default Windows installer location.
"CLANG_CL_PATH": "${env:ProgramFiles}/LLVM/bin/clang-cl.exe",
"MSVC_TOOLS_DIR": "${env:VCToolsInstallDir}",
"WINDOWS_SDK_INCLUDE_DIR": "${env:WindowsSdkDir}/Include/${env:WindowsSDKVersion}",
// Set this to the CMake binary directory used for the clang-cl build.
"CLANG_BUILD_DIR": "${workspaceFolder}/build/clang-cl",
"CLANG_COMPILER_ARGS": [
"--target=i686-pc-windows-msvc",
// Match the 14.44 MSVC toolset; update both together.
"/clang:-fms-compatibility-version=19.44"
],
"CLANG_INCLUDE_PATH": [
"${workspaceFolder}/code",
"${workspaceFolder}/code/vqalib",
"${workspaceFolder}/thirdparty/**",
"${CLANG_BUILD_DIR}/generated",
"${MSVC_TOOLS_DIR}/include",
"${MSVC_TOOLS_DIR}/atlmfc/include",
"${WINDOWS_SDK_INCLUDE_DIR}/ucrt",
"${WINDOWS_SDK_INCLUDE_DIR}/shared",
"${WINDOWS_SDK_INCLUDE_DIR}/um",
"${WINDOWS_SDK_INCLUDE_DIR}/winrt",
"${WINDOWS_SDK_INCLUDE_DIR}/cppwinrt"
],
"CLANG_DEFINES": [
"WIN32",
"_WIN32",
"_WINDOWS",
"_MBCS",
"NOMINMAX",
"NO_BLOWFISH_DLL"
]
},
"configurations": [
{
"name": "Win32 clang-cl Debug",
// Do not let the workspace's Visual Studio CMake provider override this configuration.
"configurationProvider": "",
"compilerPath": "${CLANG_CL_PATH}",
"compilerArgs": ["${CLANG_COMPILER_ARGS}"],
"intelliSenseMode": "windows-clang-x86",
"cStandard": "c17",
"cppStandard": "c++20",
"includePath": ["${CLANG_INCLUDE_PATH}"],
"defines": ["${CLANG_DEFINES}", "_DEBUG", "BX_CONFIG_DEBUG=1"]
},
{
"name": "Win32 clang-cl Release",
// Do not let the workspace's Visual Studio CMake provider override this configuration.
"configurationProvider": "",
"compilerPath": "${CLANG_CL_PATH}",
"compilerArgs": ["${CLANG_COMPILER_ARGS}"],
"intelliSenseMode": "windows-clang-x86",
"cStandard": "c17",
"cppStandard": "c++20",
"includePath": ["${CLANG_INCLUDE_PATH}"],
"defines": ["${CLANG_DEFINES}", "NDEBUG", "BX_CONFIG_DEBUG=0"]
},
{
"name": "Win32 clang-cl compilation database",
// Configure the clang-cl Ninja build before selecting this configuration.
"configurationProvider": "",
"compileCommands": "${CLANG_BUILD_DIR}/compile_commands.json",
"intelliSenseMode": "windows-clang-x86",
"cStandard": "c17",
"cppStandard": "c++20"
}
],
"version": 4
}
22 changes: 20 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@ 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)

if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
if(NOT OPENTS_EXPERIMENTAL_CLANG_CL)
message(FATAL_ERROR
"The unsupported clang-cl experiment must be configured with "
"-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/clang-cl-msvc.cmake.")
endif()
elseif(MSVC)
if(MSVC_VERSION LESS 1930)
message(FATAL_ERROR "OpenTS requires MSVC 19.30 or newer.")
endif()
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.")
endif()

enable_language(RC)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Expand Down
114 changes: 114 additions & 0 deletions cmake/toolchains/clang-cl-msvc.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86)

set(OPENTS_EXPERIMENTAL_CLANG_CL ON CACHE BOOL "" FORCE)
set(CMAKE_EXPORT_COMPILE_COMMANDS 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_file
"${OPENTS_MSVC_ROOT}/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt")
if(NOT EXISTS "${_opents_msvc_version_file}")
message(FATAL_ERROR
"Default MSVC toolset version not found: ${_opents_msvc_version_file}")
endif()

file(STRINGS "${_opents_msvc_version_file}" _opents_msvc_version LIMIT_COUNT 1)
string(STRIP "${_opents_msvc_version}" _opents_msvc_version)
if(NOT _opents_msvc_version MATCHES "^14\\.([0-9]+)\\.")
message(FATAL_ERROR "Unsupported MSVC toolset version: ${_opents_msvc_version}")
endif()
set(_opents_msvc_compatibility_version "19.${CMAKE_MATCH_1}")

set(_opents_msvc_dir "${OPENTS_MSVC_ROOT}/VC/Tools/MSVC/${_opents_msvc_version}")
set(_opents_sdk_dir "${OPENTS_MSVC_ROOT}/Windows Kits/10")

file(GLOB _opents_sdk_candidates
LIST_DIRECTORIES TRUE
RELATIVE "${_opents_sdk_dir}/Include"
"${_opents_sdk_dir}/Include/*")
list(SORT _opents_sdk_candidates COMPARE NATURAL ORDER DESCENDING)

unset(_opents_sdk_version)
foreach(_candidate IN LISTS _opents_sdk_candidates)
if(_candidate MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9]+)?$"
AND IS_DIRECTORY "${_opents_sdk_dir}/Include/${_candidate}/shared"
AND IS_DIRECTORY "${_opents_sdk_dir}/Include/${_candidate}/ucrt"
AND IS_DIRECTORY "${_opents_sdk_dir}/Include/${_candidate}/um"
AND IS_DIRECTORY "${_opents_sdk_dir}/Include/${_candidate}/winrt"
AND IS_DIRECTORY "${_opents_sdk_dir}/Lib/${_candidate}/ucrt/x86"
AND IS_DIRECTORY "${_opents_sdk_dir}/Lib/${_candidate}/um/x86")
set(_opents_sdk_version "${_candidate}")
break()
endif()
endforeach()

if(NOT _opents_sdk_version)
message(FATAL_ERROR "No complete Windows SDK found under: ${_opents_sdk_dir}")
endif()

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=${_opents_msvc_compatibility_version}")
set(CMAKE_CXX_FLAGS_INIT
"/clang:-fms-compatibility-version=${_opents_msvc_compatibility_version}")
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_USER_MAKE_RULES_OVERRIDE
"${CMAKE_CURRENT_LIST_DIR}/clang-cl-rc-rules.cmake")

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}")
3 changes: 3 additions & 0 deletions cmake/toolchains/clang-cl-rc-rules.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
set(CMAKE_DEPFILE_FLAGS_RC "")
set(CMAKE_RC_COMPILE_OBJECT
"<CMAKE_RC_COMPILER> /C 1252 <DEFINES> <INCLUDES> <FLAGS> /fo<OBJECT> <SOURCE>")
25 changes: 17 additions & 8 deletions code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,17 @@ set(OPENTS_COMPILE_OPTIONS
# /arch:SSE2 -- IEEE-754 single precision, no x87 excess precision.
# /fp:precise -- no reassociation of the engine's accumulations.
$<$<COMPILE_LANGUAGE:CXX>:
/Zi /Od /RTC1 /GR /MP /EHsc /Oy- /MTd /Zc:__cplusplus /arch:SSE2 /fp:precise
/Zi /Od /RTC1 /GR $<$<CXX_COMPILER_ID:MSVC>:/MP> /EHsc /Oy- /MTd /Zc:__cplusplus /arch:SSE2 /fp:precise
>

# ---------- C ----------
$<$<COMPILE_LANGUAGE:C>:
/Zi /Od /RTC1 /MP /Oy- /arch:SSE2 /fp:precise
/Zi /Od /RTC1 $<$<C_COMPILER_ID:MSVC>:/MP> /Oy- /arch:SSE2 /fp:precise
>

# ---------- MASM ----------
$<$<COMPILE_LANGUAGE:ASM_MASM>:
/Zi /c /coff /Cx /safeseh
-Zi -c -coff -Cx -safeseh
>
>

Expand All @@ -122,17 +122,17 @@ set(OPENTS_COMPILE_OPTIONS
# /arch:SSE2 -- IEEE-754 single precision, no x87 excess precision.
# /fp:precise -- no reassociation of the engine's accumulations.
$<$<COMPILE_LANGUAGE:CXX>:
/Zi /O2 /GF /GR /MP /EHsc /MT /Zc:__cplusplus /arch:SSE2 /fp:precise
/Zi /O2 /GF /GR $<$<CXX_COMPILER_ID:MSVC>:/MP> /EHsc /MT /Zc:__cplusplus /arch:SSE2 /fp:precise
>

# ---------- C ----------
$<$<COMPILE_LANGUAGE:C>:
/Zi /O2 /GF /MP /arch:SSE2 /fp:precise
/Zi /O2 /GF $<$<C_COMPILER_ID:MSVC>:/MP> /arch:SSE2 /fp:precise
>

# ---------- MASM ----------
$<$<COMPILE_LANGUAGE:ASM_MASM>:
/Zi /c /coff /Cx /safeseh
-Zi -c -coff -Cx -safeseh
>
>
)
Expand Down Expand Up @@ -168,6 +168,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}/thirdparty/bx-clang-compat.h"
)
endif()

message(STATUS "${PROJECT_NAME}: Adding compilier definitions...")
target_compile_definitions(OpenTS PRIVATE
WIN32
Expand Down Expand Up @@ -220,7 +229,7 @@ target_link_libraries(OpenTS PRIVATE
ole32 oleaut32 uuid odbc32 odbccp32
)

if (MSVC)
if(MSVC)
target_link_options(OpenTS PRIVATE
/SUBSYSTEM:WINDOWS
/DEBUG
Expand All @@ -245,7 +254,7 @@ message(STATUS "${PROJECT_NAME}: Adding MASM support...")

enable_language(ASM_MASM)

# All .asm files compile automatically using ML.EXE with appropriate flags.
# Dash-form options assemble under both ML.EXE and the clang-cl toolchain's UASM.
foreach(f ${OPENTS_SRC})
if(f MATCHES "\\.asm$")
set_source_files_properties(${f} PROPERTIES
Expand Down
2 changes: 1 addition & 1 deletion code/language/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ target_compile_definitions(Language PRIVATE NOMINMAX)
target_include_directories(Language PRIVATE "${OPENTS_GENERATED_DIR}")
add_dependencies(Language OpenTSBuildStamp)

if (MSVC)
if(MSVC)
target_link_options(Language PRIVATE "/NOENTRY" "/NODEFAULTLIB")
endif()

Expand Down
27 changes: 27 additions & 0 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,33 @@ name and copy the runtime files into `TS_RUN_DIR`, which defaults to `Run/`:
built configuration replaces the previous copy in `Run/`. Compiler and linker
intermediates remain under the selected build directory.

## 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 a Visual Studio layout and Windows SDK. The
cross-build uses the layout's default MSVC toolset and newest complete SDK.
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`. It exports `compile_commands.json`; one configuration in
`.vscode/c_cpp_properties.clang.example.json` reads that file for IntelliSense.

## Build from Visual Studio Code

Visual Studio Code (VSCode) support includes (assuming recommended extensions are installed):
Expand Down
17 changes: 17 additions & 0 deletions thirdparty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ set(BGFX_CONFIG_RENDERER_WEBGPU OFF CACHE BOOL "" FORCE)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

add_subdirectory(bgfx.cmake)

# 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)

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
"$<$<COMPILE_LANGUAGE:CXX>:/FI${PROJECT_SOURCE_DIR}/thirdparty/bx-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)
endif()
15 changes: 15 additions & 0 deletions thirdparty/bx-clang-compat.h
Original file line number Diff line number Diff line change
@@ -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 <bx/bx.h>

#if defined(__clang__) && defined(_M_IX86)
#undef __stdcall
#define __stdcall __attribute__((stdcall))
#endif
Loading