include(default)
{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}
{% set arch = detect_api.detect_arch() %}
{% set sanitizers = os.getenv("SANITIZERS") %}

{% if not sanitizers %}
{# Sanitizers not configured; no additional settings needed #}
{% else %}

{% if compiler == "msvc" %}
    {{ "Sanitizers are not supported on Windows/MSVC. Please unset the SANITIZERS environment variable." }}
{% endif %}

{% set known_sanitizers = ["address", "thread", "undefinedbehavior"] %}
{% set provided_sanitizers = [] %}
{% for san in sanitizers.split(",") %}
    {% set san = san.strip() %}
    {% if san not in known_sanitizers %}
        {{ "Unknown sanitizer in SANITIZERS: " ~ san }}
    {% endif %}
    {% set _ = provided_sanitizers.append(san) %}
{% endfor %}

{% set enable_asan = "address" in provided_sanitizers %}
{% set enable_tsan = "thread" in provided_sanitizers %}
{% set enable_ubsan = "undefinedbehavior" in provided_sanitizers %}

{% if enable_asan and enable_tsan %}
    {{ "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously." }}
{% endif %}

{% set sanitizer_types = [] %}
{% set defines = [] %}

{% if enable_asan %}
    {% set _ = sanitizer_types.append("address") %}
    {% set _ = defines.append("BOOST_USE_ASAN") %}
    {% set _ = defines.append("BOOST_USE_UCONTEXT") %}
{% elif enable_tsan %}
    {% set _ = sanitizer_types.append("thread") %}
    {% set _ = defines.append("BOOST_USE_TSAN") %}
    {% set _ = defines.append("BOOST_USE_UCONTEXT") %}
{% endif %}

{% if enable_ubsan %}
    {% set _ = sanitizer_types.append("undefined") %}
    {% set _ = sanitizer_types.append("float-divide-by-zero") %}
    {# Clang supports additional UB checks beyond the GCC baseline #}
    {% if compiler == "clang" or compiler == "apple-clang" %}
        {% set _ = sanitizer_types.append("unsigned-integer-overflow") %}
    {% endif %}
{% endif %}

{# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #}
{% set sanitizer_compiler_flags = ["-fno-omit-frame-pointer", "-O1"] %}

{% if compiler == "gcc" %}
    {# Suppress false positive warnings with GCC #}
    {% set _ = sanitizer_compiler_flags.append("-Wno-stringop-overflow") %}

    {% set relocation_flags = [] %}

    {% if arch == "x86_64" and enable_asan %}
        {# Large code model prevents relocation errors in instrumented ASAN binaries #}
        {% set _ = sanitizer_compiler_flags.append("-mcmodel=large") %}
        {% set _ = relocation_flags.append("-mcmodel=large") %}
    {% elif enable_tsan %}
        {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #}
        {% set _ = sanitizer_compiler_flags.append("-Wno-tsan") %}
        {% if arch == "x86_64" %}
            {# Medium code model for TSAN; large is incompatible #}
            {% set _ = sanitizer_compiler_flags.append("-mcmodel=medium") %}
            {% set _ = relocation_flags.append("-mcmodel=medium") %}
        {% endif %}
    {% endif %}

    {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %}
    {% set _ = sanitizer_compiler_flags.append(fsanitize) %}
    {% set _ = relocation_flags.append(fsanitize) %}

    {% set sanitizer_linker_flags = relocation_flags %}
{% elif compiler == "clang" or compiler == "apple-clang" %}
    {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %}
    {% set _ = sanitizer_compiler_flags.append(fsanitize) %}

    {% set sanitizer_linker_flags = [fsanitize] %}
{% endif %}

[conf]
tools.build:defines+={{ defines }}
tools.build:cxxflags+={{ sanitizer_compiler_flags }}
tools.build:sharedlinkflags+={{ sanitizer_linker_flags }}
tools.build:exelinkflags+={{ sanitizer_linker_flags }}

tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]

# &: means "apply only to the consumer/root package"
&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{ sanitizers }}", "SANITIZERS_COMPILER_FLAGS": "{{ sanitizer_compiler_flags | join(' ') }}", "SANITIZERS_LINKER_FLAGS": "{{ sanitizer_linker_flags | join(' ') }}"}

[options]
{% if enable_asan %}
    # Build Boost.Context with ucontext backend (not fcontext) so that
    # ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
    # are compiled into the library. fcontext (assembly) has no ASAN support.
    # define=BOOST_USE_ASAN=1 is critical: it must be defined when building
    # Boost.Context itself so the ucontext backend compiles in the ASAN annotations.
    boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1
    boost/*:without_context=False
    # Boost stacktrace fails to build with some sanitizers
    boost/*:without_stacktrace=True
{% elif enable_tsan %}
    # Build Boost.Context with ucontext backend for TSAN. fcontext (assembly)
    # has no TSAN annotations, so without this the BOOST_USE_TSAN/BOOST_USE_UCONTEXT
    # defines in [conf] would be ineffective.
    boost/*:extra_b2_flags=context-impl=ucontext thread-sanitizer=on define=BOOST_USE_TSAN=1
    boost/*:without_context=False
    boost/*:without_stacktrace=True
{% endif %}

{% endif %}
