aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-16 10:52:45 +0100
committerGitHub Enterprise <[email protected]>2026-03-16 10:52:45 +0100
commit79e10a165cf09dc2cc120b3a226c51f87c235f20 (patch)
treecf51b07e097904044b4bf65bc3fe0ad14134074f
parentLinux build improvements (#843) (diff)
downloadzen-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).
-rw-r--r--docs/WindowsOnLinux.md109
-rw-r--r--repo/packages/c/consul/xmake.lua8
-rw-r--r--repo/packages/n/nomad/xmake.lua3
-rw-r--r--repo/packages/o/oidctoken/xmake.lua3
-rwxr-xr-xscripts/win_cross/get_win_sdk.sh305
-rw-r--r--src/transports/winsock/xmake.lua2
-rw-r--r--src/zen/cmds/service_cmd.cpp4
-rw-r--r--src/zen/xmake.lua6
-rw-r--r--src/zen/zen.rc2
-rw-r--r--src/zenbase/include/zenbase/zenbase.h19
-rw-r--r--src/zencore-test/targetver.h2
-rw-r--r--src/zencore/callstack.cpp2
-rw-r--r--src/zencore/filesystem.cpp2
-rw-r--r--src/zencore/include/zencore/string.h3
-rw-r--r--src/zencore/memtrack/callstacktrace.cpp16
-rw-r--r--src/zencore/process.cpp4
-rw-r--r--src/zenhorde/hordetransportaes.cpp2
-rw-r--r--src/zenhttp/servers/httpplugin.cpp2
-rw-r--r--src/zenhttp/servers/httpsys.cpp2
-rw-r--r--src/zenserver/storage/vfs/vfsservice.cpp2
-rw-r--r--src/zenserver/targetver.h2
-rw-r--r--src/zenserver/xmake.lua10
-rw-r--r--src/zenserver/zenserver.rc2
-rw-r--r--thirdparty/xmake.lua4
-rw-r--r--xmake.lua98
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
diff --git a/xmake.lua b/xmake.lua
index 4110f25e2..846bc8f1d 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -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