138 lines
4.1 KiB
Bash
138 lines
4.1 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
#
|
||
|
|
# Build libtcod-headless with sanitizer instrumentation.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# tools/build_debug_libs.sh # Build with debug symbols only
|
||
|
|
# tools/build_debug_libs.sh --asan # Build with AddressSanitizer
|
||
|
|
# tools/build_debug_libs.sh --tsan # Build with ThreadSanitizer
|
||
|
|
# tools/build_debug_libs.sh --clean # Remove build artifacts first
|
||
|
|
#
|
||
|
|
# Output: __lib_debug/libtcod.so (instrumented)
|
||
|
|
#
|
||
|
|
# Why: The pre-built libtcod in __lib/ is uninstrumented. ASan/TSan can
|
||
|
|
# detect bugs in our code that corrupt libtcod's memory, but cannot detect
|
||
|
|
# bugs originating inside libtcod (FOV, pathfinding) that touch our data.
|
||
|
|
# Building libtcod with the same sanitizer flags closes this gap.
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
|
|
LIBTCOD_DIR="$PROJECT_ROOT/modules/libtcod-headless"
|
||
|
|
OUTPUT_DIR="$PROJECT_ROOT/__lib_debug"
|
||
|
|
JOBS="$(nproc 2>/dev/null || echo 4)"
|
||
|
|
|
||
|
|
MODE="debug"
|
||
|
|
CLEAN=0
|
||
|
|
for arg in "$@"; do
|
||
|
|
case "$arg" in
|
||
|
|
--asan) MODE="asan" ;;
|
||
|
|
--tsan) MODE="tsan" ;;
|
||
|
|
--clean) CLEAN=1 ;;
|
||
|
|
--help|-h)
|
||
|
|
echo "Usage: $0 [--asan|--tsan] [--clean]"
|
||
|
|
echo ""
|
||
|
|
echo "Flags:"
|
||
|
|
echo " (default) Debug symbols only (-g -O1)"
|
||
|
|
echo " --asan AddressSanitizer + UBSan instrumentation"
|
||
|
|
echo " --tsan ThreadSanitizer instrumentation"
|
||
|
|
echo " --clean Remove build artifacts before building"
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo "Unknown argument: $arg"
|
||
|
|
echo "Run '$0 --help' for usage."
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
BUILD_DIR="$LIBTCOD_DIR/build-debug-$MODE"
|
||
|
|
|
||
|
|
case "$MODE" in
|
||
|
|
debug)
|
||
|
|
SANITIZER_FLAGS=""
|
||
|
|
echo "=== Building libtcod-headless with debug symbols ==="
|
||
|
|
;;
|
||
|
|
asan)
|
||
|
|
SANITIZER_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"
|
||
|
|
echo "=== Building libtcod-headless with ASan + UBSan ==="
|
||
|
|
;;
|
||
|
|
tsan)
|
||
|
|
SANITIZER_FLAGS="-fsanitize=thread"
|
||
|
|
echo "=== Building libtcod-headless with TSan ==="
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
|
||
|
|
if [ "$CLEAN" -eq 1 ]; then
|
||
|
|
echo "Cleaning $BUILD_DIR..."
|
||
|
|
rm -rf "$BUILD_DIR"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ ! -f "$LIBTCOD_DIR/CMakeLists.txt" ]; then
|
||
|
|
echo "ERROR: libtcod-headless not found at $LIBTCOD_DIR/CMakeLists.txt"
|
||
|
|
echo "Make sure the submodule is initialized:"
|
||
|
|
echo " git submodule update --init modules/libtcod-headless"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
mkdir -p "$BUILD_DIR"
|
||
|
|
cd "$BUILD_DIR"
|
||
|
|
|
||
|
|
CMAKE_EXTRA_FLAGS=""
|
||
|
|
if [ -n "$SANITIZER_FLAGS" ]; then
|
||
|
|
CMAKE_EXTRA_FLAGS="-DCMAKE_C_FLAGS=$SANITIZER_FLAGS -DCMAKE_CXX_FLAGS=$SANITIZER_FLAGS"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ ! -f Makefile ]; then
|
||
|
|
echo "Configuring libtcod-headless ($MODE)..."
|
||
|
|
# shellcheck disable=SC2086
|
||
|
|
cmake "$LIBTCOD_DIR" \
|
||
|
|
-DCMAKE_BUILD_TYPE=Debug \
|
||
|
|
-DBUILD_SHARED_LIBS=ON \
|
||
|
|
$CMAKE_EXTRA_FLAGS
|
||
|
|
echo "Configuration complete."
|
||
|
|
else
|
||
|
|
echo "Makefile exists, skipping configure (use --clean to reconfigure)."
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Building libtcod-headless (-j$JOBS)..."
|
||
|
|
make -j"$JOBS"
|
||
|
|
|
||
|
|
BUILT_LIB=""
|
||
|
|
for candidate in "$BUILD_DIR/libtcod.so" "$BUILD_DIR/libtcod"*.so*; do
|
||
|
|
if [ -f "$candidate" ] && [ ! -L "$candidate" ]; then
|
||
|
|
BUILT_LIB="$candidate"
|
||
|
|
break
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
if [ -z "$BUILT_LIB" ]; then
|
||
|
|
echo "ERROR: Could not find built libtcod.so in $BUILD_DIR"
|
||
|
|
echo "Contents:"
|
||
|
|
ls -la "$BUILD_DIR"/libtcod* 2>/dev/null || echo " (no libtcod files found)"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
mkdir -p "$OUTPUT_DIR"
|
||
|
|
|
||
|
|
BASENAME="$(basename "$BUILT_LIB")"
|
||
|
|
echo "Copying $BUILT_LIB -> $OUTPUT_DIR/$BASENAME"
|
||
|
|
cp "$BUILT_LIB" "$OUTPUT_DIR/$BASENAME"
|
||
|
|
|
||
|
|
cd "$OUTPUT_DIR"
|
||
|
|
for link_name in libtcod.so libtcod.so.2 libtcod.so.2.2; do
|
||
|
|
if [ "$link_name" != "$BASENAME" ]; then
|
||
|
|
ln -sf "$BASENAME" "$link_name"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "=== Instrumented libtcod build complete ==="
|
||
|
|
echo " Mode: $MODE"
|
||
|
|
echo " Library: $OUTPUT_DIR/$BASENAME"
|
||
|
|
echo " Size: $(du -h "$OUTPUT_DIR/$BASENAME" | cut -f1)"
|
||
|
|
echo ""
|
||
|
|
echo "The existing 'make asan' / 'make tsan' targets link __lib_debug/ first,"
|
||
|
|
echo "so the instrumented libtcod will be picked up automatically."
|