#!/bin/bash # Build script for uTun (supports both Linux and Windows/MSYS2) # Usage: ./build.sh [options] # Options: # -h, --help Show this help # --clean Clean before build # --full Full rebuild (autoreconf + configure + make) # --asan Build with AddressSanitizer (debug) # --hardened Build with compiler security hardening flags # -j[N] Build with N parallel jobs (default: 4) set -e # Exit on error # Detect platform if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ -n "$MSYSTEM" ]]; then IS_WINDOWS=1 echo "Detected Windows/MSYS2 environment" else IS_WINDOWS=0 echo "Detected Linux environment" fi # Parse arguments CLEAN=0 FULL_REBUILD=0 USE_ASAN=0 USE_HARDENED=0 JOBS="-j4" MAKE_ARGS="" HARDEN_FLAGS="" while [[ $# -gt 0 ]]; do case $1 in -h|--help) echo "Usage: ./build.sh [options]" echo "Options:" echo " -h, --help Show this help" echo " --clean Clean before build" echo " --full Full rebuild (autoreconf + configure + make)" echo " --asan Build with AddressSanitizer" echo " --hardened Build with compiler security hardening flags" echo " -j[N] Build with N parallel jobs (default: 4)" exit 0 ;; --clean) CLEAN=1 shift ;; --full) FULL_REBUILD=1 shift ;; --asan) USE_ASAN=1 shift ;; --hardened) USE_HARDENED=1 shift ;; -j*) JOBS="$1" shift ;; *) MAKE_ARGS="$MAKE_ARGS $1" shift ;; esac done # Check required tools if ! command -v gcc &> /dev/null; then echo "Error: gcc not found." if [ "$IS_WINDOWS" -eq 1 ]; then echo "Please install: pacman -S mingw-w64-ucrt-x86_64-toolchain" else echo "Please install build-essential or gcc" fi exit 1 fi # Clean if requested if [ "$CLEAN" -eq 1 ]; then echo "Cleaning build..." make clean 2>/dev/null || true fi # Full rebuild: regenerate build system if [ "$FULL_REBUILD" -eq 1 ]; then echo "" echo "======================================" echo "FULL REBUILD: autoreconf" echo "======================================" echo "" # Check for autotools if ! command -v autoreconf &> /dev/null; then echo "Error: autoreconf not found." if [ "$IS_WINDOWS" -eq 1 ]; then echo "Install autotools: pacman -S mingw-w64-ucrt-x86_64-autotools" else echo "Install autotools: apt-get install autoconf automake libtool" fi exit 1 fi # Clean generated files echo "Cleaning generated files..." make distclean 2>/dev/null || true rm -rf build/ # Regenerate build system (root Makefile is hand-written, not generated) echo "" echo "Running autoreconf -fi..." autoreconf -fi 2>&1 | tee autoreconf.log echo "" echo "======================================" echo "Build system regenerated successfully" echo "======================================" echo "" else # Quick/incremental build: generate configure if needed if [ ! -f ./configure ]; then echo "Configure script not found. Generating..." if ! command -v autoreconf &> /dev/null; then echo "Error: autoreconf not found." if [ "$IS_WINDOWS" -eq 1 ]; then echo "Install autotools: pacman -S mingw-w64-ucrt-x86_64-autotools" else echo "Install autotools: apt-get install autoconf automake libtool" fi exit 1 fi autoreconf -fiv fi fi # Auto-detect FFmpeg (optional video transcoding). If libav libraries are # available, build with --with-ffmpeg; configure gracefully skips it otherwise. FFMPEG_OPT="" if command -v pkg-config >/dev/null 2>&1 && \ pkg-config --exists libavformat libavcodec libavutil libswscale libswresample 2>/dev/null; then FFMPEG_OPT="--with-ffmpeg" echo "FFmpeg detected — building with video transcoding" else echo "FFmpeg not found — building without video transcoding" fi # Generate Makefiles if needed if [ ! -f Makefile ]; then echo "" echo "Running ./configure..." if [ "$USE_ASAN" -eq 1 ]; then ./configure CFLAGS="-g -Og -fsanitize=address -fno-omit-frame-pointer -fno-pie" LDFLAGS="-no-pie" $FFMPEG_OPT elif [ "$USE_HARDENED" -eq 1 ]; then ./configure --enable-hardening $FFMPEG_OPT else ./configure $FFMPEG_OPT fi fi # Build (all .o files go to build/ directory, binary copied to project root) echo "" echo "Compiling with $JOBS..." BUILD_SUCCESS=0 if [ "$USE_ASAN" -eq 1 ]; then echo "(AddressSanitizer enabled — lib/ and src/ only, skipping tests)" AF="-fsanitize=address -fno-omit-frame-pointer -g -Og" AL="-fsanitize=address" (cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; } (cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; } BUILD_SUCCESS=1 elif [ "$USE_HARDENED" -eq 1 ]; then echo "(Hardening flags enabled)" HF="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -Wformat=2 -Wformat-overflow=2 -fPIE" HL="-Wl,-z,relro -Wl,-z,now -pie" (cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; } (cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; } (cd tests && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "tests build failed"; exit 1; } BUILD_SUCCESS=1 else make $JOBS $MAKE_ARGS 2>&1 | tee build.log if [ ${PIPESTATUS[0]} -eq 0 ]; then BUILD_SUCCESS=1 fi fi if [ $BUILD_SUCCESS -eq 1 ]; then echo "" echo "Build completed successfully!" echo "Binary: utun" # Rebuild chatgui (CMake project) if transport sources changed if [ -f tools/chatgui/build/CMakeCache.txt ] && command -v cmake &> /dev/null; then echo "" echo "======================================" echo "Rebuilding chatgui (CMake)" echo "======================================" (cd tools/chatgui/build && cmake --build . -j4 2>&1) || echo "WARNING: chatgui build failed" fi else echo "" echo "ERROR: Build failed!" echo "Check build.log for details" exit 1 fi