diff options
| author | Dan Engelbrecht <[email protected]> | 2026-03-17 13:56:50 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-17 13:56:50 +0100 |
| commit | 90ca5315b2745fde3ccf9ce9d7106537cae566c8 (patch) | |
| tree | 4f44464225dc8c8e8386f428b31697971e0f0a2f /xmake.lua | |
| parent | revise oplog block arrangement (#842) (diff) | |
| download | zen-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.lua | 187 |
1 files changed, 157 insertions, 30 deletions
@@ -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 --)"} } } |