#!/bin/sh

# This script is taken from https://raw.githubusercontent.com/bytecodealliance/wit-bindgen/refs/tags/v0.45.0/ci/rebuild-libwit-bindgen-cabi.sh
# and adapted for usage during the package build, since the pre-built artifacts
# it generates are removed from the upstream tarball, but this script is not
# shipped in the vendored crate.
#
# See https://github.com/bytecodealliance/wit-bindgen/issues/1288
#
# Diff to upstream's version:
# - adapt paths
# - add versions to called LLVM tools
# - add explicit --target to clang-21 invocations
# - skip stripping, we don't care about Linux/MacOS differences

# This script, and various infrastructure, is a hack to work around the lack of
# stable support in Rust to generate a weak symbol.
#
# The basic problem here is that the Rust `wit-bindgen` crate wants to export
# the `cabi_realloc` symbol from the final binary. This library, however,
# is not stable which means that we're publishing new versions of `wit-bindgen`
# over its development. This means that if `wit-bindgen`-the-crate were to
# export a `#[unsafe(no_mangle)]` symbol of `cabi_realloc` then it wouldn't
# work to have two versions of `wit-bindgen` in the same project. This can
# arise relatively quickly, however, so this is something we want to solve.
#
# The general idea of the solution here is to ensure that the `cabi_realloc`
# symbol itself is declared as a weak symbol. A weakly-defined symbol means
# that if the linker sees multiple copies it can discard all but one. This is
# the semantics we want where some `wit-bindgen` needs to define `cabi_realloc`
# but it doesn't matter much which one.
#
# Stable Rust can't define weak symbols as of the time of this writing. C,
# however, can. Unfortunately users of this crate do not always have a C
# compiler on-hand for wasm, nor do we want to require one. That's where all
# these hacks come into play. With that intro, the purpose of this script is to:
#
# * Generate a `cabi_realloc.rs` file with a "mangled" Rust symbol that's
#   unique per-major-version of the crate.
# * Generate a `cabi_realloc.c` file that defines a weak `cabi_realloc` symbol
#   that calls the above Rust symbol
# * Compile `cabi_realloc.c` into an object and place it into an archive and
#   check that archive into this repo.
#
# This all leads up to the point where we're distributing binary artifacts with
# this crate. These artifacts are verified in CI to ensure what this script
# generates.
#
# Overall this is intended to provide `cabi_realloc` as a weak symbol,
# everything works on stable Rust, and users don't need a C compiler when they
# use this crate.

set -ex

version=$1
version_mangled="$(echo $version | sed -e 's/\./_/g')"

realloc=cabi_realloc_wit_bindgen_${version_mangled}

rm -f vendor/wit-bindgen-$version/src/rt/wit_bindgen_*.{rs,o,c}
rm -f vendor/wit-bindgen-$version/src/rt/libwit_bindgen_cabi.a

cat >vendor/wit-bindgen-$version/src/rt/wit_bindgen_cabi_realloc.rs <<-EOF
// This file is generated by $0

#[unsafe(no_mangle)]
pub unsafe extern "C" fn $realloc(
    old_ptr: *mut u8,
    old_len: usize,
    align: usize,
    new_len: usize,
) -> *mut u8 {
    crate::rt::cabi_realloc(old_ptr, old_len, align, new_len)
}
EOF

cat >vendor/wit-bindgen-$version/src/rt/wit_bindgen_cabi_realloc.c <<-EOF
// This file is generated by $0

#include <stdint.h>

extern void *$realloc(void *ptr, size_t old_size, size_t align, size_t new_size);

__attribute__((__weak__, __export_name__("cabi_realloc")))
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
  return $realloc(ptr, old_size, align, new_size);
}
EOF

cat >vendor/wit-bindgen-$version/src/rt/wit_bindgen_cabi_wasip3.c <<-EOF
// This file is generated by $0

#include <stdlib.h>

static void *WASIP3_TASK = NULL;

__attribute__((__weak__))
void *wasip3_task_set(void *ptr) {
  void *ret = WASIP3_TASK;
  WASIP3_TASK = ptr;
  return ret;
}
EOF

build() {
  file=$1
  target=$2
  clang-21 vendor/wit-bindgen-$version/src/rt/$1.c \
    -O -c -o vendor/wit-bindgen-$version/src/rt/$1.o -mcpu=mvp --target=$target
  # Remove the `producers` section. This appears to differ whether the host for
  # clang is either macOS or Linux. Not needed here anyway, so discard it to help
  # either host produce the same object.
  #strip -d producers vendor/wit-bindgen-$version/src/rt/$1.o \
  #  -o vendor/wit-bindgen-$version/src/rt/$1.o
}

build wit_bindgen_cabi_realloc wasm32-wasip2
build wit_bindgen_cabi_wasip3 wasm32-wasip2

llvm-ar-21 crus vendor/wit-bindgen-$version/src/rt/libwit_bindgen_cabi.a \
  vendor/wit-bindgen-$version/src/rt/wit_bindgen_cabi_realloc.o \
  vendor/wit-bindgen-$version/src/rt/wit_bindgen_cabi_wasip3.o
    
