#!/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 " 004 - Encryption (legacy)" 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 (modern)" 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" # Check if we're running from the correct directory CURRENT_DIR=$(basename "$(pwd)") if [ "$CURRENT_DIR" != "nostr_core_lib" ]; then print_error "Build script must be run from the nostr_core_lib directory" echo "" echo "Current directory: $CURRENT_DIR" echo "Expected directory: nostr_core_lib" echo "" echo "Please change to the nostr_core_lib directory first, then run the build script." echo "" echo "Correct usage:" echo " cd nostr_core_lib" echo " ./build.sh" echo "" echo "Or if nostr_core_lib is in your project directory:" echo " cd nostr_core_lib" echo " ./build.sh" echo " cd .." echo " gcc your_app.c nostr_core_lib/libnostr_core_x64.a -lz -ldl -lpthread -lm -o your_app" echo "" exit 1 fi 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 004 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 004 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 ########################################################################################### ########################################################################################### ############ CHECK AND BUILD DEPENDENCIES BASED ON NEEDED NIPS ########################################################################################### ########################################################################################### print_info "Checking dependencies based on needed NIPs..." # Set 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 # Determine which dependencies are needed based on NIPs NEED_SECP256K1=false NEED_OPENSSL=false NEED_CURL=false # secp256k1 is always needed (core cryptography for NIP-001) NEED_SECP256K1=true # Check if network-dependent NIPs are included NETWORK_NIPS="005 011" # NIP-005 (DNS), NIP-011 (Relay info) for nip in $NEEDED_NIPS; do case $nip in 005|011) NEED_CURL=true print_info "NIP-$nip requires HTTP functionality - curl needed" ;; esac done # Check if WebSocket functionality is needed (always included currently) # Since nostr_websocket/nostr_websocket_openssl.c is always compiled NEED_OPENSSL=true NEED_CURL=true print_info "WebSocket functionality enabled - OpenSSL and curl needed" if [ "$VERBOSE" = true ]; then print_info "Dependency requirements:" [ "$NEED_SECP256K1" = true ] && echo " ✓ secp256k1 (core crypto)" [ "$NEED_OPENSSL" = true ] && echo " ✓ OpenSSL (TLS/WebSocket)" [ "$NEED_CURL" = true ] && echo " ✓ curl (HTTP requests)" fi # Function to build secp256k1 if needed and missing build_secp256k1() { if [ "$NEED_SECP256K1" != true ]; then return 0 fi if [ -f "$SECP256K1_LIB" ]; then if [ "$VERBOSE" = true ]; then print_success "secp256k1 already available" fi return 0 fi print_info "Building secp256k1..." if [ ! -d "secp256k1" ]; then print_error "secp256k1 source directory not found" exit 1 fi # Force clean build if .libs exists but no library if [ -d "secp256k1/.libs" ] && [ ! -f "$SECP256K1_LIB" ]; then print_info "Cleaning previous secp256k1 build..." (cd secp256k1 && make clean) || true fi (cd secp256k1 && \ ./autogen.sh && \ ./configure --enable-static --disable-shared --enable-module-recovery && \ make clean && \ make -j$(nproc)) || { print_error "Failed to build secp256k1"; exit 1; } # Verify the library was actually created if [ ! -f "$SECP256K1_LIB" ]; then print_error "secp256k1 library not created: $SECP256K1_LIB" print_error "Check if secp256k1 source files are present" if [ "$VERBOSE" = true ]; then print_info "Checking secp256k1 directory contents:" ls -la secp256k1/.libs/ 2>/dev/null || print_warning "No .libs directory found" ls -la secp256k1/src/ 2>/dev/null || print_warning "No src directory found" fi exit 1 fi print_success "secp256k1 built successfully" } # Function to build OpenSSL if needed and missing build_openssl() { if [ "$NEED_OPENSSL" != true ]; then return 0 fi if [ -f "openssl-install/lib64/libssl.a" ] && [ -f "openssl-install/lib64/libcrypto.a" ]; then if [ "$VERBOSE" = true ]; then print_success "OpenSSL already available" fi return 0 fi print_info "Building OpenSSL (this may take 5-10 minutes)..." if [ ! -d "openssl-3.4.2" ]; then print_error "openssl-3.4.2 source directory not found" exit 1 fi (cd openssl-3.4.2 && \ ./Configure linux-x86_64 no-shared --prefix="$(pwd)/../openssl-install" && \ make -j$(nproc) && \ make install) || { print_error "Failed to build OpenSSL"; exit 1; } print_success "OpenSSL built successfully" } # Function to build curl if needed and missing build_curl() { if [ "$NEED_CURL" != true ]; then return 0 fi if [ -f "curl-install/lib/libcurl.a" ]; then if [ "$VERBOSE" = true ]; then print_success "curl already available" fi return 0 fi print_info "Building curl..." if [ ! -d "curl-8.15.0/curl-8.15.0" ]; then print_error "curl-8.15.0/curl-8.15.0 source directory not found" exit 1 fi (cd curl-8.15.0/curl-8.15.0 && \ ./configure --disable-shared --enable-static \ --with-openssl="$(pwd)/../../openssl-install" \ --without-libpsl --without-brotli \ --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy \ --disable-dict --disable-telnet --disable-tftp --disable-pop3 \ --disable-imap --disable-smb --disable-smtp --disable-gopher \ --disable-manual \ --prefix="$(pwd)/../../curl-install" && \ make -j$(nproc) && \ make install) || { print_error "Failed to build curl"; exit 1; } print_success "curl built successfully" } # Build only the needed dependencies build_secp256k1 build_openssl build_curl ########################################################################################### ########################################################################################### ############ 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" SOURCES="$SOURCES nostr_core/core_relays.c" SOURCES="$SOURCES nostr_websocket/nostr_websocket_openssl.c" 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" # Store the build directory to ensure correct paths when extracting from subdirectories BUILD_DIR=$(pwd) # 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 library exists) SECP256K1_OBJECTS="" if [ -f "$SECP256K1_LIB" ]; then if [ "$VERBOSE" = true ]; then print_info "Extracting secp256k1 objects..." fi (cd "$TMP_SECP256K1" && ar x "$BUILD_DIR/$SECP256K1_LIB") SECP256K1_OBJECTS="$TMP_SECP256K1/*.o" else print_warning "secp256k1 library not found: $SECP256K1_LIB - skipping secp256k1 objects" fi # Extract OpenSSL objects if [ "$VERBOSE" = true ]; then print_info "Extracting OpenSSL objects..." fi (cd "$TMP_OPENSSL" && ar x "$BUILD_DIR/openssl-install/lib64/libssl.a") (cd "$TMP_OPENSSL" && ar x "$BUILD_DIR/openssl-install/lib64/libcrypto.a") # Extract curl objects if [ "$VERBOSE" = true ]; then print_info "Extracting curl objects..." fi (cd "$TMP_CURL" && ar x "$BUILD_DIR/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 ######################################################### if [ -n "$SECP256K1_OBJECTS" ]; then ar rcs "$OUTPUT" $OBJECTS $SECP256K1_OBJECTS "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o else ar rcs "$OUTPUT" $OBJECTS "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o fi 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