aboutsummaryrefslogtreecommitdiff
path: root/xmake.lua
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2026-03-17 13:56:50 +0100
committerGitHub Enterprise <[email protected]>2026-03-17 13:56:50 +0100
commit90ca5315b2745fde3ccf9ce9d7106537cae566c8 (patch)
tree4f44464225dc8c8e8386f428b31697971e0f0a2f /xmake.lua
parentrevise oplog block arrangement (#842) (diff)
downloadzen-90ca5315b2745fde3ccf9ce9d7106537cae566c8.tar.xz
zen-90ca5315b2745fde3ccf9ce9d7106537cae566c8.zip
add sanitizer options to xmake (#847)v5.7.23-pre1v5.7.23-pre0
- Improvement: Add easy access options for sanitizers with `xmake config` and `xmake test` as options - `--msan=[y|n]` Enable MemorySanitizer (Linux only, requires all deps instrumented) - `--asan=[y|n]` Enable AddressSanitizer (disables mimalloc and sentry) - `--tsan=[y|n]` Enable ThreadSanitizer (Linux/Mac only)
Diffstat (limited to 'xmake.lua')
-rw-r--r--xmake.lua187
1 files changed, 157 insertions, 30 deletions
diff --git a/xmake.lua b/xmake.lua
index 846bc8f1d..d40131e35 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -19,6 +19,13 @@ end
set_allowedmodes("debug", "release")
add_rules("mode.debug", "mode.release")
+-- Disable xmake's run.autobuild (on by default in xmake 3.x). Auto-rebuild
+-- before run is dangerous: if the current config differs from the build that
+-- produced the binary (e.g. ASAN was toggled), xmake can rebuild with wrong
+-- flags, causing linker errors (e.g. MSVC annotate_string mismatch). Users
+-- are expected to run `xmake build` explicitly before `xmake run`.
+set_policy("run.autobuild", false)
+
if is_plat("windows") then
if false then
-- DLL runtime
@@ -37,44 +44,161 @@ if is_plat("windows") then
end
end
---------------------------------------------------------------------------
-- Sanitizers
--
-- https://xmake.io/api/description/builtin-policies.html#build-sanitizer-address
--
--- When using sanitizers, it may be necessary to change some configuration
--- options. In particular, you may want to use `--zensentry=no` to disable
--- Sentry support as it may not be compatible with some sanitizers. Also,
--- it may be necessary to disable mimalloc by using `--zenmimalloc=no`.
-
--- AddressSanitizer is supported on Windows (MSVC 2019+), Linux, and MacOS
+-- Each sanitizer has a .supp file (asan.supp, tsan.supp, msan.supp). A single
+-- sanitizer.options rule handles all three in one before_run callback.
+--
+-- asan.supp and msan.supp contain key=value runtime options (no native
+-- "options from file" mechanism exists for either sanitizer on any platform).
+--
+-- tsan.supp is a mixed-format file: key=value runtime options (parsed by xmake
+-- and set in TSAN_OPTIONS) plus native TSAN suppression patterns (race:...,
+-- mutex:...) that are passed via TSAN_OPTIONS=suppressions=<path> on Linux/Mac.
+-- The TSAN runtime silently skips key=value lines when parsing the suppression
+-- file (they don't match type:pattern; TSAN emits a verbose-level warning).
+--
+-- IMPORTANT: The env vars are set by xmake's before_run hook. If you run a
+-- binary directly (not via xmake run / xmake test) you must set them manually.
-local use_asan = false -- Automatically disables Sentry when set to true
-set_policy("build.sanitizer.address", use_asan)
+--------------------------------------------------------------------------
--- ThreadSanitizer, MemorySanitizer, LeakSanitizer, and UndefinedBehaviorSanitizer
--- are supported on Linux and MacOS only.
+-- AddressSanitizer is supported on Windows (MSVC 2019+), Linux, and MacOS.
+-- Enable with: `xmake config --asan=y` or `xmake test --asan`
+-- (automatically disables mimalloc and sentry, which are incompatible with ASAN)
--
--- You can enable these by editing the xmake.lua directly, or by passing the
--- appropriate flags on the command line:
+-- Options live in asan.supp. For manual runs:
+-- Linux/Mac: ASAN_OPTIONS="new_delete_type_mismatch=0" ./zenserver
+-- Windows: set ASAN_OPTIONS=new_delete_type_mismatch=0
+
+option("asan")
+ set_default(false)
+ set_showmenu(true)
+ set_description("Enable AddressSanitizer (disables mimalloc and sentry)")
+option_end()
+
+-- Global flag: read by sub-target xmake.lua files (like enable_unity)
+use_asan = has_config("asan")
+
+if use_asan then
+ set_policy("build.sanitizer.address", true)
+ -- On Windows/MSVC, explicitly define __SANITIZE_ADDRESS__ so that
+ -- IntelliSense in VS sees it (XmakeDefines -> PreprocessorDefinitions).
+ -- MSVC sets it implicitly at compile time via /fsanitize=address, but
+ -- vsxmake IntelliSense needs it in the explicit defines list.
+ -- Not needed on Clang/GCC: they set it implicitly and adding it via -D
+ -- would cause a macro-redefinition warning (error with -Werror).
+ if is_os("windows") then
+ add_defines("__SANITIZE_ADDRESS__=1")
+ end
+end
+
+--------------------------------------------------------------------------
+
+-- ThreadSanitizer is supported on Linux and MacOS only.
+-- Enable with: `xmake config --tsan=y` or `xmake test --tsan`
--
--- `xmake --policies=build.sanitizer.thread:y` for ThreadSanitizer,
--- `xmake --policies=build.sanitizer.memory:y` for MemorySanitizer, etc.
+-- Key=value options and suppression patterns both live in tsan.supp. For
+-- manual runs: TSAN_OPTIONS="detect_deadlocks=0:suppressions=$(pwd)/tsan.supp"
+
+option("tsan")
+ set_default(false)
+ set_showmenu(true)
+ set_description("Enable ThreadSanitizer (Linux/Mac only)")
+option_end()
--- When using TSAN you will want to also use the suppression tile to silence
--- known benign races. You do this by ensuring the the TSAN_OPTIONS environment
--- vriable is set to something like `TSAN_OPTIONS="suppressions=$(projectdir)/tsan.supp"`
+use_tsan = has_config("tsan")
+
+if use_tsan then
+ if is_os("windows") then
+ raise("TSAN is not supported on Windows. Use Linux or macOS.")
+ end
+ set_policy("build.sanitizer.thread", true)
+end
+
+--------------------------------------------------------------------------
+
+-- MemorySanitizer is supported on Linux only. In practice it rarely works
+-- because all dependencies must also be compiled with MSan instrumentation.
+-- Enable with: `xmake config --msan=y` or `xmake test --msan`
--
--- `prompt> TSAN_OPTIONS="detect_deadlocks=0 suppressions=$(projectdir)/tsan.supp" xmake run zenserver`
+-- Options live in msan.supp. For manual runs: set MSAN_OPTIONS manually.
+
+option("msan")
+ set_default(false)
+ set_showmenu(true)
+ set_description("Enable MemorySanitizer (Linux only, requires all deps instrumented)")
+option_end()
+
+use_msan = has_config("msan")
+
+if use_msan then
+ set_policy("build.sanitizer.memory", true)
+end
+
+--------------------------------------------------------------------------
+
+-- Single rule handles all three sanitizer env vars. parse_supp_opts is defined
+-- once inside before_run (scripting scope) where io is a valid global.
+
+rule("sanitizer.options")
+ before_run(function(target)
+ -- Parses key=value lines from a .supp file. Blank lines and lines whose
+ -- first non-whitespace character is '#' are ignored. Suppression-pattern
+ -- lines (race:..., mutex:...) don't match ^[%w_]+= and are skipped.
+ local function parse_supp_opts(filepath)
+ local opts = {}
+ local content = os.isfile(filepath) and io.readfile(filepath) or ""
+ for line in content:gmatch("[^\r\n]+") do
+ line = line:match("^%s*(.-)%s*$")
+ if line ~= "" and not line:match("^#") and line:match("^[%w_]+=") then
+ table.insert(opts, line)
+ end
+ end
+ return opts
+ end
+
+ if target:policy("build.sanitizer.address") and not os.getenv("ASAN_OPTIONS") then
+ local opts = parse_supp_opts(path.join(os.projectdir(), "asan.supp"))
+ if #opts > 0 then
+ os.setenv("ASAN_OPTIONS", table.concat(opts, ":"))
+ end
+ end
+
+ if target:policy("build.sanitizer.thread") and not os.getenv("TSAN_OPTIONS") then
+ local suppfile = path.join(os.projectdir(), "tsan.supp")
+ local opts = parse_supp_opts(suppfile)
+ -- Also pass the suppression patterns via the native file mechanism when
+ -- both conditions hold:
+ -- 1. Not clang-cl: file path support requires the LLVM/GCC TSAN runtime.
+ -- 2. Platform is Linux or macOS: TSAN is unavailable on Windows
+ -- (config-time raise() above prevents reaching here on Windows).
+ local tc_name = get_config("toolchain") or ""
+ local is_not_clang_cl = tc_name ~= "clang-cl"
+ if os.isfile(suppfile) and is_not_clang_cl
+ and (target:is_plat("linux") or target:is_plat("macosx")) then
+ table.insert(opts, "suppressions=" .. suppfile)
+ end
+ if #opts > 0 then
+ os.setenv("TSAN_OPTIONS", table.concat(opts, ":"))
+ end
+ end
+
+ if target:policy("build.sanitizer.memory") and not os.getenv("MSAN_OPTIONS") then
+ local opts = parse_supp_opts(path.join(os.projectdir(), "msan.supp"))
+ if #opts > 0 then
+ os.setenv("MSAN_OPTIONS", table.concat(opts, ":"))
+ end
+ end
+ end)
+rule_end()
+add_rules("sanitizer.options")
---set_policy("build.sanitizer.thread", true)
--set_policy("build.sanitizer.leak", true)
--set_policy("build.sanitizer.undefined", true)
--- In practice, this does not work because of the difficulty of compiling
--- dependencies with MemorySanitizer.
---set_policy("build.sanitizer.memory", true)
-
--------------------------------------------------------------------------
-- Dependencies
@@ -259,16 +383,16 @@ end
option("zensentry")
set_default(true)
set_showmenu(true)
- set_description("Enables Sentry support")
+ set_description("Enables Sentry support (incompatible with ASAN)")
option_end()
-add_define_by_config("ZEN_USE_SENTRY", "zensentry")
+add_defines("ZEN_USE_SENTRY=" .. ((has_config("zensentry") and not use_asan) and "1" or "0"))
option("zenmimalloc")
- set_default(not use_asan)
+ set_default(true)
set_showmenu(true)
- set_description("Use MiMalloc for faster memory management")
+ set_description("Use MiMalloc for faster memory management (incompatible with ASAN)")
option_end()
-add_define_by_config("ZEN_USE_MIMALLOC", "zenmimalloc")
+add_defines("ZEN_USE_MIMALLOC=" .. ((has_config("zenmimalloc") and not use_asan) and "1" or "0"))
option("zenrpmalloc")
set_default(true)
@@ -486,7 +610,10 @@ task("test")
{'j', "junit", "k", nil, "Enable junit report output"},
{'n', "noskip", "k", nil, "Run skipped tests (passes --no-skip to doctest)"},
{nil, "repeat", "kv", nil, "Repeat tests N times (stops on first failure)"},
- {'v', "verbose", "k", nil, "Route child process output to stdout (zenserver-test)"},
+ {'V', "output", "k", nil, "Route child process output to stdout (zenserver-test)"},
+ {nil, "asan", "k", nil, "Enable AddressSanitizer (disables mimalloc and sentry)"},
+ {nil, "tsan", "k", nil, "Enable ThreadSanitizer (Linux/Mac only)"},
+ {nil, "msan", "k", nil, "Enable MemorySanitizer (Linux only, requires all deps instrumented)"},
{nil, "arguments", "vs", nil, "Extra arguments passed to test runners (after --)"}
}
}