libyams_mobile stopped compiling without anyone noticing because no CI lane built it: every lane forced YAMS_ENABLE_MOBILE_BINDINGS=false, and the "Check mobile ABI surface" step was commented out and pointed at scripts/ci/check_mobile_abi.sh, which was never committed. Add a linux-x86_64-mobile matrix entry that configures with enable-mobile-bindings and enable-mobile-tests, compiles only the yams_mobile and catch2_mobile_abi targets, runs the ABI check, and runs the mobile meson suite. Other lanes keep mobile bindings off. Add scripts/ci/check_mobile_abi.sh. It compiles the public header as C11 and C++20 and takes the address of every YAMS_MOBILE_API function, so a declaration hidden from C (the extern "C" bug fixed in the previous commit) fails. It then checks that the library exports exactly the declared yams_mobile_* functions with unmangled names. This lane proves the Linux x86_64 host build only. It does not show that an Android .so or iOS XCFramework loads on a device. Signed-off-by: trvon <me@trevon.dev>
91 lines
3.6 KiB
Bash
Executable file
91 lines
3.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Verify the libyams_mobile C ABI surface.
|
|
#
|
|
# Usage: scripts/ci/check_mobile_abi.sh <path-to-libyams_mobile.{so,dylib}> [header]
|
|
#
|
|
# Checks, host build only (this says nothing about Android/iOS device loading):
|
|
# 1. The public header compiles as C11 and as C++20, so every declaration is
|
|
# visible to plain-C / FFI consumers (Swift, Kotlin/JNI, Dart FFI).
|
|
# 2. Every function declared with YAMS_MOBILE_API is exported from the shared
|
|
# library with an unmangled C name.
|
|
# 3. The library reports no yams_mobile_* export that the header does not declare.
|
|
set -euo pipefail
|
|
|
|
LIB_PATH="${1:-}"
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
HEADER="${2:-${REPO_ROOT}/include/yams/api/mobile_bindings.h}"
|
|
|
|
if [ -z "$LIB_PATH" ] || [ ! -f "$LIB_PATH" ]; then
|
|
echo "usage: $0 <libyams_mobile shared library> [header]" >&2
|
|
exit 2
|
|
fi
|
|
if [ ! -f "$HEADER" ]; then
|
|
echo "mobile header not found: $HEADER" >&2
|
|
exit 2
|
|
fi
|
|
|
|
WORK_DIR="$(mktemp -d "${RUNNER_TEMP:-${TMPDIR:-/tmp}}/yams-mobile-abi.XXXXXX")"
|
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
|
|
# Declared API: flatten the header and pull the identifier in front of '(' in
|
|
# each `YAMS_MOBILE_API ... ;` declaration.
|
|
tr '\n' ' ' <"$HEADER" |
|
|
grep -oE 'YAMS_MOBILE_API[^;{#]*\(' |
|
|
grep -oE 'yams_mobile_[A-Za-z0-9_]+[[:space:]]*\($' |
|
|
sed -E 's/[[:space:]]*\($//' | sort -u >"$WORK_DIR/declared.txt"
|
|
|
|
declared_count=$(wc -l <"$WORK_DIR/declared.txt" | tr -d ' ')
|
|
if [ "$declared_count" -eq 0 ]; then
|
|
echo "no YAMS_MOBILE_API declarations found in $HEADER" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 1. Header must compile in both languages and declare every symbol in C.
|
|
INCLUDE_DIR="$(cd "$(dirname "$HEADER")/../.." && pwd)"
|
|
{
|
|
echo '#include <yams/api/mobile_bindings.h>'
|
|
echo 'const void* yams_mobile_abi_probe[] = {'
|
|
sed -E 's/^(.*)$/ (const void*)\&\1,/' "$WORK_DIR/declared.txt"
|
|
echo '};'
|
|
} >"$WORK_DIR/probe.c"
|
|
CC_BIN="${CC:-cc}"
|
|
CXX_BIN="${CXX:-c++}"
|
|
"$CC_BIN" -std=c11 -Wall -Werror -I"$INCLUDE_DIR" -c "$WORK_DIR/probe.c" -o "$WORK_DIR/probe_c.o"
|
|
cp "$WORK_DIR/probe.c" "$WORK_DIR/probe.cpp"
|
|
"$CXX_BIN" -std=c++20 -Wall -Werror -I"$INCLUDE_DIR" -c "$WORK_DIR/probe.cpp" \
|
|
-o "$WORK_DIR/probe_cpp.o"
|
|
|
|
# Both translation units must reference the same unmangled names.
|
|
nm -u "$WORK_DIR/probe_c.o" | awk '{print $NF}' | sed -E 's/^_//' | grep '^yams_mobile_' |
|
|
sort -u >"$WORK_DIR/c_refs.txt"
|
|
nm -u "$WORK_DIR/probe_cpp.o" | awk '{print $NF}' | sed -E 's/^_//' | grep '^yams_mobile_' |
|
|
sort -u >"$WORK_DIR/cpp_refs.txt"
|
|
if ! diff -u "$WORK_DIR/c_refs.txt" "$WORK_DIR/cpp_refs.txt"; then
|
|
echo "C and C++ consumers see different linkage for the mobile API (missing extern \"C\"?)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 2/3. Compare against the library's defined dynamic exports.
|
|
case "$(uname -s)" in
|
|
Darwin) nm -gU "$LIB_PATH" | awk '{print $NF}' | sed -E 's/^_//' ;;
|
|
*) nm -D --defined-only "$LIB_PATH" | awk '{print $NF}' ;;
|
|
esac | grep -E '^yams_mobile_' | sort -u >"$WORK_DIR/exported.txt"
|
|
|
|
status=0
|
|
comm -23 "$WORK_DIR/declared.txt" "$WORK_DIR/exported.txt" >"$WORK_DIR/missing.txt"
|
|
if [ -s "$WORK_DIR/missing.txt" ]; then
|
|
echo "declared in $HEADER but not exported by $LIB_PATH:" >&2
|
|
sed 's/^/ /' "$WORK_DIR/missing.txt" >&2
|
|
status=1
|
|
fi
|
|
comm -13 "$WORK_DIR/declared.txt" "$WORK_DIR/exported.txt" >"$WORK_DIR/extra.txt"
|
|
if [ -s "$WORK_DIR/extra.txt" ]; then
|
|
echo "exported by $LIB_PATH but not declared in $HEADER:" >&2
|
|
sed 's/^/ /' "$WORK_DIR/extra.txt" >&2
|
|
status=1
|
|
fi
|
|
|
|
if [ "$status" -eq 0 ]; then
|
|
echo "mobile ABI OK: ${declared_count} functions declared, exported, and C-linkable ($LIB_PATH)"
|
|
fi
|
|
exit "$status"
|