#!/bin/bash -e
# Build voyem for the container's native platform. Used by the
# Containerfile on podman farm nodes. Always produces a Linux binary.
# Tries a MinGW Windows build for the same architecture; unicorn's
# CMake build may refuse that cross, and that failure is ignored.

OUTPUT_DIR="${OUTPUT_DIR:-/artifacts}"
BIN_DIR="$OUTPUT_DIR/bin"
mkdir -p "$BIN_DIR"

# Windows builds use LLVM MinGW (the *-windows-gnullvm targets). Debian's
# mingw-w64 packages have no aarch64-pc-windows-gnu compiler, and Rust has
# no such target; gnullvm is the one rustup ships.
case "$(uname -m)" in
x86_64)  arch=amd64; win_target=x86_64-pc-windows-gnullvm; win_cc=x86_64-w64-mingw32-clang ;;
aarch64) arch=arm64; win_target=aarch64-pc-windows-gnullvm; win_cc=aarch64-w64-mingw32-clang ;;
*)       echo "container-build: unsupported arch: $(uname -m)" >&2; exit 1 ;;
esac

pkg_version() {
    awk -F'"' '/^version[[:space:]]*=/ { print $2; exit }' Cargo.toml
}

want=$(pkg_version)
if [ -z "$want" ]; then
    echo "container-build: Cargo.toml has no version" >&2
    exit 1
fi
if [ -n "${VERSION:-}" ] && [ "$VERSION" != "$want" ]; then
    echo "container-build: VERSION=$VERSION but Cargo.toml is $want" >&2
    exit 1
fi

echo "container-build: linux/$arch" >&2
cargo build --release --locked
cp target/release/voyem "$BIN_DIR/voyem"

got=$("$BIN_DIR/voyem" --version | awk 'NR==1 { print $2; exit }')
if [ "$got" != "$want" ]; then
    echo "container-build: voyem --version is ${got:-?}, expected $want" >&2
    exit 1
fi

if command -v "$win_cc" >/dev/null 2>&1; then
    echo "container-build: windows/$arch (best-effort)" >&2
    if rustup target add "$win_target"; then
        triple_env=$(printf '%s' "$win_target" | tr 'a-z-' 'A-Z_')
        win_prefix=${win_cc%-clang}
        resource=$("$win_cc" -print-resource-dir)
        # Unicorn's vendored QEMU header calls the x64 _setjmp(env, NULL).
        # LLVM MinGW's aarch64 setjmp is __mingw_setjmp(env). A -I ahead of
        # the crate headers supplies that one-line difference.
        win_ov=$(mktemp -d)
        win_hdr=$(find /usr/local/cargo/registry/src -path '*/unicorn-engine-sys-*/qemu/include/sysemu/os-win32.h' -print -quit)
        mkdir -p "$win_ov/sysemu"
        if ! python3 - "$win_hdr" "$win_ov/sysemu/os-win32.h" <<'PY'
import pathlib, sys
src, dest = sys.argv[1:]
old = "#define setjmp(env) _setjmp(env, NULL)\n"
new = """#if defined(__aarch64__)
#define setjmp(env) __mingw_setjmp(env)
#else
#define setjmp(env) _setjmp(env, NULL)
#endif
"""
text = pathlib.Path(src).read_text()
if old not in text:
    sys.stderr.write("container-build: os-win32.h setjmp block not found\n")
    sys.exit(1)
pathlib.Path(dest).write_text(text.replace(old, new, 1))
PY
        then
            echo "container-build: could not adjust Unicorn's Windows setjmp; skipping Windows" >&2
            exit 0
        fi
        real_cc=$(command -v "$win_cc")
        win_wrap=$(mktemp)
        cat >"$win_wrap" <<EOF
#!/bin/sh
exec "$real_cc" -I"$win_ov" "\$@"
EOF
        chmod +x "$win_wrap"
        # cc-rs reads CC_<target> with the original case. Cargo's linker
        # variable is the uppercased form.
        export "CC_${win_target//-/_}=$win_wrap"
        export "CARGO_TARGET_${triple_env}_LINKER=$real_cc"
        # bindgen shells out to the host libclang, which does not understand
        # the gnullvm triple or the MinGW header layout.
        export "BINDGEN_EXTRA_CLANG_ARGS_${win_target//-/_}=--target=${win_prefix} -resource-dir ${resource} -isystem /opt/llvm-mingw/${win_prefix}/include"
        if cargo build --release --locked --target "$win_target"; then
            cp "target/$win_target/release/voyem.exe" "$BIN_DIR/voyem.exe"
        else
            echo "container-build: Windows $arch build failed; continuing without it" >&2
        fi
    else
        echo "container-build: rustup target add $win_target failed; skipping Windows" >&2
    fi
else
    echo "container-build: $win_cc not found; skipping Windows" >&2
fi
