#!/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 BUILD_EXAMPLES=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 ;; --examples|-e) BUILD_EXAMPLES=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 " --examples, -e Build all example programs in examples/ 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 " 017 - Private Direct Messages" echo " 019 - Bech32 encoding (nsec/npub)" echo " 042 - Authentication of clients to relays" echo " 044 - Encryption (modern)" echo " 059 - Gift Wrap" 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 -lssl -lcrypto -lcurl -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 017 019 042 044 059" 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 042 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 or examples, include all NIPs to ensure compatibility if ([ "$BUILD_TESTS" = true ] || [ "$BUILD_EXAMPLES" = true ]) && [ -z "$FORCE_NIPS" ]; then NEEDED_NIPS="001 004 005 006 011 013 017 019 042 044 059" print_info "Building tests/examples - including all available NIPs for 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..." # 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 # WebSocket functionality is always enabled (using system OpenSSL) NEED_OPENSSL=true NEED_CURL=true print_info "WebSocket functionality enabled - system OpenSSL and curl required" 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 detect system secp256k1 detect_system_secp256k1() { if [ "$NEED_SECP256K1" != true ]; then return 0 fi print_info "Detecting system secp256k1..." # Try pkg-config first if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists libsecp256k1; then SECP256K1_CFLAGS=$(pkg-config --cflags libsecp256k1) SECP256K1_LIBS=$(pkg-config --libs libsecp256k1) if [ "$VERBOSE" = true ]; then print_success "Found secp256k1 via pkg-config" print_info " CFLAGS: $SECP256K1_CFLAGS" print_info " LIBS: $SECP256K1_LIBS" fi return 0 fi # Fallback to standard locations SECP256K1_CFLAGS="" SECP256K1_LIBS="-lsecp256k1" # Check common header locations for header_path in /usr/include/secp256k1.h /usr/local/include/secp256k1.h; do if [ -f "$header_path" ]; then if [ "$header_path" != "/usr/include/secp256k1.h" ]; then SECP256K1_CFLAGS="-I$(dirname $header_path)" fi if [ "$VERBOSE" = true ]; then print_success "Found secp256k1 headers at: $header_path" fi break fi done # Check if we can find the libraries if ! echo '#include ' | gcc $SECP256K1_CFLAGS -E - >/dev/null 2>&1; then print_error "secp256k1 development headers not found" print_info "Install with: sudo apt install libsecp256k1-dev (Ubuntu/Debian)" print_info " sudo yum install libsecp256k1-devel (CentOS/RHEL)" print_info " brew install secp256k1 (macOS)" exit 1 fi print_success "System secp256k1 detected" } # Function to detect system OpenSSL detect_system_openssl() { if [ "$NEED_OPENSSL" != true ]; then return 0 fi print_info "Detecting system OpenSSL..." # Try pkg-config first if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists openssl; then OPENSSL_CFLAGS=$(pkg-config --cflags openssl) OPENSSL_LIBS=$(pkg-config --libs openssl) if [ "$VERBOSE" = true ]; then print_success "Found OpenSSL via pkg-config" print_info " CFLAGS: $OPENSSL_CFLAGS" print_info " LIBS: $OPENSSL_LIBS" fi return 0 fi # Fallback to standard locations OPENSSL_CFLAGS="" OPENSSL_LIBS="-lssl -lcrypto" # Check common header locations for header_path in /usr/include/openssl /usr/local/include/openssl; do if [ -f "$header_path/ssl.h" ]; then if [ "$header_path" != "/usr/include/openssl" ]; then OPENSSL_CFLAGS="-I$(dirname $header_path)" fi if [ "$VERBOSE" = true ]; then print_success "Found OpenSSL headers at: $header_path" fi break fi done # Check if we can find the libraries if ! echo '#include ' | gcc $OPENSSL_CFLAGS -E - >/dev/null 2>&1; then print_error "OpenSSL development headers not found" print_info "Install with: sudo apt install libssl-dev (Ubuntu/Debian)" print_info " sudo yum install openssl-devel (CentOS/RHEL)" print_info " brew install openssl (macOS)" exit 1 fi print_success "System OpenSSL detected" } # Function to detect system curl detect_system_curl() { if [ "$NEED_CURL" != true ]; then return 0 fi print_info "Detecting system curl..." # Try pkg-config first if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists libcurl; then CURL_CFLAGS=$(pkg-config --cflags libcurl) CURL_LIBS=$(pkg-config --libs libcurl) if [ "$VERBOSE" = true ]; then print_success "Found curl via pkg-config" print_info " CFLAGS: $CURL_CFLAGS" print_info " LIBS: $CURL_LIBS" fi return 0 fi # Fallback to standard locations CURL_CFLAGS="" CURL_LIBS="-lcurl" # Check common header locations for header_path in /usr/include/curl /usr/local/include/curl; do if [ -f "$header_path/curl.h" ]; then if [ "$header_path" != "/usr/include/curl" ]; then CURL_CFLAGS="-I$(dirname $header_path)" fi if [ "$VERBOSE" = true ]; then print_success "Found curl headers at: $header_path" fi break fi done # Check if we can find the libraries if ! echo '#include ' | gcc $CURL_CFLAGS -E - >/dev/null 2>&1; then print_error "curl development headers not found" print_info "Install with: sudo apt install libcurl4-openssl-dev (Ubuntu/Debian)" print_info " sudo yum install libcurl-devel (CentOS/RHEL)" print_info " brew install curl (macOS)" exit 1 fi print_success "System curl detected" } # Build only the needed dependencies detect_system_secp256k1 detect_system_openssl detect_system_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_core/core_relay_pool.c" SOURCES="$SOURCES nostr_websocket/nostr_websocket_openssl.c" SOURCES="$SOURCES nostr_core/request_validator.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)" ;; 017) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-017(DMs)" ;; 019) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-019(Bech32)" ;; 042) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-042(Auth)" ;; 044) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-044(Encrypt)" ;; 059) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-059(Gift-Wrap)" ;; 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 -Inostr_websocket" # Add system library includes if [ -n "$SECP256K1_CFLAGS" ]; then INCLUDES="$INCLUDES $SECP256K1_CFLAGS" fi if [ -n "$OPENSSL_CFLAGS" ]; then INCLUDES="$INCLUDES $OPENSSL_CFLAGS" fi if [ -n "$CURL_CFLAGS" ]; then INCLUDES="$INCLUDES $CURL_CFLAGS" fi # System libraries SYSTEM_LIBS="" if [ -n "$SECP256K1_LIBS" ]; then SYSTEM_LIBS="$SYSTEM_LIBS $SECP256K1_LIBS" fi if [ -n "$OPENSSL_LIBS" ]; then SYSTEM_LIBS="$SYSTEM_LIBS $OPENSSL_LIBS" fi if [ -n "$CURL_LIBS" ]; then SYSTEM_LIBS="$SYSTEM_LIBS $CURL_LIBS" fi # 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 static library with system dependencies: $OUTPUT" # Note: All crypto libraries (secp256k1, OpenSSL, curl) are now system dependencies # Only our own object files are included in the static library ######################################################### ### THE ACTUAL COMMAND TO LINK .o FILES INTO A .a FILE ######################################################### ar rcs "$OUTPUT" $OBJECTS AR_RESULT=$? ########################################################################################### ########################################################################################### ############ 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" # Test compilation with system libraries LINK_FLAGS="-lz -ldl -lpthread -lm $SYSTEM_LIBS" 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 # Build examples if requested if [ "$BUILD_EXAMPLES" = true ]; then print_info "Scanning examples/ directory for example programs..." if [ ! -d "examples" ]; then print_warning "examples/ directory not found - skipping example builds" else EXAMPLE_COUNT=0 SUCCESS_COUNT=0 # Find all .c files in examples/ directory (not subdirectories) while IFS= read -r -d '' example_file; do EXAMPLE_COUNT=$((EXAMPLE_COUNT + 1)) example_name=$(basename "$example_file" .c) example_exe="examples/$example_name" print_info "Building example: $example_name" # Example compilation with system libraries LINK_FLAGS="-lz -ldl -lpthread -lm $SYSTEM_LIBS" if [ "$VERBOSE" = true ]; then print_info " Command: $CC $CFLAGS $INCLUDES \"$example_file\" -o \"$example_exe\" ./$OUTPUT $LINK_FLAGS" fi if $CC $CFLAGS $INCLUDES "$example_file" -o "$example_exe" "./$OUTPUT" $LINK_FLAGS; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) print_success "Built $example_name" if [ "$VERBOSE" = true ]; then print_info " Executable: $example_exe" fi else print_error " Failed to build: $example_name" fi done < <(find examples/ -maxdepth 1 -name "*.c" -type f -print0) if [ $EXAMPLE_COUNT -eq 0 ]; then print_warning "No .c files found in examples/ directory" else print_success "Built $SUCCESS_COUNT/$EXAMPLE_COUNT example programs" fi fi echo "" fi echo "Usage in your project:" echo " gcc your_app.c $OUTPUT -lz -ldl -lpthread -lm $SYSTEM_LIBS -o your_app" echo "" else print_error "Failed to create static library" exit 1 fi