diff options
| author | Stefan Boberg <[email protected]> | 2026-03-16 10:52:45 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-16 10:52:45 +0100 |
| commit | 79e10a165cf09dc2cc120b3a226c51f87c235f20 (patch) | |
| tree | cf51b07e097904044b4bf65bc3fe0ad14134074f | |
| parent | Linux build improvements (#843) (diff) | |
| download | zen-79e10a165cf09dc2cc120b3a226c51f87c235f20.tar.xz zen-79e10a165cf09dc2cc120b3a226c51f87c235f20.zip | |
Enable cross compilation of Windows targets on Linux (#839)
This PR makes it *possible* to do a Windows build on Linux via `clang-cl`.
It doesn't actually change any build process. No policy change, just mechanics and some code fixes to clear clang compilation.
The code fixes are mainly related to #include file name casing, to match the on-disk casing of the SDK files (via xwin).
25 files changed, 536 insertions, 78 deletions
diff --git a/docs/WindowsOnLinux.md b/docs/WindowsOnLinux.md new file mode 100644 index 000000000..540447cb2 --- /dev/null +++ b/docs/WindowsOnLinux.md @@ -0,0 +1,109 @@ +# Cross-Compiling Windows Binaries on Linux + +This document describes how to build `zenserver.exe` and `zen.exe` for Windows +from a Linux host using clang-cl, lld-link, and the Windows SDK fetched via +[xwin](https://github.com/Jake-Shadle/xwin). + +## Prerequisites + +- **LLVM/Clang 17+** with the following tools (typically in `/usr/lib/llvm-<ver>/bin/`): + - `clang` (used as `clang-cl` via `--driver-mode=cl`) + - `lld-link` + - `llvm-lib` + - `llvm-rc` + - `llvm-ml` + - `llvm-mt` + + On Ubuntu/Debian: + ```bash + sudo apt install clang-18 lld-18 llvm-18 + ``` + +- **xmake** (build system) — see [xmake.io](https://xmake.io) for installation. + +## Step 1: Fetch the Windows SDK + +Run the provided script to download the Windows SDK and MSVC CRT headers/libs: + +```bash +./scripts/win_cross/get_win_sdk.sh +``` + +This downloads the SDK to `~/.xwin-sdk` by default. Override the location with +an argument or the `$XWIN_SDK_DIR` environment variable: + +```bash +./scripts/win_cross/get_win_sdk.sh /path/to/sdk +``` + +The script also creates: +- Tool wrapper scripts (clang-cl, lld-link, llvm-lib, etc.) in the SDK `bin/` directory +- An MSVC-compatible directory layout that xmake's built-in clang-cl toolchain can discover +- Debug CRT lib symlinks so cmake package builds succeed + +## Step 2: Configure and Build + +```bash +xmake config -y -p windows -a x64 \ + --toolchain=clang-cl \ + --sdk=$HOME/.xwin-sdk \ + --mrc=$HOME/.xwin-sdk/bin/x64/rc.exe \ + -m release \ + --zensentry=n \ + --zenmimalloc=n + +xmake +``` + +### Notes + +- **`--sdk` must use an absolute path** — `$HOME/.xwin-sdk` works, but + `~/.xwin-sdk` does not (xmake doesn't expand `~`). +- **`--mrc`** points to the resource compiler wrapper. Without it, xmake can't + auto-detect `llvm-rc` (it doesn't support `--version`). +- **`--zensentry=no`** is recommended — crashpad (used by sentry-native) is + difficult to cross-compile. +- **`--zenmimalloc=no`** is recommended initially to reduce the package surface. +- **LTO is automatically disabled** for cross-compilation builds. + +## Step 3: Verify + +```bash +file build/windows/x64/release/zenserver.exe +# PE32+ executable (console) x86-64, for MS Windows, 12 sections + +file build/windows/x64/release/zen.exe +# PE32+ executable (console) x86-64, for MS Windows, 12 sections +``` + +## Running with Wine (optional) + +The resulting binaries can be run under Wine for testing: + +```bash +wine build/windows/x64/release/zenserver.exe --help +``` + +## Troubleshooting + +### Library not found (case sensitivity) + +The Windows SDK ships headers and libs with specific casing (e.g. `DbgHelp.h`, +`ws2_32.lib`). Linux filesystems are case-sensitive, so `#include <Dbghelp.h>` +won't find `DbgHelp.h`. The codebase uses lowercase includes where possible. If +you encounter a missing header/lib, check the actual casing in the SDK directory. + +### `add_ldflags(...) is ignored` + +xmake's auto flag checker may reject MSVC linker flags when using clang-cl. Add +`{force = true}` to the `add_ldflags()` call. + +### Stale build state + +If you hit unexpected errors after changing toolchain settings, clean everything: + +```bash +rm -rf .xmake build ~/.xmake +``` + +Then reconfigure and rebuild. diff --git a/repo/packages/c/consul/xmake.lua b/repo/packages/c/consul/xmake.lua index 6982e6f03..82bd803b6 100644 --- a/repo/packages/c/consul/xmake.lua +++ b/repo/packages/c/consul/xmake.lua @@ -29,10 +29,14 @@ package("consul") end) on_test(function (package) + -- Skip binary verification when cross-compiling (e.g. Windows target on Linux host) + if package:is_cross() then + return + end if is_plat("windows") then os.run("%s version", package:installdir("bin", "consul.exe")) - elseif is_plat("linux") then - -- this should include macosx as well, but needs more logic to differentiate arm64 vs + elseif is_plat("linux") then + -- this should include macosx as well, but needs more logic to differentiate arm64 vs -- amd64 since arm64 binary won't run on amd64 macs. arm64 macs have Rosetta though so -- they can run the amd64 binary. os.run("%s version", package:installdir("bin", "consul")) diff --git a/repo/packages/n/nomad/xmake.lua b/repo/packages/n/nomad/xmake.lua index 85ea10985..20380e1a1 100644 --- a/repo/packages/n/nomad/xmake.lua +++ b/repo/packages/n/nomad/xmake.lua @@ -29,6 +29,9 @@ package("nomad") end) on_test(function (package) + if package:is_cross() then + return + end if is_plat("windows") then os.run("%s version", package:installdir("bin", "nomad.exe")) elseif is_plat("linux") then diff --git a/repo/packages/o/oidctoken/xmake.lua b/repo/packages/o/oidctoken/xmake.lua index 76360e7bf..4dc231b21 100644 --- a/repo/packages/o/oidctoken/xmake.lua +++ b/repo/packages/o/oidctoken/xmake.lua @@ -15,6 +15,9 @@ package("oidctoken") end) on_test(function (package) + if package:is_cross() then + return + end if is_plat("windows") then os.run("%s --help", package:installdir("bin", "OidcToken.exe")) else diff --git a/scripts/win_cross/get_win_sdk.sh b/scripts/win_cross/get_win_sdk.sh new file mode 100755 index 000000000..b22d1bf3a --- /dev/null +++ b/scripts/win_cross/get_win_sdk.sh @@ -0,0 +1,305 @@ +#!/bin/bash +# +# Downloads xwin and uses it to fetch the Windows SDK and MSVC CRT headers/libs +# needed for cross-compiling Windows binaries from Linux using clang-cl. +# +# Usage: +# ./get_win_sdk.sh [output_dir] +# +# Output defaults to ~/.xwin-sdk (override via $XWIN_SDK_DIR or first argument). + +set -euo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +die() { echo "ERROR: $1" >&2; exit 1; } + +sdk_dir="${1:-${XWIN_SDK_DIR:-${HOME}/.xwin-sdk}}" + +if [[ "${sdk_dir}" == "--help" ]]; then + echo "usage: $(basename "${BASH_SOURCE[0]}") [output_dir]" + echo "" + echo "Downloads the Windows SDK and MSVC CRT via xwin for cross-compilation." + echo "Default output: ~/.xwin-sdk (override via \$XWIN_SDK_DIR or first argument)" + exit 0 +fi + +# If the directory already has SDK content, skip download +if [ -d "${sdk_dir}/sdk/include/um" ] && [ -d "${sdk_dir}/crt/include" ]; then + echo "SDK already present at '${sdk_dir}', skipping download." + echo "Delete the directory to force re-download." + # Still create the compat layout in case it's missing (e.g. script was updated) + CREATE_COMPAT_ONLY=true +else + CREATE_COMPAT_ONLY=false +fi + +if [ -e "${sdk_dir}" ]; then + # Allow re-use of existing empty or partial directory + if [ -d "${sdk_dir}" ]; then + : + else + die "'${sdk_dir}' exists but is not a directory" + fi +fi + +mkdir -p "${sdk_dir}" + +# ------------------------------------------------------------------------- +# Detect LLVM installation +# ------------------------------------------------------------------------- +LLVM_BIN="${LLVM_BIN_DIR:-}" +if [ -z "${LLVM_BIN}" ]; then + # Try common locations + for candidate in /usr/lib/llvm-19/bin /usr/lib/llvm-18/bin /usr/lib/llvm-17/bin; do + if [ -x "${candidate}/clang" ]; then + LLVM_BIN="${candidate}" + break + fi + done +fi +if [ -z "${LLVM_BIN}" ]; then + # Fallback: try to find clang on PATH + CLANG_PATH=$(command -v clang 2>/dev/null || true) + if [ -n "${CLANG_PATH}" ]; then + LLVM_BIN=$(dirname "$(readlink -f "${CLANG_PATH}")") + fi +fi +if [ -z "${LLVM_BIN}" ]; then + die "Could not find LLVM/clang installation. Set LLVM_BIN_DIR to the bin directory." +fi +echo "Using LLVM at: ${LLVM_BIN}" + +# ------------------------------------------------------------------------- +# Download xwin binary and fetch SDK (skip if already present) +# ------------------------------------------------------------------------- +if [ "${CREATE_COMPAT_ONLY}" = false ]; then + XWIN_VERSION="0.6.5" + XWIN_ARCHIVE="xwin-${XWIN_VERSION}-x86_64-unknown-linux-musl.tar.gz" + XWIN_URL="https://github.com/Jake-Shadle/xwin/releases/download/${XWIN_VERSION}/${XWIN_ARCHIVE}" + + TMPDIR=$(mktemp -d) + trap 'rm -rf "${TMPDIR}"' EXIT + + echo "Downloading xwin ${XWIN_VERSION}..." + if command -v wget &>/dev/null; then + wget -q --show-progress -O "${TMPDIR}/${XWIN_ARCHIVE}" "${XWIN_URL}" + elif command -v curl &>/dev/null; then + curl -fSL --progress-bar -o "${TMPDIR}/${XWIN_ARCHIVE}" "${XWIN_URL}" + else + die "Neither wget nor curl found" + fi + + echo "Extracting xwin..." + tar -xzf "${TMPDIR}/${XWIN_ARCHIVE}" -C "${TMPDIR}" + + XWIN_BIN="${TMPDIR}/xwin-${XWIN_VERSION}-x86_64-unknown-linux-musl/xwin" + if [ ! -x "${XWIN_BIN}" ]; then + die "xwin binary not found after extraction" + fi + + echo "Fetching Windows SDK and CRT (this may take a few minutes)..." + "${XWIN_BIN}" --accept-license splat --output "${sdk_dir}" +fi + +# ------------------------------------------------------------------------- +# Create tool wrapper scripts in bin/ +# ------------------------------------------------------------------------- +BIN_DIR="${sdk_dir}/bin" +mkdir -p "${BIN_DIR}" + +# clang-cl wrapper (since the host may not have a clang-cl symlink) +cat > "${BIN_DIR}/clang-cl" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/clang" --driver-mode=cl -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH "\$@" +WRAPPER +chmod +x "${BIN_DIR}/clang-cl" + +# clang wrapper for GNU assembly (.S files) +cat > "${BIN_DIR}/clang" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/clang" "\$@" +WRAPPER +chmod +x "${BIN_DIR}/clang" + +# ------------------------------------------------------------------------- +# Create MSVC-compatible directory layout for xmake package builds. +# +# xmake's built-in msvc toolchain on Linux uses find_build_tools() which +# expects the following structure: +# <sdk>/VC/Tools/MSVC/<version>/include → CRT headers +# <sdk>/VC/Tools/MSVC/<version>/lib/<arch> → CRT libs +# <sdk>/Windows Kits/10/Include/<ver>/{ucrt,um,shared} → SDK headers +# <sdk>/Windows Kits/10/Lib/<ver>/{ucrt,um}/<arch> → SDK libs +# <sdk>/bin/<arch>/ → tool wrappers +# +# We create this layout using symlinks back to the xwin flat layout. +# ------------------------------------------------------------------------- +echo "Creating MSVC-compatible directory layout..." + +FAKE_VC_VER="14.0.0" +FAKE_SDK_VER="10.0.0.0" + +# --- VC Tools (CRT) --- +VC_DIR="${sdk_dir}/VC/Tools/MSVC/${FAKE_VC_VER}" +mkdir -p "${VC_DIR}" +ln -sfn "${sdk_dir}/crt/include" "${VC_DIR}/include" +mkdir -p "${VC_DIR}/lib" +ln -sfn "${sdk_dir}/crt/lib/x86_64" "${VC_DIR}/lib/x64" + +# --- Windows Kits (SDK headers) --- +WINSDK_INC="${sdk_dir}/Windows Kits/10/Include/${FAKE_SDK_VER}" +mkdir -p "${WINSDK_INC}" +ln -sfn "${sdk_dir}/sdk/include/ucrt" "${WINSDK_INC}/ucrt" +ln -sfn "${sdk_dir}/sdk/include/um" "${WINSDK_INC}/um" +ln -sfn "${sdk_dir}/sdk/include/shared" "${WINSDK_INC}/shared" + +# --- Windows Kits (SDK libs) --- +WINSDK_LIB="${sdk_dir}/Windows Kits/10/Lib/${FAKE_SDK_VER}" +mkdir -p "${WINSDK_LIB}/ucrt" "${WINSDK_LIB}/um" +ln -sfn "${sdk_dir}/sdk/lib/ucrt/x86_64" "${WINSDK_LIB}/ucrt/x64" +ln -sfn "${sdk_dir}/sdk/lib/um/x86_64" "${WINSDK_LIB}/um/x64" + +# --- Tool wrappers in bin/<arch>/ (for msvc toolchain PATH setup) --- +ARCH_BIN="${sdk_dir}/bin/x64" +mkdir -p "${ARCH_BIN}" + +# cl → clang-cl wrapper +cat > "${ARCH_BIN}/cl" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/clang" --driver-mode=cl -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/cl" +cp "${ARCH_BIN}/cl" "${ARCH_BIN}/cl.exe" + +# link → lld-link (with /lib mode redirecting to llvm-lib for archiver use) +# xmake sets ar=link.exe for non-LTO MSVC builds and may pass linker-only flags +# like /opt:ref to the archiver. We detect /lib mode, filter those flags, and +# redirect to llvm-lib. Also handles response files (@file) that xmake uses +# when the argument list is too long. +cat > "${ARCH_BIN}/link" << WRAPPER +#!/bin/bash +ALL_ARGS=() +for arg in "\$@"; do + if [[ "\$arg" == @* ]]; then + rspfile="\${arg#@}" + while IFS= read -r line; do + [[ -n "\$line" ]] && ALL_ARGS+=("\$line") + done < "\$rspfile" + else + ALL_ARGS+=("\$arg") + fi +done +LIB_MODE=false +HAS_OUT_LIB=false +HAS_OBJ_ONLY=true +ARGS=() +for arg in "\${ALL_ARGS[@]}"; do + lower="\${arg,,}" + case "\$lower" in + /lib|-lib) LIB_MODE=true ;; + /out:*.lib|-out:*.lib) HAS_OUT_LIB=true; ARGS+=("\$arg") ;; + /opt:*|-opt:*) ;; + /subsystem:*|-subsystem:*) HAS_OBJ_ONLY=false; ARGS+=("\$arg") ;; + *.exe) HAS_OBJ_ONLY=false; ARGS+=("\$arg") ;; + *) ARGS+=("\$arg") ;; + esac +done +if [ "\$LIB_MODE" = true ] || ([ "\$HAS_OUT_LIB" = true ] && [ "\$HAS_OBJ_ONLY" = true ]); then + LIB_ARGS=() + for arg in "\${ARGS[@]}"; do + case "\${arg,,}" in + -nodefaultlib:*|/nodefaultlib:*) ;; + *) LIB_ARGS+=("\$arg") ;; + esac + done + exec "${LLVM_BIN}/llvm-lib" "\${LIB_ARGS[@]}" +else + exec "${LLVM_BIN}/lld-link" "\$@" +fi +WRAPPER +chmod +x "${ARCH_BIN}/link" +cp "${ARCH_BIN}/link" "${ARCH_BIN}/link.exe" + +# lib → llvm-lib +cat > "${ARCH_BIN}/lib" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/llvm-lib" "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/lib" +cp "${ARCH_BIN}/lib" "${ARCH_BIN}/lib.exe" + +# rc → llvm-rc (with SDK include paths for winres.h etc.) +cat > "${ARCH_BIN}/rc" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/llvm-rc" /I "${sdk_dir}/crt/include" /I "${sdk_dir}/sdk/include/ucrt" /I "${sdk_dir}/sdk/include/um" /I "${sdk_dir}/sdk/include/shared" "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/rc" +cp "${ARCH_BIN}/rc" "${ARCH_BIN}/rc.exe" + +# ml64 → llvm-ml (MASM-compatible assembler) +cat > "${ARCH_BIN}/ml64" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/llvm-ml" -m64 "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/ml64" +cp "${ARCH_BIN}/ml64" "${ARCH_BIN}/ml64.exe" + +# clang-cl (for xmake's built-in clang-cl toolchain detection) +cat > "${ARCH_BIN}/clang-cl" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/clang" --driver-mode=cl -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/clang-cl" + +# llvm-ar (cmake's clang-cl driver may use llvm-ar as archiver name but with +# MSVC-style flags like /nologo /out: — redirect to llvm-lib which handles these) +cat > "${ARCH_BIN}/llvm-ar" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/llvm-lib" "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/llvm-ar" + +# lld-link (for LTO builds where clang-cl toolchain uses lld-link) +cat > "${ARCH_BIN}/lld-link" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/lld-link" "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/lld-link" + +# mt → llvm-mt (manifest tool) +cat > "${ARCH_BIN}/mt" << WRAPPER +#!/bin/bash +exec "${LLVM_BIN}/llvm-mt" "\$@" +WRAPPER +chmod +x "${ARCH_BIN}/mt" +cp "${ARCH_BIN}/mt" "${ARCH_BIN}/mt.exe" + +# ------------------------------------------------------------------------- +# Create debug CRT lib symlinks (cmake's try_compile uses Debug config +# by default, which links against msvcrtd.lib etc. -- these don't exist +# in xwin since it only ships release libs. Symlink to release versions +# so cmake compiler tests pass.) +# ------------------------------------------------------------------------- +CRT_LIB="${sdk_dir}/crt/lib/x86_64" +for lib in msvcrt MSVCRT vcruntime msvcprt libcmt LIBCMT libcpmt libcpmt1 libconcrt libconcrt1 libvcruntime; do + release="${CRT_LIB}/${lib}.lib" + debug="${CRT_LIB}/${lib}d.lib" + if [ -f "${release}" ] && [ ! -e "${debug}" ]; then + ln -sfn "${lib}.lib" "${debug}" + fi +done + +echo "" +echo "Windows SDK installed to: ${sdk_dir}" +echo " SDK headers: ${sdk_dir}/sdk/include/um" +echo " SDK libs: ${sdk_dir}/sdk/lib/um/x86_64" +echo " CRT headers: ${sdk_dir}/crt/include" +echo " CRT libs: ${sdk_dir}/crt/lib/x86_64" +echo " Tool wrappers: ${BIN_DIR}/" +echo " MSVC compat: ${sdk_dir}/VC/ and ${sdk_dir}/Windows Kits/" +echo "" +echo "Usage:" +echo " xmake config -p windows -a x64 --toolchain=clang-cl --sdk=\${sdk_dir}" +echo "" +echo "Done" diff --git a/src/transports/winsock/xmake.lua b/src/transports/winsock/xmake.lua index c14283546..cdba75885 100644 --- a/src/transports/winsock/xmake.lua +++ b/src/transports/winsock/xmake.lua @@ -5,7 +5,7 @@ target("winsock") set_group("transports") add_headerfiles("**.h") add_files("**.cpp") - add_links("Ws2_32") + add_links("ws2_32") add_includedirs(".") set_symbols("debug") add_deps("zenbase", "transport-sdk") diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index a781dc340..3347f1afe 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -12,8 +12,8 @@ #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> # include <shellapi.h> -# include <Shlwapi.h> -# pragma comment(lib, "Shlwapi.lib") +# include <shlwapi.h> +# pragma comment(lib, "shlwapi.lib") #endif #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC diff --git a/src/zen/xmake.lua b/src/zen/xmake.lua index 4c134404a..df249ade4 100644 --- a/src/zen/xmake.lua +++ b/src/zen/xmake.lua @@ -14,8 +14,10 @@ target("zen") if is_plat("windows") then add_files("zen.rc") - add_ldflags("/subsystem:console,5.02") - add_ldflags("/LTCG") + add_ldflags("/subsystem:console,5.02", {force = true}) + if not (get_config("toolchain") or ""):find("clang") then + add_ldflags("/LTCG") + end end if is_plat("macosx") then diff --git a/src/zen/zen.rc b/src/zen/zen.rc index 0617681a7..3adf25b72 100644 --- a/src/zen/zen.rc +++ b/src/zen/zen.rc @@ -7,7 +7,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) -101 ICON "..\\zen.ico" +101 ICON "../zen.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION ZEN_CFG_VERSION_MAJOR,ZEN_CFG_VERSION_MINOR,ZEN_CFG_VERSION_ALTER,0 diff --git a/src/zenbase/include/zenbase/zenbase.h b/src/zenbase/include/zenbase/zenbase.h index 2aec1f314..1d5051c5b 100644 --- a/src/zenbase/include/zenbase/zenbase.h +++ b/src/zenbase/include/zenbase/zenbase.h @@ -211,7 +211,24 @@ char (&ZenArrayCountHelper(const T (&)[N]))[N + 1]; # define ZEN_EXE_SUFFIX_LITERAL "" #endif -#define ZEN_UNUSED(...) ((void)__VA_ARGS__) +#if ZEN_COMPILER_CLANG +// Clang warns about the comma operator in ((void)a, b) with -Wunused-value. +// Use a fold expression via a helper to suppress each argument individually. +namespace zen::detail { +inline void +unused_impl() +{ +} +template<typename... T> +inline void +unused_impl(T&&...) +{ +} +} // namespace zen::detail +# define ZEN_UNUSED(...) ::zen::detail::unused_impl(__VA_ARGS__) +#else +# define ZEN_UNUSED(...) ((void)__VA_ARGS__) +#endif ////////////////////////////////////////////////////////////////////////// diff --git a/src/zencore-test/targetver.h b/src/zencore-test/targetver.h index d432d6993..4805141de 100644 --- a/src/zencore-test/targetver.h +++ b/src/zencore-test/targetver.h @@ -7,4 +7,4 @@ // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. -#include <SDKDDKVer.h> +#include <sdkddkver.h> diff --git a/src/zencore/callstack.cpp b/src/zencore/callstack.cpp index ee0b0625a..a16bb3f13 100644 --- a/src/zencore/callstack.cpp +++ b/src/zencore/callstack.cpp @@ -6,7 +6,7 @@ #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> -# include <Dbghelp.h> +# include <DbgHelp.h> #endif #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index e557b376c..0d361801f 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -17,7 +17,7 @@ #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> -# include <ShlObj.h> +# include <shlobj.h> # pragma comment(lib, "shell32.lib") # pragma comment(lib, "ole32.lib") #endif diff --git a/src/zencore/include/zencore/string.h b/src/zencore/include/zencore/string.h index 7b46f0e38..60293a313 100644 --- a/src/zencore/include/zencore/string.h +++ b/src/zencore/include/zencore/string.h @@ -333,7 +333,8 @@ public: #if defined(__clang__) && !defined(__APPLE__) && !defined(_MSC_VER) /* UE Toolchain Clang has different types for int64_t and long long so an override is - needed here. Without it, Clang can't disambiguate integer overloads */ + needed here. Without it, Clang can't disambiguate integer overloads. + On MSVC ABI (including clang-cl), int64_t is long long so no separate overload is needed. */ inline StringBuilderImpl& operator<<(long long n) { IntNum Str(n); diff --git a/src/zencore/memtrack/callstacktrace.cpp b/src/zencore/memtrack/callstacktrace.cpp index 013c51535..ccbea1282 100644 --- a/src/zencore/memtrack/callstacktrace.cpp +++ b/src/zencore/memtrack/callstacktrace.cpp @@ -193,8 +193,12 @@ private: std::atomic_uint32_t CallstackIdCounter{1}; // 0 is reserved for "unknown callstack" }; +} // namespace zen + # if UE_CALLSTACK_TRACE_USE_UNWIND_TABLES +namespace zen { + /* * Windows' x64 binaries contain a ".pdata" section that describes the location * and size of its functions and details on how to unwind them. The unwind @@ -908,10 +912,12 @@ FBacktracer::GetBacktraceId(void* AddressOfReturnAddress) // queue (i.e. the processing thread has caught up processing). return CallstackTracer.AddCallstack(BacktraceEntry); } -} +} // namespace zen # else // UE_CALLSTACK_TRACE_USE_UNWIND_TABLES +namespace zen { + //////////////////////////////////////////////////////////////////////////////// class FBacktracer { @@ -921,8 +927,8 @@ public: static FBacktracer* Get(); inline uint32_t GetBacktraceId(void* AddressOfReturnAddress); uint32_t GetBacktraceId(uint64_t ReturnAddress); - void AddModule(uintptr_t Base, const char16_t* Name) {} - void RemoveModule(uintptr_t Base) {} + void AddModule(uintptr_t /*Base*/, const char16_t* /*Name*/) {} + void RemoveModule(uintptr_t /*Base*/) {} private: static FBacktracer* Instance; @@ -963,6 +969,7 @@ FBacktracer::GetBacktraceId(void* AddressOfReturnAddress) uint32_t FBacktracer::GetBacktraceId(uint64_t ReturnAddress) { + ZEN_UNUSED(ReturnAddress); # if !UE_BUILD_SHIPPING uint64_t StackFrames[256]; int32_t NumStackFrames = FPlatformStackWalk::CaptureStackBackTrace(StackFrames, UE_ARRAY_COUNT(StackFrames)); @@ -1006,7 +1013,8 @@ FBacktracer::GetBacktraceId(uint64_t ReturnAddress) return 0; } -} + +} // namespace zen # endif // UE_CALLSTACK_TRACE_USE_UNWIND_TABLES diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 852678ffe..080607f13 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -21,8 +21,8 @@ ZEN_THIRD_PARTY_INCLUDES_START # include <Psapi.h> # include <shellapi.h> -# include <Shlobj.h> -# include <TlHelp32.h> +# include <shlobj.h> +# include <tlhelp32.h> #else # include <fcntl.h> # include <pthread.h> diff --git a/src/zenhorde/hordetransportaes.cpp b/src/zenhorde/hordetransportaes.cpp index 986dd3705..505b6bde7 100644 --- a/src/zenhorde/hordetransportaes.cpp +++ b/src/zenhorde/hordetransportaes.cpp @@ -12,7 +12,7 @@ #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> # include <bcrypt.h> -# pragma comment(lib, "Bcrypt.lib") +# pragma comment(lib, "bcrypt.lib") #else ZEN_THIRD_PARTY_INCLUDES_START # include <openssl/evp.h> diff --git a/src/zenhttp/servers/httpplugin.cpp b/src/zenhttp/servers/httpplugin.cpp index a1bb719c8..31b0315d4 100644 --- a/src/zenhttp/servers/httpplugin.cpp +++ b/src/zenhttp/servers/httpplugin.cpp @@ -147,7 +147,7 @@ public: HttpPluginServerRequest& operator=(const HttpPluginServerRequest&) = delete; // As this is plugin transport connection used for specialized connections we assume it is not a machine local connection - bool IsLocalMachineRequest() const override { return false; } + virtual bool IsLocalMachineRequest() const override { return false; } virtual std::string_view GetAuthorizationHeader() const override; virtual Oid ParseSessionId() const override; virtual uint32_t ParseRequestId() const override; diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp index 4d6a53696..f8fb1c9be 100644 --- a/src/zenhttp/servers/httpsys.cpp +++ b/src/zenhttp/servers/httpsys.cpp @@ -1173,7 +1173,7 @@ HttpSysServer::RegisterHttpUrls(int BasePort) { Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); - if ((Result == ERROR_SHARING_VIOLATION)) + if (Result == ERROR_SHARING_VIOLATION) { ZEN_INFO("Desired port {} is in use (HttpAddUrlToUrlGroup returned: {}), retrying", EffectivePort, diff --git a/src/zenserver/storage/vfs/vfsservice.cpp b/src/zenserver/storage/vfs/vfsservice.cpp index 863ec348a..f418c4131 100644 --- a/src/zenserver/storage/vfs/vfsservice.cpp +++ b/src/zenserver/storage/vfs/vfsservice.cpp @@ -62,7 +62,7 @@ GetContentAsCbObject(HttpServerRequest& HttpReq, CbObject& Cb) // echo {"method": "mount", "params": {"path": "d:\\VFS_ROOT"}} | curl.exe http://localhost:8558/vfs --data-binary @- // echo {"method": "unmount"} | curl.exe http://localhost:8558/vfs --data-binary @- -VfsService::VfsService(HttpStatusService& StatusService, VfsServiceImpl* ServiceImpl) : m_StatusService(StatusService), m_Impl(ServiceImpl) +VfsService::VfsService(HttpStatusService& StatusService, VfsServiceImpl* ServiceImpl) : m_Impl(ServiceImpl), m_StatusService(StatusService) { m_Router.RegisterRoute( "info", diff --git a/src/zenserver/targetver.h b/src/zenserver/targetver.h index d432d6993..4805141de 100644 --- a/src/zenserver/targetver.h +++ b/src/zenserver/targetver.h @@ -7,4 +7,4 @@ // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. -#include <SDKDDKVer.h> +#include <sdkddkver.h> diff --git a/src/zenserver/xmake.lua b/src/zenserver/xmake.lua index 52889fff5..6b29dadfb 100644 --- a/src/zenserver/xmake.lua +++ b/src/zenserver/xmake.lua @@ -60,13 +60,15 @@ target("zenserver") end if is_plat("windows") then - add_ldflags("/subsystem:console,5.02") - add_ldflags("/MANIFEST:EMBED") - add_ldflags("/LTCG") + add_ldflags("/subsystem:console,5.02", {force = true}) + add_ldflags("/MANIFEST:EMBED", {force = true}) + if not (get_config("toolchain") or ""):find("clang") then + add_ldflags("/LTCG") + end add_files("zenserver.rc") add_cxxflags("/bigobj") add_links("delayimp", "projectedfslib") - add_ldflags("/delayload:ProjectedFSLib.dll") + add_ldflags("/delayload:ProjectedFSLib.dll", {force = true}) else remove_files("windows/**") end diff --git a/src/zenserver/zenserver.rc b/src/zenserver/zenserver.rc index f353bd9cc..abe1acf71 100644 --- a/src/zenserver/zenserver.rc +++ b/src/zenserver/zenserver.rc @@ -28,7 +28,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "..\\zen.ico" +IDI_ICON1 ICON "../zen.ico" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/thirdparty/xmake.lua b/thirdparty/xmake.lua index f06eb6fff..e8832d50a 100644 --- a/thirdparty/xmake.lua +++ b/thirdparty/xmake.lua @@ -36,7 +36,7 @@ target('rpmalloc') set_kind("static") set_group('thirdparty') set_languages("c17", "cxx20") - if is_os("windows") then + if is_os("windows") and not (get_config("toolchain") or ""):find("clang") then add_cflags("/experimental:c11atomics", {force=true, tools="cl"}) end add_defines("RPMALLOC_FIRST_CLASS_HEAPS=1", "ENABLE_STATISTICS=1", "ENABLE_OVERRIDE=0") @@ -82,7 +82,7 @@ target("blake3") add_headerfiles("blake3/c/blake3.h") add_includedirs("blake3/c", {public=true}) - if is_os("windows") then + if is_os("windows") and not (get_config("toolchain") or ""):find("clang") then add_cflags("/experimental:c11atomics", {tools="cl"}) add_cflags("/wd4245", {force = true}) -- conversion from 'type1' to 'type2', possible loss of data elseif is_os("macosx") then @@ -6,6 +6,12 @@ set_configvar("ZEN_DATA_FORCE_SCRUB_VERSION", 0) set_allowedplats("windows", "linux", "macosx") set_allowedarchs("windows|x64", "linux|x86_64", "macosx|x86_64", "macosx|arm64") +-- Returns true when building for Windows with native MSVC (not clang-cl cross-compilation) +function is_native_msvc() + local tc = get_config("toolchain") or "" + return is_plat("windows") and tc ~= "clang-cl" +end + -------------------------------------------------------------------------- -- We support debug and release modes. On Windows we use static CRT to -- minimize dependencies. @@ -146,7 +152,9 @@ enable_unity = false if is_mode("release") then -- LTO does not appear to work with the current Linux UE toolchain -- Also, disabled LTO on Mac to reduce time spent building openssl tests - if not is_plat("linux", "macosx") then + -- Disabled for cross-compilation (clang-cl on Linux) due to cmake package compat issues + local is_cross_win = is_plat("windows") and is_host("linux") + if not is_plat("linux", "macosx") and not is_cross_win then set_policy("build.optimization.lto", true) end set_optimize("fastest") @@ -169,6 +177,12 @@ else set_encodings("source:utf-8", "target:utf-8") end +-- When cross-compiling with clang-cl, the xwin SDK may ship a newer MSVC STL +-- than the host clang version supports. Bypass the version gate. +if is_plat("windows") and not is_native_msvc() then + add_defines("_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH") +end + if is_os("windows") then add_defines( "_CRT_SECURE_NO_WARNINGS", @@ -181,59 +195,49 @@ if is_os("windows") then "_WIN32_WINNT=0x0A00", "_WINSOCK_DEPRECATED_NO_WARNINGS" -- let us use the ANSI functions ) - -- Make builds more deterministic and portable - add_cxxflags("/d1trimfile:$(curdir)\\") -- eliminates the base path from __FILE__ paths - add_cxxflags("/experimental:deterministic") -- (more) deterministic compiler output - add_ldflags("/PDBALTPATH:%_PDB%") -- deterministic pdb reference in exe - - add_cxxflags("/Zc:preprocessor") -- Enable preprocessor conformance mode - add_cxxflags("/Zc:u8EscapeEncoding") -- Enable UTF-8 encoding for u8 string literals - add_cxxflags("/Zc:inline") -- Enforce inline semantics + -- Make builds more deterministic and portable (MSVC-only flags) + if is_native_msvc() then + add_cxxflags("/d1trimfile:$(curdir)\\") -- eliminates the base path from __FILE__ paths + add_cxxflags("/experimental:deterministic") -- (more) deterministic compiler output + add_ldflags("/PDBALTPATH:%_PDB%") -- deterministic pdb reference in exe + add_cxxflags("/Zc:u8EscapeEncoding") -- Enable UTF-8 encoding for u8 string literals (clang does this by default) + add_cxxflags("/Zc:preprocessor") -- Enable preprocessor conformance mode + add_cxxflags("/Zc:inline") -- Enforce inline semantics + end -- add_ldflags("/MAP") end -if is_os("linux") or is_os("macosx") then - add_cxxflags("-Wno-implicit-fallthrough") - add_cxxflags("-Wno-missing-field-initializers") - add_cxxflags("-Wno-strict-aliasing") - add_cxxflags("-Wno-switch") - add_cxxflags("-Wno-unused-lambda-capture") - add_cxxflags("-Wno-unused-private-field") - add_cxxflags("-Wno-unused-value") - add_cxxflags("-Wno-unused-variable") - add_cxxflags("-Wno-vla-cxx-extension") +-- Clang warning suppressions (native clang on Linux/Mac, or clang-cl cross-compile) +if is_os("linux") or is_os("macosx") or not is_native_msvc() then + -- Silence warnings about unrecognized -Wno-* flags on older clang versions + add_cxxflags("-Wno-unknown-warning-option", {force = true}) + add_cxxflags("-Wno-delete-non-abstract-non-virtual-dtor", {force = true}) + add_cxxflags("-Wno-format", {force = true}) + add_cxxflags("-Wno-implicit-fallthrough", {force = true}) + add_cxxflags("-Wno-inconsistent-missing-override", {force = true}) + add_cxxflags("-Wno-missing-field-initializers", {force = true}) + add_cxxflags("-Wno-nonportable-include-path", {force = true}) + add_cxxflags("-Wno-sign-compare", {force = true}) + add_cxxflags("-Wno-strict-aliasing", {force = true}) + add_cxxflags("-Wno-switch", {force = true}) + add_cxxflags("-Wno-unused-lambda-capture", {force = true}) + add_cxxflags("-Wno-unused-private-field", {force = true}) + add_cxxflags("-Wno-unused-value", {force = true}) + add_cxxflags("-Wno-unused-variable", {force = true}) + add_cxxflags("-Wno-vla-cxx-extension", {force = true}) -- GCC false positive: constinit static locals used by reference are reported as unused-but-set - add_cxxflags("-Wno-unused-but-set-variable", {tools="gcc"}) + add_cxxflags("-Wno-unused-but-set-variable", {tools = "gcc"}) end +-- Additional suppressions specific to clang-cl cross-compilation if get_config("toolchain") == "clang-cl" then - add_cxxflags("-Wno-unknown-warning-option", {force=true}) - add_cxxflags("-Wno-cast-function-type-mismatch", {force=true}) - add_cxxflags("-Wno-delete-non-abstract-non-virtual-dtor", {force=true}) - add_cxxflags("-Wno-format", {force=true}) - add_cxxflags("-Wno-implicit-fallthrough", {force=true}) - add_cxxflags("-Wno-inconsistent-missing-override", {force=true}) - add_cxxflags("-Wno-missing-field-initializers", {force=true}) - add_cxxflags("-Wno-parentheses-equality", {force=true}) - add_cxxflags("-Wno-reorder-ctor", {force=true}) - add_cxxflags("-Wno-sign-compare", {force=true}) - add_cxxflags("-Wno-strict-aliasing", {force=true}) - add_cxxflags("-Wno-switch", {force=true}) - add_cxxflags("-Wno-unused-but-set-variable", {force=true}) - add_cxxflags("-Wno-unused-lambda-capture", {force=true}) - add_cxxflags("-Wno-unused-parameter", {force=true}) - add_cxxflags("-Wno-unused-private-field", {force=true}) - add_cxxflags("-Wno-unused-value", {force=true}) - add_cxxflags("-Wno-unused-variable", {force=true}) - add_cxxflags("-Wno-vla-cxx-extension", {force=true}) - add_cflags("-Wno-unknown-warning-option", {force=true}) - add_cflags("-Wno-unused-command-line-argument", {force=true}) -end - -if is_os("macosx") then - -- silence warnings about -Wno-vla-cxx-extension since to my knowledge we can't - -- detect the clang version used in Xcode and only recent versions contain this flag - add_cxxflags("-Wno-unknown-warning-option") + add_cxxflags("-Wno-cast-function-type-mismatch", {force = true}) + add_cxxflags("-Wno-parentheses-equality", {force = true}) + add_cxxflags("-Wno-reorder-ctor", {force = true}) + add_cxxflags("-Wno-unused-but-set-variable", {force = true}) + add_cxxflags("-Wno-unused-parameter", {force = true}) + add_cflags("-Wno-unknown-warning-option", {force = true}) + add_cflags("-Wno-unused-command-line-argument", {force = true}) end if is_os("linux") then |