209 lines
5.3 KiB
Bash
Executable File
209 lines
5.3 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# nt static-only builder (Docker + musl + ccache)
|
|
# Output: ./nt
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; }
|
|
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; }
|
|
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; }
|
|
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl"
|
|
IMAGE_TAG="nt-static-builder:latest"
|
|
CCACHE_DIR_HOST="$SCRIPT_DIR/.ccache"
|
|
BUILD_WORK_DIR="$SCRIPT_DIR/.build-static"
|
|
TARGET_ARCH=""
|
|
|
|
show_usage() {
|
|
echo "nt static-only builder"
|
|
echo ""
|
|
echo "USAGE:"
|
|
echo " $0 [--arch <x86_64|arm64>] [--clean]"
|
|
echo ""
|
|
echo "OPTIONS:"
|
|
echo " --arch <arch> Target architecture: x86_64 or arm64"
|
|
echo " --clean Remove .build-static before build"
|
|
echo " -h, --help Show this help"
|
|
}
|
|
|
|
CLEAN=0
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--arch)
|
|
if [[ -z "${2:-}" ]]; then
|
|
print_error "--arch requires a value: x86_64 or arm64"
|
|
exit 1
|
|
fi
|
|
case "$2" in
|
|
x86_64|arm64)
|
|
TARGET_ARCH="$2"
|
|
;;
|
|
*)
|
|
print_error "Unsupported architecture '$2'"
|
|
print_error "Supported values: x86_64, arm64"
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift 2
|
|
;;
|
|
--clean)
|
|
CLEAN=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_error "Unknown argument '$1'"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
print_error "docker is required"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker info >/dev/null 2>&1; then
|
|
print_error "docker daemon is not running"
|
|
exit 1
|
|
fi
|
|
|
|
HOST_UNAME="$(uname -m)"
|
|
case "$HOST_UNAME" in
|
|
x86_64)
|
|
HOST_ARCH="x86_64"
|
|
;;
|
|
aarch64|arm64)
|
|
HOST_ARCH="arm64"
|
|
;;
|
|
*)
|
|
HOST_ARCH="unknown"
|
|
;;
|
|
esac
|
|
|
|
if [[ -z "$TARGET_ARCH" ]]; then
|
|
if [[ "$HOST_ARCH" == "unknown" ]]; then
|
|
TARGET_ARCH="x86_64"
|
|
print_warning "Unknown host architecture '$HOST_UNAME'; defaulting target arch to x86_64"
|
|
else
|
|
TARGET_ARCH="$HOST_ARCH"
|
|
fi
|
|
fi
|
|
|
|
case "$TARGET_ARCH" in
|
|
x86_64)
|
|
PLATFORM="linux/amd64"
|
|
;;
|
|
arm64)
|
|
PLATFORM="linux/arm64"
|
|
;;
|
|
*)
|
|
print_error "Unsupported target architecture '$TARGET_ARCH'"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [[ "$CLEAN" -eq 1 ]]; then
|
|
print_status "Cleaning $BUILD_WORK_DIR"
|
|
rm -rf "$BUILD_WORK_DIR" 2>/dev/null || true
|
|
docker run --rm \
|
|
--platform "$PLATFORM" \
|
|
-v "$SCRIPT_DIR:/work" \
|
|
alpine:3.19 \
|
|
/bin/sh -lc 'rm -rf /work/.build-static'
|
|
fi
|
|
|
|
mkdir -p "$CCACHE_DIR_HOST" "$BUILD_WORK_DIR"
|
|
|
|
docker run --rm \
|
|
--platform "$PLATFORM" \
|
|
-v "$CCACHE_DIR_HOST:/ccache" \
|
|
alpine:3.19 \
|
|
/bin/sh -lc "chown -R $(id -u):$(id -g) /ccache 2>/dev/null || true; chmod -R u+rwX /ccache 2>/dev/null || true; mkdir -p /ccache/tmp"
|
|
|
|
print_status "Building/updating builder image: $IMAGE_TAG"
|
|
docker build --platform "$PLATFORM" -f "$DOCKERFILE" -t "$IMAGE_TAG" "$SCRIPT_DIR" >/dev/null
|
|
|
|
print_status "Compiling static nt inside container ($PLATFORM) with ccache"
|
|
docker run --rm \
|
|
--platform "$PLATFORM" \
|
|
--user "$(id -u):$(id -g)" \
|
|
-v "$SCRIPT_DIR:/work" \
|
|
-v "$CCACHE_DIR_HOST:/ccache" \
|
|
-w /work \
|
|
-e CCACHE_DIR=/ccache \
|
|
"$IMAGE_TAG" \
|
|
/bin/bash -lc '
|
|
set -euo pipefail
|
|
|
|
mkdir -p .build-static/obj
|
|
|
|
CFLAGS_BASE="-std=c99 -O2 -Wall -Wextra -D_GNU_SOURCE -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"
|
|
INCLUDES="-Iinclude -Iresources/nostr_core_lib -Iresources/nostr_core_lib/nostr_core -Iresources/nostr_core_lib/nostr_websocket -Iresources/nostr_core_lib/cjson -Iresources/tui_ncurses"
|
|
CFLAGS="$CFLAGS_BASE $INCLUDES"
|
|
|
|
mapfile -t SOURCES < <(
|
|
{
|
|
find src -name "*.c" -print
|
|
echo resources/tui_ncurses/tui_ncurses.c
|
|
find resources/nostr_core_lib/nostr_core -name "*.c" -print
|
|
find resources/nostr_core_lib/nostr_core/crypto -name "*.c" -print
|
|
echo resources/nostr_core_lib/nostr_websocket/nostr_websocket_openssl.c
|
|
echo resources/nostr_core_lib/platform/linux.c
|
|
echo resources/nostr_core_lib/cjson/cJSON.c
|
|
} | sort -u
|
|
)
|
|
|
|
for src in "${SOURCES[@]}"; do
|
|
obj=".build-static/obj/${src}.o"
|
|
mkdir -p "$(dirname "$obj")"
|
|
ccache gcc $CFLAGS -c "$src" -o "$obj"
|
|
done
|
|
|
|
CURL_LDFLAGS="$(pkg-config --libs --static libcurl)"
|
|
|
|
OBJS=$(find .build-static/obj -name "*.o" -print | sort)
|
|
gcc -static -o nt $OBJS \
|
|
$CURL_LDFLAGS \
|
|
-lsqlite3 -lncursesw -lsecp256k1 -lssl -lcrypto -lpthread -lm -lz -ldl
|
|
|
|
strip nt || true
|
|
'
|
|
|
|
print_status "Verifying ./nt static linkage"
|
|
if [[ ! -x "$SCRIPT_DIR/nt" ]]; then
|
|
print_error "Build failed: ./nt missing"
|
|
exit 1
|
|
fi
|
|
|
|
FILE_OUTPUT="$(file "$SCRIPT_DIR/nt" 2>&1 || true)"
|
|
LDD_OUTPUT="$(ldd "$SCRIPT_DIR/nt" 2>&1 || true)"
|
|
echo "$FILE_OUTPUT" >&2
|
|
echo "$LDD_OUTPUT" >&2
|
|
|
|
if echo "$LDD_OUTPUT" | grep -Eq "not a dynamic executable|statically linked" || echo "$FILE_OUTPUT" | grep -q "statically linked"; then
|
|
print_success "Static binary ready: $SCRIPT_DIR/nt"
|
|
else
|
|
print_error "./nt does not appear fully static"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "ccache stats"
|
|
docker run --rm \
|
|
--platform "$PLATFORM" \
|
|
-v "$CCACHE_DIR_HOST:/ccache" \
|
|
-e CCACHE_DIR=/ccache \
|
|
"$IMAGE_TAG" /bin/bash -lc 'ccache -s' >&2 || true
|