#!/bin/bash # Build fully static MUSL binaries for C-Relay using Alpine Docker # Produces truly portable binaries with zero runtime dependencies set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BUILD_DIR="$SCRIPT_DIR/build" DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl" # Parse command line arguments DEBUG_BUILD=false if [[ "$1" == "--debug" ]]; then DEBUG_BUILD=true echo "==========================================" echo "C-Relay MUSL Static Binary Builder (DEBUG MODE)" echo "==========================================" else echo "==========================================" echo "C-Relay MUSL Static Binary Builder (PRODUCTION MODE)" echo "==========================================" fi echo "Project directory: $SCRIPT_DIR" echo "Build directory: $BUILD_DIR" echo "Debug build: $DEBUG_BUILD" echo "" # Create build directory mkdir -p "$BUILD_DIR" # Check if Docker is available if ! command -v docker &> /dev/null; then echo "ERROR: Docker is not installed or not in PATH" echo "" echo "Docker is required to build MUSL static binaries." echo "Please install Docker:" echo " - Ubuntu/Debian: sudo apt install docker.io" echo " - Or visit: https://docs.docker.com/engine/install/" echo "" exit 1 fi # Check if Docker daemon is running if ! docker info &> /dev/null; then echo "ERROR: Docker daemon is not running or user not in docker group" echo "" echo "Please start Docker and ensure you're in the docker group:" echo " - sudo systemctl start docker" echo " - sudo usermod -aG docker $USER && newgrp docker" echo " - Or start Docker Desktop" echo "" exit 1 fi DOCKER_CMD="docker" echo "✓ Docker is available and running" echo "" # Detect architecture ARCH=$(uname -m) case "$ARCH" in x86_64) PLATFORM="linux/amd64" OUTPUT_NAME="c_relay_static_x86_64" ;; aarch64|arm64) PLATFORM="linux/arm64" OUTPUT_NAME="c_relay_static_arm64" ;; *) echo "WARNING: Unknown architecture: $ARCH" echo "Defaulting to linux/amd64" PLATFORM="linux/amd64" OUTPUT_NAME="c_relay_static_${ARCH}" ;; esac echo "Building for platform: $PLATFORM" echo "Output binary: $OUTPUT_NAME" echo "" # Build the Docker image echo "==========================================" echo "Step 1: Building Alpine Docker image" echo "==========================================" echo "This will:" echo " - Use Alpine Linux (native MUSL)" echo " - Build all dependencies statically" echo " - Compile c-relay with full static linking" echo "" $DOCKER_CMD build \ --platform "$PLATFORM" \ --build-arg DEBUG_BUILD=$DEBUG_BUILD \ -f "$DOCKERFILE" \ -t c-relay-musl-builder:latest \ --progress=plain \ . || { echo "" echo "ERROR: Docker build failed" echo "Check the output above for details" exit 1 } echo "" echo "✓ Docker image built successfully" echo "" # Extract the binary from the container echo "==========================================" echo "Step 2: Extracting static binary" echo "==========================================" # Build the builder stage to extract the binary $DOCKER_CMD build \ --platform "$PLATFORM" \ --build-arg DEBUG_BUILD=$DEBUG_BUILD \ --target builder \ -f "$DOCKERFILE" \ -t c-relay-static-builder-stage:latest \ . > /dev/null 2>&1 # Create a temporary container to copy the binary CONTAINER_ID=$($DOCKER_CMD create c-relay-static-builder-stage:latest) # Copy binary from container $DOCKER_CMD cp "$CONTAINER_ID:/build/c_relay_static" "$BUILD_DIR/$OUTPUT_NAME" || { echo "ERROR: Failed to extract binary from container" $DOCKER_CMD rm "$CONTAINER_ID" 2>/dev/null exit 1 } # Clean up container $DOCKER_CMD rm "$CONTAINER_ID" > /dev/null echo "✓ Binary extracted to: $BUILD_DIR/$OUTPUT_NAME" echo "" # Make binary executable chmod +x "$BUILD_DIR/$OUTPUT_NAME" # Verify the binary echo "==========================================" echo "Step 3: Verifying static binary" echo "==========================================" echo "" echo "Checking for dynamic dependencies:" if LDD_OUTPUT=$(timeout 5 ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1); then if echo "$LDD_OUTPUT" | grep -q "not a dynamic executable"; then echo "✓ Binary is fully static (no dynamic dependencies)" TRULY_STATIC=true elif echo "$LDD_OUTPUT" | grep -q "statically linked"; then echo "✓ Binary is statically linked" TRULY_STATIC=true else echo "⚠ WARNING: Binary may have dynamic dependencies:" echo "$LDD_OUTPUT" TRULY_STATIC=false fi else # ldd failed or timed out - check with file command instead if file "$BUILD_DIR/$OUTPUT_NAME" | grep -q "statically linked"; then echo "✓ Binary is statically linked (verified with file command)" TRULY_STATIC=true else echo "⚠ Could not verify static linking (ldd check failed)" TRULY_STATIC=false fi fi echo "" echo "File size: $(ls -lh "$BUILD_DIR/$OUTPUT_NAME" | awk '{print $5}')" echo "" # Test if binary runs echo "Testing binary execution:" if "$BUILD_DIR/$OUTPUT_NAME" --version 2>&1 | head -5; then echo "✓ Binary executes successfully" else echo "⚠ Binary execution test failed (this may be normal if --version is not supported)" fi echo "" # Summary echo "==========================================" echo "Build Summary" echo "==========================================" echo "Binary: $BUILD_DIR/$OUTPUT_NAME" echo "Size: $(du -h "$BUILD_DIR/$OUTPUT_NAME" | cut -f1)" echo "Platform: $PLATFORM" if [ "$DEBUG_BUILD" = true ]; then echo "Build Type: DEBUG (with symbols, no optimization)" else echo "Build Type: PRODUCTION (optimized, stripped)" fi if [ "$TRULY_STATIC" = true ]; then echo "Linkage: Fully static binary (Alpine MUSL-based)" echo "Portability: Works on ANY Linux distribution" else echo "Linkage: Static binary (may have minimal dependencies)" fi echo "" echo "✓ Build complete!" echo ""