[client] windows: add native clang-cl build

Build the Windows client against the MSVC ABI while retaining clang
language support. Use vcpkg for ANGLE and libsamplerate, and build
MsQuic with its bundled static OpenSSL.
This commit is contained in:
Geoffrey McRae
2026-10-02 18:52:45 +10:00
parent c456596bd7
commit 57f18d7e80
33 changed files with 583 additions and 205 deletions

View File

@@ -1,34 +1,156 @@
if(LG_MAKE_OBJECT_ACTION STREQUAL "EMBED")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_SOURCE OR
NOT DEFINED LG_MAKE_OBJECT_HEADER OR
NOT DEFINED LG_MAKE_OBJECT_SYMBOL)
message(FATAL_ERROR
"The embed action requires input, source, header, and symbol")
endif()
file(READ "${LG_MAKE_OBJECT_INPUT}" LG_MAKE_OBJECT_DATA HEX)
set(LG_MAKE_OBJECT_BYTES "")
string(LENGTH "${LG_MAKE_OBJECT_DATA}" LG_MAKE_OBJECT_LENGTH)
set(LG_MAKE_OBJECT_OFFSET 0)
while(LG_MAKE_OBJECT_OFFSET LESS LG_MAKE_OBJECT_LENGTH)
math(EXPR LG_MAKE_OBJECT_REMAINING
"${LG_MAKE_OBJECT_LENGTH} - ${LG_MAKE_OBJECT_OFFSET}")
if(LG_MAKE_OBJECT_REMAINING GREATER 32)
set(LG_MAKE_OBJECT_CHUNK_LENGTH 32)
else()
set(LG_MAKE_OBJECT_CHUNK_LENGTH "${LG_MAKE_OBJECT_REMAINING}")
endif()
string(SUBSTRING "${LG_MAKE_OBJECT_DATA}"
${LG_MAKE_OBJECT_OFFSET} ${LG_MAKE_OBJECT_CHUNK_LENGTH}
LG_MAKE_OBJECT_CHUNK)
string(REGEX REPLACE "([0-9a-fA-F][0-9a-fA-F])"
"(char)0x\\1, " LG_MAKE_OBJECT_CHUNK "${LG_MAKE_OBJECT_CHUNK}")
string(APPEND LG_MAKE_OBJECT_BYTES
" ${LG_MAKE_OBJECT_CHUNK}\n")
math(EXPR LG_MAKE_OBJECT_OFFSET
"${LG_MAKE_OBJECT_OFFSET} + ${LG_MAKE_OBJECT_CHUNK_LENGTH}")
endwhile()
get_filename_component(LG_MAKE_OBJECT_SOURCE_DIR
"${LG_MAKE_OBJECT_SOURCE}" DIRECTORY)
get_filename_component(LG_MAKE_OBJECT_HEADER_DIR
"${LG_MAKE_OBJECT_HEADER}" DIRECTORY)
file(MAKE_DIRECTORY
"${LG_MAKE_OBJECT_SOURCE_DIR}"
"${LG_MAKE_OBJECT_HEADER_DIR}")
file(WRITE "${LG_MAKE_OBJECT_SOURCE}"
"#include <stddef.h>\n\n"
"const char ${LG_MAKE_OBJECT_SYMBOL}[] =\n"
"{\n${LG_MAKE_OBJECT_BYTES} (char)0x00\n};\n\n"
"const size_t ${LG_MAKE_OBJECT_SYMBOL}_size =\n"
" sizeof(${LG_MAKE_OBJECT_SYMBOL});\n")
file(WRITE "${LG_MAKE_OBJECT_HEADER}"
"#include <stddef.h>\n\n"
"extern const char ${LG_MAKE_OBJECT_SYMBOL}[];\n"
"extern const size_t ${LG_MAKE_OBJECT_SYMBOL}_size;\n")
return()
endif()
function(_lg_expand_includes input include_dir output_var)
file(READ "${input}" content)
string(REGEX MATCH
"(^|\n)#include[ \t]*\"([^\"]+)\"[ \t\r]*(\n|$)" include_match
"${content}")
while(include_match)
set(include_prefix "${CMAKE_MATCH_1}")
set(include_name "${CMAKE_MATCH_2}")
set(include_line "${include_match}")
set(include_path "${include_dir}/${include_name}")
if(NOT EXISTS "${include_path}")
message(FATAL_ERROR
"Unable to resolve ${include_name} included by ${input}")
endif()
_lg_expand_includes("${include_path}" "${include_dir}" include_content)
if(NOT "${include_content}" MATCHES "\n$")
string(APPEND include_content "\n")
endif()
string(REPLACE "${include_line}"
"${include_prefix}${include_content}" content
"${content}")
string(REGEX MATCH
"(^|\n)#include[ \t]*\"([^\"]+)\"[ \t\r]*(\n|$)" include_match
"${content}")
endwhile()
set(${output_var} "${content}" PARENT_SCOPE)
endfunction()
if(LG_MAKE_OBJECT_ACTION STREQUAL "PREPROCESS")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_OUTPUT OR
NOT DEFINED LG_MAKE_OBJECT_INCLUDE_DIR)
message(FATAL_ERROR
"The preprocess action requires input, output, and include directory")
endif()
_lg_expand_includes(
"${LG_MAKE_OBJECT_INPUT}"
"${LG_MAKE_OBJECT_INCLUDE_DIR}"
LG_MAKE_OBJECT_DATA)
get_filename_component(LG_MAKE_OBJECT_OUTPUT_DIR
"${LG_MAKE_OBJECT_OUTPUT}" DIRECTORY)
file(MAKE_DIRECTORY "${LG_MAKE_OBJECT_OUTPUT_DIR}")
file(WRITE "${LG_MAKE_OBJECT_OUTPUT}" "${LG_MAKE_OBJECT_DATA}")
return()
endif()
if(LG_MAKE_OBJECT_ACTION STREQUAL "DEFINES")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_OUTPUT)
message(FATAL_ERROR "The defines action requires input and output")
endif()
file(STRINGS "${LG_MAKE_OBJECT_INPUT}" LG_MAKE_OBJECT_DEFINES
REGEX "^#define")
set(LG_MAKE_OBJECT_DATA "")
foreach(LG_MAKE_OBJECT_DEFINE IN LISTS LG_MAKE_OBJECT_DEFINES)
string(APPEND LG_MAKE_OBJECT_DATA "${LG_MAKE_OBJECT_DEFINE}\n")
endforeach()
get_filename_component(LG_MAKE_OBJECT_OUTPUT_DIR
"${LG_MAKE_OBJECT_OUTPUT}" DIRECTORY)
file(MAKE_DIRECTORY "${LG_MAKE_OBJECT_OUTPUT_DIR}")
file(WRITE "${LG_MAKE_OBJECT_OUTPUT}" "${LG_MAKE_OBJECT_DATA}")
return()
endif()
set(LG_MAKE_OBJECT_SCRIPT "${CMAKE_CURRENT_LIST_FILE}")
function(make_object out_var)
set(result)
set(result_h)
foreach(in_f ${ARGN})
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_f "${out_f}.o")
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(in_path "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_c "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.c")
string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}")
add_custom_command(OUTPUT ${out_f}
COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${in_f} ${out_f}.tmp
COMMAND dd if=/dev/zero bs=1 count=1 of=${out_f}.tmp oflag=append conv=notrunc status=none
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${out_f}.tmp
COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_start=b_${sym_in} ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_end=b_${sym_in}_end ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_size=b_${sym_in}_size ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating object from ${in_f}"
add_custom_command(
OUTPUT "${out_c}" "${out_h}"
COMMAND "${CMAKE_COMMAND}"
"-DLG_MAKE_OBJECT_ACTION=EMBED"
"-DLG_MAKE_OBJECT_INPUT=${in_path}"
"-DLG_MAKE_OBJECT_SOURCE=${out_c}"
"-DLG_MAKE_OBJECT_HEADER=${out_h}"
"-DLG_MAKE_OBJECT_SYMBOL=b_${sym_in}"
-P "${LG_MAKE_OBJECT_SCRIPT}"
DEPENDS "${in_path}" "${LG_MAKE_OBJECT_SCRIPT}"
COMMENT "Embedding ${in_f}"
VERBATIM
)
file(WRITE ${out_h} "extern const char b_${sym_in}[];\n")
file(APPEND ${out_h} "extern const char b_${sym_in}_end[];\n")
file(APPEND ${out_h} "#define b_${sym_in}_size (b_${sym_in}_end - b_${sym_in})\n")
get_filename_component(h_dir ${out_h} DIRECTORY)
list(APPEND result_h ${h_dir})
list(APPEND result ${out_f})
get_filename_component(h_dir "${out_h}" DIRECTORY)
list(APPEND result_h "${h_dir}")
list(APPEND result "${out_c}")
endforeach()
list(REMOVE_DUPLICATES result_h)
@@ -38,9 +160,15 @@ endfunction()
function(make_defines in_file out_file)
file(RELATIVE_PATH rel_f ${CMAKE_CURRENT_SOURCE_DIR} "${in_file}")
add_custom_command(OUTPUT ${out_file}
COMMAND grep "^#define" "${in_file}" > "${out_file}"
DEPENDS ${in_file}
COMMENT "Creating #defines from ${rel_f}"
add_custom_command(
OUTPUT "${out_file}"
COMMAND "${CMAKE_COMMAND}"
"-DLG_MAKE_OBJECT_ACTION=DEFINES"
"-DLG_MAKE_OBJECT_INPUT=${in_file}"
"-DLG_MAKE_OBJECT_OUTPUT=${out_file}"
-P "${LG_MAKE_OBJECT_SCRIPT}"
DEPENDS "${in_file}" "${LG_MAKE_OBJECT_SCRIPT}"
COMMENT "Extracting definitions from ${rel_f}"
VERBATIM
)
endfunction()

View File

@@ -77,7 +77,6 @@ function(lg_add_static_msquic)
set(LG_MSQUIC_BINARY_DIR "${CMAKE_BINARY_DIR}/MsQuic")
set(LG_MSQUIC_SYSTEM_LIBRARIES
ws2_32
schannel
ntdll
bcrypt
ncrypt
@@ -106,8 +105,41 @@ function(lg_add_static_msquic)
endif()
if(MSVC)
if(NOT CMAKE_GENERATOR MATCHES "^Ninja")
message(FATAL_ERROR
"The native Windows MsQuic build requires Ninja or Ninja "
"Multi-Config")
endif()
if(NOT EXISTS
"${LG_MSQUIC_SOURCE_DIR}/submodules/openssl/Configure")
message(FATAL_ERROR
"The MsQuic OpenSSL submodule is not initialized; run "
"git submodule update --init --recursive")
endif()
find_program(LG_MSQUIC_CL NAMES cl REQUIRED)
find_program(LG_MSQUIC_NMAKE NAMES nmake REQUIRED)
find_program(LG_MSQUIC_NASM NAMES nasm REQUIRED)
find_program(LG_MSQUIC_PERL NAMES perl REQUIRED)
list(APPEND LG_MSQUIC_CMAKE_ARGS
"-DQUIC_TLS_LIB=schannel")
"-DQUIC_TLS_LIB=openssl"
"-DQUIC_USE_EXTERNAL_OPENSSL=OFF"
"-DQUIC_USE_SYSTEM_LIBCRYPTO=OFF"
"-DQUIC_OPENSSL_INCLUDE_DIR="
"-DQUIC_OPENSSL_LIB_DIR="
"-DQUIC_OPENSSL_ROOT_DIR="
"-DCMAKE_C_COMPILER=${LG_MSQUIC_CL}"
"-DCMAKE_CXX_COMPILER=${LG_MSQUIC_CL}")
if(NOT CMAKE_CONFIGURATION_TYPES)
list(APPEND LG_MSQUIC_CMAKE_ARGS
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif()
list(APPEND LG_MSQUIC_SYSTEM_LIBRARIES
gdi32
user32
wbemuuid
winmm)
set(LG_MSQUIC_RELEASE_LIBRARY
"${LG_MSQUIC_BINARY_DIR}/bin/Release/msquic.lib")
endif()

View File

@@ -2,18 +2,36 @@ set(OPTIMIZE_FOR_NATIVE AUTO CACHE STRING "Build with -march=native")
set_property(CACHE OPTIMIZE_FOR_NATIVE PROPERTY STRINGS AUTO ON OFF)
set(OPTIMIZE_FOR_NATIVE_ARCH "none")
if(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR (OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" AND OPTIMIZE_FOR_NATIVE_DEFAULT))
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(OPTIMIZE_FOR_NATIVE_PREFIX "/clang:-march=")
elseif(MSVC)
set(OPTIMIZE_FOR_NATIVE_PREFIX "")
else()
set(OPTIMIZE_FOR_NATIVE_PREFIX "-march=")
endif()
if(NOT OPTIMIZE_FOR_NATIVE_PREFIX)
if(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR
NOT OPTIMIZE_FOR_NATIVE MATCHES "^(AUTO|OFF)$")
message(WARNING
"OPTIMIZE_FOR_NATIVE is not supported by ${CMAKE_C_COMPILER_ID}")
endif()
elseif(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR
(OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" AND OPTIMIZE_FOR_NATIVE_DEFAULT))
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}native"
COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
set(OPTIMIZE_FOR_NATIVE_ARCH "native")
set(OPTIMIZE_FOR_NATIVE_ON ON)
endif()
elseif(OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" OR OPTIMIZE_FOR_NATIVE STREQUAL "OFF")
CHECK_C_COMPILER_FLAG("-march=x86-64-v2" COMPILER_SUPPORTS_MARCH_X86_64_V2)
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}x86-64-v2"
COMPILER_SUPPORTS_MARCH_X86_64_V2)
if(COMPILER_SUPPORTS_MARCH_X86_64_V2)
set(OPTIMIZE_FOR_NATIVE_ARCH "x86-64-v2")
else()
CHECK_C_COMPILER_FLAG("-march=nehalem" COMPILER_SUPPORTS_MARCH_NEHALEM)
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}nehalem"
COMPILER_SUPPORTS_MARCH_NEHALEM)
if(COMPILER_SUPPORTS_MARCH_NEHALEM)
set(OPTIMIZE_FOR_NATIVE_ARCH "nehalem")
endif()
@@ -24,7 +42,8 @@ else()
endif()
if(NOT OPTIMIZE_FOR_NATIVE_ARCH STREQUAL "none")
add_compile_options("-march=${OPTIMIZE_FOR_NATIVE_ARCH}")
add_compile_options(
"${OPTIMIZE_FOR_NATIVE_PREFIX}${OPTIMIZE_FOR_NATIVE_ARCH}")
endif()
add_feature_info(OPTIMIZE_FOR_NATIVE OPTIMIZE_FOR_NATIVE_ON "Optimized for ${OPTIMIZE_FOR_NATIVE_ARCH}")