#!/usr/bin/env bash
# Zig C++ compiler wrapper
#
# Translates GCC/Clang-specific flags that Zig doesn't understand into
# compatible equivalents. Currently handles:
#
#   -march=armv8.4-a+sha3  →  -mcpu=generic+v8.4a+sha3
#       Zig doesn't accept GCC-style -march for AArch64 targets.
#       This is the only -march variant used in the AWS-LC build today
#       (see crypto/fipsmodule/CMakeLists.txt). If additional -march flags
#       are added in the future, corresponding translations may be needed here.
#
#   -Wp,<arg>  →  <arg>
#       Zig doesn't support the -Wp, preprocessor passthrough syntax.
#       The underlying flags (-D, -U, etc.) are passed directly instead.
#       Used by third_party/jitterentropy for -Wp,-U_FORTIFY_SOURCE.

set -euo pipefail

if ! command -v zig &> /dev/null; then
    echo "Error: zig compiler not found in PATH" >&2
    echo "Please install zig from https://ziglang.org/download/" >&2
    exit 1
fi

filtered_args=()
for arg in "$@"; do
    case "$arg" in
        -march=armv8.4-a+sha3)
            filtered_args+=("-mcpu=generic+v8.4a+sha3")
            ;;
        -Wp,*)
            filtered_args+=("${arg#-Wp,}")
            ;;
        *)
            filtered_args+=("$arg")
            ;;
    esac
done

# On macOS we compile with `zig c++` but link with `/usr/bin/c++`, because
# zig's self-hosted MachO linker segfaults (see toolchain.cmake overrides
# in zig.yml). That split means compile-time and link-time libc++ ABIs
# must stay in sync. Starting with LLVM 21, libc++ out-of-lines helpers
# like std::__1::__hash_memory into the dylib; zig versions shipping a
# newer libc++ than the system's /usr/bin/c++ would then emit object
# files referencing symbols the system libc++ can't resolve.
#
# The Apple build of libc++ handles exactly this via a vendor-availability
# mechanism: when _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS is 1 the
# headers define each _LIBCPP_INTRODUCED_IN_LLVM_<N> macro to 0 for
# versions not yet shipped in the platform's dylib, causing the affected
# helpers to be inlined in the header instead of referenced as dylib
# exports. Zig's libc++ defaults this macro to 0, so we force it on for
# Darwin targets. This is a no-op on zig versions whose libc++ LLVM
# version is already covered by the system dylib, and prevents a linker
# failure on any newer zig version whose libc++ is ahead of the runner's.
# -U first to avoid -Wmacro-redefined.
if [[ "$(uname -s)" == "Darwin" ]]; then
    filtered_args=(
        -U_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS
        -D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=1
        "${filtered_args[@]}"
    )
fi

# Merge stderr into stdout and print the invocation on failure so CI logs
# always surface zig's diagnostic even when something in the cmd.exe ->
# bash -> zig chain drops stderr (observed on windows-11-arm previously).
#
# Capture rc via `|| rc=$?` rather than `if ! ...; then rc=$?` because the
# latter makes $? reflect the negated if-test result (0/1) instead of zig's
# real exit status (e.g. 139 for SIGSEGV).
rc=0
zig c++ -fno-sanitize=undefined -fno-sanitize=address -fno-sanitize=memory "${filtered_args[@]}" 2>&1 || rc=$?
if [[ "${rc}" -ne 0 ]]; then
    echo "zig-c++: zig exited with status ${rc}" >&2
    echo "zig-c++: invocation was: zig c++ ${filtered_args[*]}" >&2
    exit "${rc}"
fi
