#!/bin/bash # NOSTR Core Library - Customer Build Script with Auto-Detection # Automatically detects which NIPs are needed based on #include statements set -e # Exit on error # Color constants RED='\033[31m' GREEN='\033[32m' YELLOW='\033[33m' BLUE='\033[34m' BOLD='\033[1m' RESET='\033[0m' # Detect if we should use colors (terminal output and not piped) USE_COLORS=true if [ ! -t 1 ] || [ "$NO_COLOR" = "1" ]; then USE_COLORS=false fi # Function to print output with colors print_info() { if [ "$USE_COLORS" = true ]; then echo -e "${BLUE}[INFO]${RESET} $1" else echo "[INFO] $1" fi } print_success() { if [ "$USE_COLORS" = true ]; then echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} $1" else echo "[SUCCESS] $1" fi } print_warning() { if [ "$USE_COLORS" = true ]; then echo -e "${YELLOW}[WARNING]${RESET} $1" else echo "[WARNING] $1" fi } print_error() { if [ "$USE_COLORS" = true ]; then echo -e "${RED}${BOLD}[ERROR]${RESET} $1" else echo "[ERROR] $1" fi } # Default values ARCHITECTURE="" FORCE_NIPS="" VERBOSE=false HELP=false BUILD_TESTS=false NO_COLOR_FLAG=false # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in x64|x86_64|amd64) ARCHITECTURE="x64" shift ;; arm64|aarch64) ARCHITECTURE="arm64" shift ;; --nips=*) FORCE_NIPS="${1#*=}" shift ;; --verbose|-v) VERBOSE=true shift ;; --tests|-t) BUILD_TESTS=true shift ;; --no-color) NO_COLOR_FLAG=true shift ;; --help|-h) HELP=true shift ;; *) print_error "Unknown argument: $1" HELP=true shift ;; esac done # Apply no-color flag if [ "$NO_COLOR_FLAG" = true ]; then USE_COLORS=false fi # Show help if [ "$HELP" = true ]; then echo "NOSTR Core Library - Customer Build Script" echo "" echo "Usage: $0 [architecture] [options]" echo "" echo "Architectures:" echo " x64, x86_64, amd64 Build for x86-64 architecture" echo " arm64, aarch64 Build for ARM64 architecture" echo " (default) Build for current architecture" echo "" echo "Options:" echo " --nips=1,5,6,19 Force specific NIPs (comma-separated)" echo " --nips=all Include all available NIPs" echo " --tests, -t Build all test programs in tests/ directory" echo " --verbose, -v Verbose output" echo " --no-color Disable colored output" echo " --help, -h Show this help" echo "" echo "Auto-Detection:" echo " Script automatically scans your .c files for #include statements" echo " like #include \"nip001.h\" and compiles only needed NIPs." echo "" echo "Available NIPs:" echo " 001 - Basic Protocol (event creation, signing)" echo " 005 - DNS-based identifiers" echo " 006 - Key derivation from mnemonic" echo " 011 - Relay information document" echo " 013 - Proof of Work" echo " 019 - Bech32 encoding (nsec/npub)" echo " 044 - Encryption" echo "" echo "Examples:" echo " $0 # Auto-detect NIPs, build for current arch" echo " $0 x64 # Auto-detect NIPs, build for x64" echo " $0 --nips=1,6,19 # Force NIPs 1,6,19 only" echo " $0 arm64 --nips=all # Build all NIPs for ARM64" echo " $0 -t # Build library and all tests" exit 0 fi print_info "NOSTR Core Library - Customer Build Script" print_info "Auto-detecting needed NIPs from your source code..." ########################################################################################### ########################################################################################### ############ AUTODETECT NIPS FROM SOURCE FILES ########################################################################################### ########################################################################################### NEEDED_NIPS="" if [ -n "$FORCE_NIPS" ]; then if [ "$FORCE_NIPS" = "all" ]; then NEEDED_NIPS="001 004 005 006 011 013 019 044" print_info "Forced: Building all available NIPs" else # Convert comma-separated list to space-separated with 3-digit format NEEDED_NIPS=$(echo "$FORCE_NIPS" | tr ',' ' ' | sed 's/\b\([0-9]\)\b/00\1/g' | sed 's/\b\([0-9][0-9]\)\b/0\1/g') print_info "Forced NIPs: $NEEDED_NIPS" fi else # Auto-detect from .c files in current directory if ls *.c >/dev/null 2>&1; then # Scan for nip*.h includes (with or without nostr_core/ prefix) DETECTED=$(grep -h '#include[[:space:]]*["\<]\(nostr_core/\)\?nip[0-9][0-9][0-9]\.h["\>]' *.c 2>/dev/null | \ sed 's/.*nip0*\([0-9]*\)\.h.*/\1/' | \ sort -u | \ sed 's/\b\([0-9]\)\b/00\1/g' | sed 's/\b\([0-9][0-9]\)\b/0\1/g') # Check for nostr_core.h (includes everything) if grep -q '#include[[:space:]]*["\<]nostr_core\.h["\>]' *.c 2>/dev/null; then print_info "Found #include \"nostr_core.h\" - building all NIPs" NEEDED_NIPS="001 005 006 011 013 019 044" elif [ -n "$DETECTED" ]; then NEEDED_NIPS="$DETECTED" print_success "Auto-detected NIPs: $(echo $NEEDED_NIPS | tr ' ' ',')" else print_warning "No NIP includes detected in *.c files" print_info "Defaulting to basic NIPs: 001, 006, 019" NEEDED_NIPS="001 006 019" fi else print_warning "No .c files found in current directory" print_info "Defaulting to basic NIPs: 001, 006, 019" NEEDED_NIPS="001 006 019" fi fi # If building tests, include all NIPs to ensure test compatibility if [ "$BUILD_TESTS" = true ] && [ -z "$FORCE_NIPS" ]; then NEEDED_NIPS="001 005 006 011 013 019 044" print_info "Building tests - including all available NIPs for test compatibility" fi # Ensure NIP-001 is always included (required for core functionality) if ! echo "$NEEDED_NIPS" | grep -q "001"; then NEEDED_NIPS="001 $NEEDED_NIPS" print_info "Added NIP-001 (required for core functionality)" fi ########################################################################################### ########################################################################################### ############ AUTODETECT SYSTEM ARCHITECTURE ########################################################################################### ########################################################################################### # Determine architecture if [ -z "$ARCHITECTURE" ]; then ARCH=$(uname -m) case $ARCH in x86_64|amd64) ARCHITECTURE="x64" ;; aarch64|arm64) ARCHITECTURE="arm64" ;; *) ARCHITECTURE="x64" # Default fallback print_warning "Unknown architecture '$ARCH', defaulting to x64" ;; esac fi print_info "Target architecture: $ARCHITECTURE" # Set compiler based on architecture case $ARCHITECTURE in x64) CC="gcc" ARCH_SUFFIX="x64" ;; arm64) CC="aarch64-linux-gnu-gcc" ARCH_SUFFIX="arm64" ;; *) print_error "Unsupported architecture: $ARCHITECTURE" exit 1 ;; esac # Check if compiler exists if ! command -v $CC &> /dev/null; then print_error "Compiler $CC not found" if [ "$ARCHITECTURE" = "arm64" ]; then print_info "Install ARM64 cross-compiler: sudo apt install gcc-aarch64-linux-gnu" fi exit 1 fi ########################################################################################### ########################################################################################### ############ ADD CORE DEPENDENCIES THAT NEED TO BE BUILT TO THE $SOURCES VARIABLE ########################################################################################### ########################################################################################### SOURCES="nostr_core/crypto/nostr_secp256k1.c" SOURCES="$SOURCES nostr_core/crypto/nostr_aes.c" SOURCES="$SOURCES nostr_core/crypto/nostr_chacha20.c" SOURCES="$SOURCES cjson/cJSON.c" SOURCES="$SOURCES nostr_core/utils.c" SOURCES="$SOURCES nostr_core/nostr_common.c" # Add secp256k1 library path based on architecture case $ARCHITECTURE in x64) SECP256K1_LIB="secp256k1/.libs/libsecp256k1.a" ;; arm64) SECP256K1_LIB="secp256k1/.libs/libsecp256k1_arm64.a" ;; esac NIP_DESCRIPTIONS="" for nip in $NEEDED_NIPS; do NIP_FILE="nostr_core/nip${nip}.c" if [ -f "$NIP_FILE" ]; then SOURCES="$SOURCES $NIP_FILE" case $nip in 001) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-001(Basic)" ;; 004) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-004(Encrypt)" ;; 005) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-005(DNS)" ;; 006) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-006(Keys)" ;; 011) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-011(Relay-Info)" ;; 013) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-013(PoW)" ;; 019) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-019(Bech32)" ;; 044) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-044(Encrypt)" ;; esac else print_warning "NIP file not found: $NIP_FILE - skipping" fi done # Build flags CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2" CFLAGS="$CFLAGS -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING" INCLUDES="-I. -Inostr_core -Inostr_core/crypto -Icjson -Isecp256k1/include -Inostr_websocket" INCLUDES="$INCLUDES -I./openssl-install/include -I./curl-install/include" # Static libraries STATIC_LIBS="./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a" STATIC_LIBS="$STATIC_LIBS ./curl-install/lib/libcurl.a" # Output library name OUTPUT="libnostr_core_${ARCH_SUFFIX}.a" print_info "Compiling with: $CC" print_info "Including:$NIP_DESCRIPTIONS" if [ "$VERBOSE" = true ]; then print_info "Sources: $SOURCES" print_info "Flags: $CFLAGS $INCLUDES" fi ########################################################################################### ########################################################################################### ############ COMPILE EACH SOURCE FROM $SOURCES INTO A .o FILE ########################################################################################### ########################################################################################### OBJECTS="" for source in $SOURCES; do if [ -f "$source" ]; then obj_name=$(basename "$source" .c).${ARCH_SUFFIX}.o OBJECTS="$OBJECTS $obj_name" if [ "$VERBOSE" = true ]; then print_info "Compiling: $source -> $obj_name" fi ################################################# # THE ACTUAL COMMAND TO COMPILE .c FILES ################################################# $CC $CFLAGS $INCLUDES -c "$source" -o "$obj_name" if [ $? -ne 0 ]; then print_error "Failed to compile $source" exit 1 fi else print_error "Source file not found: $source" exit 1 fi done ########################################################################################### ########################################################################################### ############ CREATE THE FINAL STATIC LIBRARY FOR THE PROJECT: libnostr_core_XX.a ############ BY LINKING IN ALL OUR .o FILES THAT ARE REQUESTED TO BE ADDED. ########################################################################################### ########################################################################################### print_info "Creating self-contained static library: $OUTPUT" # Create temporary directories for extracting objects TMP_SECP256K1=".tmp_secp256k1_$$" TMP_OPENSSL=".tmp_openssl_$$" TMP_CURL=".tmp_curl_$$" mkdir -p "$TMP_SECP256K1" "$TMP_OPENSSL" "$TMP_CURL" # Extract secp256k1 objects if [ -f "$SECP256K1_LIB" ]; then if [ "$VERBOSE" = true ]; then print_info "Extracting secp256k1 objects..." fi (cd "$TMP_SECP256K1" && ar x "../$SECP256K1_LIB") fi # Extract OpenSSL objects if [ "$VERBOSE" = true ]; then print_info "Extracting OpenSSL objects..." fi (cd "$TMP_OPENSSL" && ar x "../openssl-install/lib64/libssl.a") (cd "$TMP_OPENSSL" && ar x "../openssl-install/lib64/libcrypto.a") # Extract curl objects if [ "$VERBOSE" = true ]; then print_info "Extracting curl objects..." fi (cd "$TMP_CURL" && ar x "../curl-install/lib/libcurl.a") # Combine all objects into final library if [ "$VERBOSE" = true ]; then print_info "Combining all objects into self-contained library..." fi ######################################################### ### THE ACTUAL COMMAND TO LINK .o FILES INTO A .a FILE ######################################################### ar rcs "$OUTPUT" $OBJECTS "$TMP_SECP256K1"/*.o "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o AR_RESULT=$? # Cleanup temporary directories rm -rf "$TMP_SECP256K1" "$TMP_OPENSSL" "$TMP_CURL" ########################################################################################### ########################################################################################### ############ IF THE LINKING OCCURED SUCCESSFULLY, BUILD THE TEST FILE EXECUTABLES ############ BY LINKING IN ALL OUR .o FILES THAT ARE REQUESTED TO BE ADDED. ########################################################################################### ########################################################################################### if [ $AR_RESULT -eq 0 ]; then # Cleanup object files rm -f $OBJECTS # Show library info size_kb=$(du -k "$OUTPUT" | cut -f1) print_success "Built $OUTPUT (${size_kb}KB) with:$NIP_DESCRIPTIONS" # Build tests if requested if [ "$BUILD_TESTS" = true ]; then print_info "Scanning tests/ directory for test programs..." if [ ! -d "tests" ]; then print_warning "tests/ directory not found - skipping test builds" else TEST_COUNT=0 SUCCESS_COUNT=0 # Find all .c files in tests/ directory (not subdirectories) while IFS= read -r -d '' test_file; do TEST_COUNT=$((TEST_COUNT + 1)) test_name=$(basename "$test_file" .c) test_exe="tests/$test_name" print_info "Building test: $test_name" # Simple test compilation - everything is in our fat library LINK_FLAGS="-lz -ldl -lpthread -lm -static" if [ "$VERBOSE" = true ]; then print_info " Command: $CC $CFLAGS $INCLUDES \"$test_file\" -o \"$test_exe\" ./$OUTPUT $LINK_FLAGS" fi if $CC $CFLAGS $INCLUDES "$test_file" -o "$test_exe" "./$OUTPUT" $LINK_FLAGS; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) print_success "Built $test_name" if [ "$VERBOSE" = true ]; then print_info " Executable: $test_exe" fi else print_error " Failed to build: $test_name" fi done < <(find tests/ -maxdepth 1 -name "*.c" -type f -print0) if [ $TEST_COUNT -eq 0 ]; then print_warning "No .c files found in tests/ directory" else print_success "Built $SUCCESS_COUNT/$TEST_COUNT test programs" fi fi echo "" fi echo "Usage in your project:" echo " gcc your_app.c $OUTPUT -lz -ldl -lpthread -lm -o your_app" echo "" else print_error "Failed to create static library" exit 1 fi