Instead of blocking the entire UI with "Activating best chain..." until the daemon finishes warmup, treat warmup responses as a successful connection. The wallet now: - Sets connected=true + warming_up=true when daemon returns RPC -28 - Shows warmup status with block progress in the loading overlay - Polls getinfo every few seconds to detect warmup completion - Allows Console, Peers, Settings tabs during warmup - Shows orange status indicator with warmup message in status bar - Skips balance/tx/address refresh until warmup completes - Triggers full data refresh once daemon is ready Also: fix curl handle/header leak on reconnect, fill in empty externalDetected error branch, bump version to v1.2.0 in build scripts.
1144 lines
52 KiB
Bash
Executable File
1144 lines
52 KiB
Bash
Executable File
#!/bin/bash
|
|
# DragonX ImGui Wallet - Unified Build Script
|
|
# Copyright 2024-2026 The Hush Developers
|
|
# Released under the GPLv3
|
|
#
|
|
# Usage:
|
|
# ./build.sh # Dev build (Linux, debug-friendly)
|
|
# ./build.sh --linux-release # Linux release (zip + AppImage)
|
|
# ./build.sh --win-release # Windows cross-compile (mingw-w64)
|
|
# ./build.sh --mac-release # macOS .app bundle + DMG
|
|
# ./build.sh --linux-release --win-release # Multiple targets
|
|
# ./build.sh --clean --win-release # Clean first, then build
|
|
#
|
|
# Prerequisites:
|
|
# Linux: cmake, g++, libsdl3-dev (or fetched), libsodium-dev
|
|
# Windows: mingw-w64 (posix threads), cmake
|
|
# macOS (native): Xcode CLT, cmake, create-dmg (brew install create-dmg)
|
|
# macOS (cross from Linux): osxcross + macOS SDK, genisoimage, icnsutils
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
VERSION="1.2.0"
|
|
|
|
# ── Colours ──────────────────────────────────────────────────────────────────
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${GREEN}[*]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
err() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
header(){ echo -e "\n${CYAN}══════════════════════════════════════════════════════════${NC}"; echo -e "${CYAN} $1${NC}"; echo -e "${CYAN}══════════════════════════════════════════════════════════${NC}"; }
|
|
|
|
JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
|
|
|
# ── Defaults ─────────────────────────────────────────────────────────────────
|
|
DO_DEV=false
|
|
DO_LINUX=false
|
|
DO_WIN=false
|
|
DO_MAC=false
|
|
CLEAN=false
|
|
BUILD_TYPE="Release"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
DragonX Wallet — Unified Build Script
|
|
|
|
Usage: $0 [options]
|
|
|
|
Targets (at least one required, or none for dev build):
|
|
--linux-release Linux release (zip + AppImage) -> release/linux/
|
|
--win-release Windows cross-compile (mingw-w64) -> release/windows/
|
|
--mac-release macOS .app bundle + DMG -> release/mac/
|
|
|
|
Build trees are stored under build/{linux,windows,mac}/
|
|
|
|
Options:
|
|
-c, --clean Remove build artifacts before building
|
|
-d, --debug Use Debug build type instead of Release
|
|
-j N Parallel jobs (default: $JOBS)
|
|
-h, --help Show this help
|
|
|
|
Cross-compiling from Linux:
|
|
Windows: sudo apt install mingw-w64
|
|
macOS: Requires osxcross (https://github.com/tpoechtrager/osxcross)
|
|
export OSXCROSS=/path/to/osxcross
|
|
sudo apt install genisoimage icnsutils # DMG + icon tools
|
|
|
|
Examples:
|
|
$0 # Quick dev build (Linux)
|
|
$0 --linux-release # Linux release (zip + AppImage)
|
|
$0 --win-release # Windows cross-compile
|
|
$0 --mac-release # macOS bundle + DMG (native or osxcross)
|
|
$0 --clean --linux-release --win-release # Clean + both
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# ── Parse args ───────────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--linux-release) DO_LINUX=true; shift ;;
|
|
--win-release) DO_WIN=true; shift ;;
|
|
--mac-release) DO_MAC=true; shift ;;
|
|
-c|--clean) CLEAN=true; shift ;;
|
|
-d|--debug) BUILD_TYPE="Debug"; shift ;;
|
|
-j) JOBS="$2"; shift 2 ;;
|
|
-h|--help) usage ;;
|
|
*) err "Unknown option: $1"; usage ;;
|
|
esac
|
|
done
|
|
|
|
# No release target → dev build (native, no packaging)
|
|
if ! $DO_LINUX && ! $DO_WIN && ! $DO_MAC; then
|
|
DO_DEV=true
|
|
fi
|
|
|
|
# ── Helper: find resource files ──────────────────────────────────────────────
|
|
find_sapling_params() {
|
|
local dirs=(
|
|
"$HOME/.zcash-params"
|
|
"$HOME/.hush-params"
|
|
"$SCRIPT_DIR/../SilentDragonX"
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-win"
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-linux"
|
|
"$SCRIPT_DIR"
|
|
)
|
|
for d in "${dirs[@]}"; do
|
|
if [[ -f "$d/sapling-spend.params" && -f "$d/sapling-output.params" ]]; then
|
|
SAPLING_SPEND="$d/sapling-spend.params"
|
|
SAPLING_OUTPUT="$d/sapling-output.params"
|
|
info "Found Sapling params in $d"
|
|
return 0
|
|
fi
|
|
done
|
|
warn "Sapling params not found — embedded resources will be unavailable"
|
|
return 1
|
|
}
|
|
|
|
find_asmap() {
|
|
local paths=(
|
|
"$SCRIPT_DIR/external/dragonx/asmap.dat"
|
|
"$SCRIPT_DIR/external/dragonx/contrib/asmap/asmap.dat"
|
|
"$HOME/dragonx/asmap.dat"
|
|
"$HOME/dragonx/contrib/asmap/asmap.dat"
|
|
"$SCRIPT_DIR/../asmap.dat"
|
|
"$SCRIPT_DIR/asmap.dat"
|
|
"$SCRIPT_DIR/../SilentDragonX/asmap.dat"
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-linux/asmap.dat"
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-win/asmap.dat"
|
|
)
|
|
for p in "${paths[@]}"; do
|
|
if [[ -f "$p" ]]; then
|
|
ASMAP_DAT="$p"
|
|
info "Found asmap.dat at $p"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# ── Helper: bundle daemon binaries into a target dir ─────────────────────────
|
|
bundle_linux_daemon() {
|
|
local dest="$1"
|
|
local found=0
|
|
|
|
local daemon_paths=(
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-linux/dragonxd"
|
|
"$SCRIPT_DIR/../dragonxd"
|
|
"$SCRIPT_DIR/external/dragonx/src/dragonxd"
|
|
"$HOME/dragonx/src/dragonxd"
|
|
)
|
|
for p in "${daemon_paths[@]}"; do
|
|
if [[ -f "$p" ]]; then
|
|
cp "$p" "$dest/dragonxd"; chmod +x "$dest/dragonxd"
|
|
info " Bundled dragonxd"; found=1; break
|
|
fi
|
|
done
|
|
|
|
local cli_paths=(
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-linux/dragonx-cli"
|
|
"$SCRIPT_DIR/../dragonx-cli"
|
|
"$SCRIPT_DIR/external/dragonx/src/dragonx-cli"
|
|
"$HOME/dragonx/src/dragonx-cli"
|
|
)
|
|
for p in "${cli_paths[@]}"; do
|
|
if [[ -f "$p" ]]; then
|
|
cp "$p" "$dest/dragonx-cli"; chmod +x "$dest/dragonx-cli"
|
|
info " Bundled dragonx-cli"; break
|
|
fi
|
|
done
|
|
|
|
local dragonxd_paths=(
|
|
"$SCRIPT_DIR/prebuilt-binaries/dragonxd-linux/dragonxd"
|
|
"$SCRIPT_DIR/../dragonxd"
|
|
"$SCRIPT_DIR/external/dragonx/src/dragonxd"
|
|
"$HOME/dragonx/src/dragonxd"
|
|
)
|
|
for p in "${dragonxd_paths[@]}"; do
|
|
if [[ -f "$p" ]]; then
|
|
cp "$p" "$dest/dragonxd"; chmod +x "$dest/dragonxd"
|
|
info " Bundled dragonxd"; break
|
|
fi
|
|
done
|
|
|
|
# asmap.dat
|
|
find_asmap && cp "$ASMAP_DAT" "$dest/asmap.dat" && info " Bundled asmap.dat"
|
|
|
|
return $found
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# DEV BUILD (native, no packaging)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
build_dev() {
|
|
header "Dev Build ($(uname -s) / $BUILD_TYPE)"
|
|
|
|
# Use platform-appropriate build directory
|
|
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
local bd="$SCRIPT_DIR/build/mac"
|
|
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
|
else
|
|
local bd="$SCRIPT_DIR/build/linux"
|
|
fi
|
|
|
|
if $CLEAN; then
|
|
info "Cleaning $bd ..."; rm -rf "$bd"
|
|
fi
|
|
mkdir -p "$bd" && cd "$bd"
|
|
|
|
info "Configuring ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=ON
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS"
|
|
|
|
[[ -f "bin/ObsidianDragon" ]] || { err "Build failed"; exit 1; }
|
|
info "Dev binary: $bd/bin/ObsidianDragon ($(du -h bin/ObsidianDragon | cut -f1))"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# RELEASE: LINUX — build + strip + bundle daemon + zip + AppImage
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
build_release_linux() {
|
|
header "Release: Linux x86_64"
|
|
local bd="$SCRIPT_DIR/build/linux"
|
|
local out="$SCRIPT_DIR/release/linux"
|
|
|
|
if $CLEAN; then
|
|
info "Cleaning $bd ..."; rm -rf "$bd"
|
|
fi
|
|
mkdir -p "$bd" && cd "$bd"
|
|
|
|
# ── Compile ──────────────────────────────────────────────────────────────
|
|
info "Configuring ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=ON
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS"
|
|
|
|
[[ -f "bin/ObsidianDragon" ]] || { err "Linux build failed"; exit 1; }
|
|
|
|
info "Stripping ..."
|
|
strip bin/ObsidianDragon
|
|
info "Binary: $(du -h bin/ObsidianDragon | cut -f1)"
|
|
|
|
# ── Bundle daemon ────────────────────────────────────────────────────────
|
|
bundle_linux_daemon "bin" || warn "Daemon not bundled — wallet-only build"
|
|
|
|
# ── Bundle Sapling params ────────────────────────────────────────────────
|
|
SAPLING_SPEND="" SAPLING_OUTPUT=""
|
|
find_sapling_params && {
|
|
cp -f "$SAPLING_SPEND" "bin/sapling-spend.params"
|
|
cp -f "$SAPLING_OUTPUT" "bin/sapling-output.params"
|
|
info "Bundled Sapling params"
|
|
} || warn "Sapling params not found — not bundled"
|
|
|
|
# ── Package: release/linux/ ──────────────────────────────────────────────
|
|
rm -rf "$out"
|
|
mkdir -p "$out"
|
|
|
|
local DIST="ObsidianDragon-${VERSION}-Linux-x64"
|
|
local dist_dir="$out/$DIST"
|
|
mkdir -p "$dist_dir"
|
|
|
|
cp bin/ObsidianDragon "$dist_dir/"
|
|
[[ -f bin/dragonxd ]] && cp bin/dragonxd "$dist_dir/"
|
|
[[ -f bin/dragonx-cli ]] && cp bin/dragonx-cli "$dist_dir/"
|
|
[[ -f bin/asmap.dat ]] && cp bin/asmap.dat "$dist_dir/"
|
|
[[ -f bin/sapling-spend.params ]] && cp bin/sapling-spend.params "$dist_dir/"
|
|
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$dist_dir/"
|
|
# Bundle xmrig for mining support
|
|
local XMRIG_LINUX="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
|
[[ -f "$XMRIG_LINUX" ]] && { cp "$XMRIG_LINUX" "$dist_dir/"; chmod +x "$dist_dir/xmrig"; info "Bundled xmrig"; } || warn "xmrig not found — mining unavailable in zip"
|
|
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
|
|
|
# ── Zip ──────────────────────────────────────────────────────────────────
|
|
if command -v zip &>/dev/null; then
|
|
(cd "$out" && zip -r "$DIST.zip" "$DIST")
|
|
info "Zip: $out/$DIST.zip ($(du -h "$out/$DIST.zip" | cut -f1))"
|
|
fi
|
|
rm -rf "$dist_dir"
|
|
|
|
# ── AppImage (single-file) ───────────────────────────────────────────────
|
|
info "Creating AppImage ..."
|
|
local APPDIR="$bd/AppDir"
|
|
rm -rf "$APPDIR"
|
|
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/lib" \
|
|
"$APPDIR/usr/share/applications" \
|
|
"$APPDIR/usr/share/icons/hicolor/256x256/apps" \
|
|
"$APPDIR/usr/share/ObsidianDragon/res"
|
|
|
|
cp bin/ObsidianDragon "$APPDIR/usr/bin/"
|
|
cp -r bin/res/* "$APPDIR/usr/share/ObsidianDragon/res/" 2>/dev/null || true
|
|
|
|
[[ -f bin/dragonxd ]] && cp bin/dragonxd "$APPDIR/usr/bin/"
|
|
[[ -f bin/dragonx-cli ]] && cp bin/dragonx-cli "$APPDIR/usr/bin/"
|
|
# Daemon data files must be alongside the daemon binary (usr/bin/)
|
|
# because dragonxd searches relative to its own directory.
|
|
[[ -f bin/asmap.dat ]] && cp bin/asmap.dat "$APPDIR/usr/bin/"
|
|
[[ -f bin/sapling-spend.params ]] && cp bin/sapling-spend.params "$APPDIR/usr/bin/"
|
|
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$APPDIR/usr/bin/"
|
|
# Bundle xmrig for mining support
|
|
local XMRIG_LINUX_AI="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
|
[[ -f "$XMRIG_LINUX_AI" ]] && { cp "$XMRIG_LINUX_AI" "$APPDIR/usr/bin/"; chmod +x "$APPDIR/usr/bin/xmrig"; }
|
|
|
|
# Desktop entry
|
|
cat > "$APPDIR/usr/share/applications/ObsidianDragon.desktop" <<'DESK'
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=DragonX Wallet
|
|
Comment=DragonX Cryptocurrency Wallet
|
|
Exec=ObsidianDragon
|
|
Icon=ObsidianDragon
|
|
Categories=Finance;Network;
|
|
Terminal=false
|
|
StartupNotify=true
|
|
DESK
|
|
cp "$APPDIR/usr/share/applications/ObsidianDragon.desktop" "$APPDIR/"
|
|
|
|
# Icon
|
|
if [[ -f "$SCRIPT_DIR/res/icons/dragonx-256.png" ]]; then
|
|
cp "$SCRIPT_DIR/res/icons/dragonx-256.png" \
|
|
"$APPDIR/usr/share/icons/hicolor/256x256/apps/ObsidianDragon.png"
|
|
else
|
|
cat > "$APPDIR/ObsidianDragon.svg" <<'SVG'
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
|
<rect width="256" height="256" rx="32" fill="#1a1a1a"/>
|
|
<circle cx="128" cy="128" r="80" fill="none" stroke="#2e7d32" stroke-width="8"/>
|
|
<text x="128" y="145" font-family="Arial,sans-serif" font-size="72" font-weight="bold"
|
|
fill="#4caf50" text-anchor="middle">DX</text>
|
|
</svg>
|
|
SVG
|
|
if command -v rsvg-convert &>/dev/null; then
|
|
rsvg-convert -w 256 -h 256 "$APPDIR/ObsidianDragon.svg" > \
|
|
"$APPDIR/usr/share/icons/hicolor/256x256/apps/ObsidianDragon.png"
|
|
fi
|
|
fi
|
|
cp "$APPDIR/usr/share/icons/hicolor/256x256/apps/ObsidianDragon.png" "$APPDIR/" 2>/dev/null || \
|
|
cp "$APPDIR/ObsidianDragon.svg" "$APPDIR/ObsidianDragon.png" 2>/dev/null || true
|
|
|
|
# AppRun
|
|
cat > "$APPDIR/AppRun" <<'APPRUN'
|
|
#!/bin/bash
|
|
SELF=$(readlink -f "$0")
|
|
HERE=${SELF%/*}
|
|
export DRAGONX_RES_PATH="${HERE}/usr/share/ObsidianDragon/res"
|
|
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
|
|
cd "${HERE}/usr/share/ObsidianDragon"
|
|
exec "${HERE}/usr/bin/ObsidianDragon" "$@"
|
|
APPRUN
|
|
chmod +x "$APPDIR/AppRun"
|
|
|
|
# Bundle SDL3
|
|
for lib in libSDL3.so; do
|
|
local lp
|
|
lp=$(ldconfig -p 2>/dev/null | grep "$lib" | head -1 | awk '{print $NF}')
|
|
[[ -n "$lp" && -f "$lp" ]] && cp "$lp" "$APPDIR/usr/lib/" 2>/dev/null || true
|
|
done
|
|
[[ -f "$bd/_deps/sdl3-build/libSDL3.so" ]] && cp "$bd/_deps/sdl3-build/libSDL3.so"* "$APPDIR/usr/lib/" 2>/dev/null || true
|
|
|
|
# appimagetool
|
|
local APPIMAGETOOL=""
|
|
if command -v appimagetool &>/dev/null; then
|
|
APPIMAGETOOL="appimagetool"
|
|
elif [[ -f "$bd/appimagetool-x86_64.AppImage" ]]; then
|
|
APPIMAGETOOL="$bd/appimagetool-x86_64.AppImage"
|
|
else
|
|
info "Downloading appimagetool ..."
|
|
wget -q -O "$bd/appimagetool-x86_64.AppImage" \
|
|
"https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
|
chmod +x "$bd/appimagetool-x86_64.AppImage"
|
|
APPIMAGETOOL="$bd/appimagetool-x86_64.AppImage"
|
|
fi
|
|
|
|
local ARCH
|
|
ARCH=$(uname -m)
|
|
cd "$bd"
|
|
ARCH="$ARCH" "$APPIMAGETOOL" "$APPDIR" "ObsidianDragon-${VERSION}-${ARCH}.AppImage" 2>/dev/null && {
|
|
cp "ObsidianDragon-${VERSION}-${ARCH}.AppImage" "$out/ObsidianDragon-${VERSION}.AppImage"
|
|
info "AppImage: $out/ObsidianDragon-${VERSION}.AppImage ($(du -h "$out/ObsidianDragon-${VERSION}.AppImage" | cut -f1))"
|
|
} || warn "AppImage creation failed — binaries zip still in release/linux/"
|
|
|
|
info "Linux release artifacts: $out/"
|
|
ls -lh "$out/"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# RELEASE: WINDOWS — mingw-w64 cross-compile + INCBIN embedding
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
build_release_win() {
|
|
header "Release: Windows x86_64 (cross-compile)"
|
|
local bd="$SCRIPT_DIR/build/windows"
|
|
local out="$SCRIPT_DIR/release/windows"
|
|
|
|
if $CLEAN; then
|
|
info "Cleaning $bd ..."; rm -rf "$bd"
|
|
fi
|
|
mkdir -p "$bd" && cd "$bd"
|
|
|
|
# ── Find MinGW ───────────────────────────────────────────────────────────
|
|
local MINGW_GCC="" MINGW_GXX=""
|
|
if command -v x86_64-w64-mingw32-gcc-posix &>/dev/null; then
|
|
MINGW_GCC="x86_64-w64-mingw32-gcc-posix"
|
|
MINGW_GXX="x86_64-w64-mingw32-g++-posix"
|
|
info "Using POSIX thread model"
|
|
elif command -v x86_64-w64-mingw32-gcc &>/dev/null; then
|
|
MINGW_GCC="x86_64-w64-mingw32-gcc"
|
|
MINGW_GXX="x86_64-w64-mingw32-g++"
|
|
if x86_64-w64-mingw32-gcc -v 2>&1 | grep -q "posix"; then
|
|
info "Using POSIX thread model"
|
|
else
|
|
warn "Using win32 thread model — may have threading issues"
|
|
warn "Switch: sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix"
|
|
fi
|
|
else
|
|
err "mingw-w64 not found! Install: sudo apt install mingw-w64"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Patch libwinpthread + libpthread to remove VERSIONINFO resources ────
|
|
# mingw-w64's libwinpthread.a and libpthread.a each ship a version.o
|
|
# with their own VERSIONINFO ("POSIX WinThreads for Windows") that
|
|
# collides with ours during .rsrc merge, causing Task Manager to show
|
|
# the wrong process description.
|
|
local PATCHED_LIB_DIR="$bd/patched-lib"
|
|
mkdir -p "$PATCHED_LIB_DIR"
|
|
for plib in libwinpthread.a libpthread.a; do
|
|
local SYS_LIB="/usr/x86_64-w64-mingw32/lib/$plib"
|
|
if [[ -f "$SYS_LIB" ]]; then
|
|
cp -f "$SYS_LIB" "$PATCHED_LIB_DIR/$plib"
|
|
x86_64-w64-mingw32-ar d "$PATCHED_LIB_DIR/$plib" version.o 2>/dev/null || true
|
|
info "Patched $plib (removed version.o VERSIONINFO resource)"
|
|
fi
|
|
done
|
|
|
|
# ── Toolchain file ───────────────────────────────────────────────────────
|
|
cat > "$bd/mingw-toolchain.cmake" <<TOOLCHAIN
|
|
set(CMAKE_SYSTEM_NAME Windows)
|
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
|
set(CMAKE_C_COMPILER $MINGW_GCC)
|
|
set(CMAKE_CXX_COMPILER $MINGW_GXX)
|
|
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
|
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++ -Wl,-Bstatic,--whole-archive -L$PATCHED_LIB_DIR -lwinpthread -Wl,--no-whole-archive")
|
|
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -static")
|
|
set(CMAKE_C_FLAGS "\${CMAKE_C_FLAGS} -static")
|
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
|
set(BUILD_SHARED_LIBS OFF)
|
|
TOOLCHAIN
|
|
|
|
# ── INCBIN embedded resources ────────────────────────────────────────────
|
|
info "Generating embedded resources (INCBIN) ..."
|
|
local GEN="$bd/generated"
|
|
local RES="$GEN/res"
|
|
mkdir -p "$RES"
|
|
|
|
SAPLING_SPEND="" SAPLING_OUTPUT="" ASMAP_DAT=""
|
|
find_sapling_params || true
|
|
find_asmap || true
|
|
|
|
if [[ -n "$SAPLING_SPEND" && -n "$SAPLING_OUTPUT" ]]; then
|
|
cp -f "$SAPLING_SPEND" "$RES/sapling-spend.params"
|
|
cp -f "$SAPLING_OUTPUT" "$RES/sapling-output.params"
|
|
|
|
cat > "$GEN/embedded_data.h" <<HDR
|
|
// Auto-generated — DO NOT EDIT (build.sh --win-release)
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include "incbin.h"
|
|
|
|
INCBIN(sapling_spend_params, "$RES/sapling-spend.params");
|
|
INCBIN(sapling_output_params, "$RES/sapling-output.params");
|
|
HDR
|
|
|
|
if [[ -n "$ASMAP_DAT" ]]; then
|
|
cp -f "$ASMAP_DAT" "$RES/asmap.dat"
|
|
echo "INCBIN(asmap_dat, \"$RES/asmap.dat\");" >> "$GEN/embedded_data.h"
|
|
else
|
|
echo 'extern "C" { static const uint8_t* g_asmap_dat_data = nullptr; }' >> "$GEN/embedded_data.h"
|
|
echo 'static const unsigned int g_asmap_dat_size = 0;' >> "$GEN/embedded_data.h"
|
|
fi
|
|
|
|
# ── Daemon binaries ──────────────────────────────────────────────
|
|
local DD="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win"
|
|
if [[ -d "$DD" && -f "$DD/dragonxd.exe" ]]; then
|
|
info "Embedding daemon binaries ..."
|
|
echo -e "\n#define HAS_EMBEDDED_DAEMON 1\n" >> "$GEN/embedded_data.h"
|
|
for f in dragonxd.exe dragonx-cli.exe dragonx-tx.exe; do
|
|
local sym=$(echo "$f" | sed 's/[^a-zA-Z0-9]/_/g')
|
|
if [[ -f "$DD/$f" ]]; then
|
|
cp -f "$DD/$f" "$RES/$f"
|
|
info " Staged $f ($(du -h "$DD/$f" | cut -f1))"
|
|
echo "INCBIN(${sym}, \"$RES/$f\");" >> "$GEN/embedded_data.h"
|
|
else
|
|
echo "extern \"C\" { static const uint8_t* g_${sym}_data = nullptr; }" >> "$GEN/embedded_data.h"
|
|
echo "static const unsigned int g_${sym}_size = 0;" >> "$GEN/embedded_data.h"
|
|
fi
|
|
done
|
|
else
|
|
warn "prebuilt-binaries/dragonxd-win/ not found — wallet-only build"
|
|
fi
|
|
|
|
# ── xmrig binary (from prebuilt-binaries/xmrig-hac/) ────────────────
|
|
local XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac"
|
|
if [[ -f "$XMRIG_DIR/xmrig.exe" ]]; then
|
|
cp -f "$XMRIG_DIR/xmrig.exe" "$RES/xmrig.exe"
|
|
info " Staged xmrig.exe ($(du -h "$XMRIG_DIR/xmrig.exe" | cut -f1))"
|
|
echo -e "\n#define HAS_EMBEDDED_XMRIG 1" >> "$GEN/embedded_data.h"
|
|
echo "INCBIN(xmrig_exe, \"$RES/xmrig.exe\");" >> "$GEN/embedded_data.h"
|
|
else
|
|
echo 'extern "C" { static const uint8_t* g_xmrig_exe_data = nullptr; }' >> "$GEN/embedded_data.h"
|
|
echo 'static const unsigned int g_xmrig_exe_size = 0;' >> "$GEN/embedded_data.h"
|
|
fi
|
|
|
|
# ── Theme images ─────────────────────────────────────────────────
|
|
echo -e "\n// ---- Embedded theme images ----" >> "$GEN/embedded_data.h"
|
|
local IMAGE_TABLE="" IMAGE_COUNT=0
|
|
for dir in "$SCRIPT_DIR/res/img/backgrounds/texture" "$SCRIPT_DIR/res/img/backgrounds/gradient" "$SCRIPT_DIR/res/img/logos"; do
|
|
[[ -d "$dir" ]] || continue
|
|
for img in "$dir"/*.png; do
|
|
[[ -f "$img" ]] || continue
|
|
local bn=$(basename "$img")
|
|
local sym=$(echo "$bn" | sed 's/[^a-zA-Z0-9]/_/g')
|
|
cp -f "$img" "$RES/$bn"
|
|
echo "INCBIN(img_${sym}, \"$RES/$bn\");" >> "$GEN/embedded_data.h"
|
|
IMAGE_TABLE+=" { g_img_${sym}_data, g_img_${sym}_size, \"${bn}\" },\n"
|
|
IMAGE_COUNT=$((IMAGE_COUNT + 1))
|
|
done
|
|
done
|
|
echo "" >> "$GEN/embedded_data.h"
|
|
echo "#define HAS_EMBEDDED_IMAGES 1" >> "$GEN/embedded_data.h"
|
|
echo "#define EMBEDDED_IMAGE_COUNT $IMAGE_COUNT" >> "$GEN/embedded_data.h"
|
|
echo "#define HAS_EMBEDDED_GRADIENT 1" >> "$GEN/embedded_data.h"
|
|
echo "#define HAS_EMBEDDED_LOGO 1" >> "$GEN/embedded_data.h"
|
|
# Backward compat aliases
|
|
echo "static const uint8_t* g_dark_gradient_png_data = g_img_dark_gradient_png_data;" >> "$GEN/embedded_data.h"
|
|
echo "static const unsigned int g_dark_gradient_png_size = g_img_dark_gradient_png_size;" >> "$GEN/embedded_data.h"
|
|
echo "static const uint8_t* g_logo_ObsidianDragon_dark_png_data = g_img_logo_ObsidianDragon_dark_png_data;" >> "$GEN/embedded_data.h"
|
|
echo "static const unsigned int g_logo_ObsidianDragon_dark_png_size = g_img_logo_ObsidianDragon_dark_png_size;" >> "$GEN/embedded_data.h"
|
|
echo "" >> "$GEN/embedded_data.h"
|
|
echo "struct EmbeddedImageEntry { const uint8_t* data; unsigned int size; const char* filename; };" >> "$GEN/embedded_data.h"
|
|
echo "static const EmbeddedImageEntry s_embedded_images[] = {" >> "$GEN/embedded_data.h"
|
|
echo -e "$IMAGE_TABLE" >> "$GEN/embedded_data.h"
|
|
echo " { nullptr, 0, nullptr }" >> "$GEN/embedded_data.h"
|
|
echo "};" >> "$GEN/embedded_data.h"
|
|
|
|
# ── Overlay themes ───────────────────────────────────────────────
|
|
# Expand skin files with layout sections from ui.toml before embedding
|
|
echo -e "\n// ---- Bundled overlay themes ----" >> "$GEN/embedded_data.h"
|
|
local THEME_STAGE_DIR="$bd/_expanded_themes"
|
|
mkdir -p "$THEME_STAGE_DIR"
|
|
python3 "$SCRIPT_DIR/scripts/expand_themes.py" "$SCRIPT_DIR/res/themes" "$THEME_STAGE_DIR"
|
|
local THEME_TABLE="" THEME_COUNT=0
|
|
for tf in "$THEME_STAGE_DIR"/*.toml; do
|
|
local tbn=$(basename "$tf")
|
|
[[ "$tbn" == "ui.toml" ]] && continue
|
|
local tsym=$(echo "$tbn" | sed 's/[^a-zA-Z0-9]/_/g')
|
|
cp -f "$tf" "$RES/$tbn"
|
|
echo "INCBIN(theme_${tsym}, \"$RES/$tbn\");" >> "$GEN/embedded_data.h"
|
|
THEME_TABLE+=" { g_theme_${tsym}_data, g_theme_${tsym}_size, \"${tbn}\" },\n"
|
|
THEME_COUNT=$((THEME_COUNT + 1))
|
|
done
|
|
echo "" >> "$GEN/embedded_data.h"
|
|
echo "#define EMBEDDED_THEME_COUNT $THEME_COUNT" >> "$GEN/embedded_data.h"
|
|
echo "struct EmbeddedThemeEntry { const uint8_t* data; unsigned int size; const char* filename; };" >> "$GEN/embedded_data.h"
|
|
echo "static const EmbeddedThemeEntry s_embedded_themes[] = {" >> "$GEN/embedded_data.h"
|
|
echo -e "$THEME_TABLE" >> "$GEN/embedded_data.h"
|
|
echo " { nullptr, 0, nullptr }" >> "$GEN/embedded_data.h"
|
|
echo "};" >> "$GEN/embedded_data.h"
|
|
|
|
info "Embedded resources header generated"
|
|
else
|
|
warn "Building WITHOUT embedded resources (Sapling params not found)"
|
|
fi
|
|
|
|
# ── Fetch libsodium for Windows if needed ──────────────────────────────
|
|
if [[ ! -f "$SCRIPT_DIR/libs/libsodium-win/lib/libsodium.a" ]]; then
|
|
info "Fetching libsodium for Windows ..."
|
|
"$SCRIPT_DIR/scripts/fetch-libsodium.sh" --win
|
|
fi
|
|
|
|
# ── CMake + build ────────────────────────────────────────────────────────
|
|
info "Configuring (cross-compile) ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_TOOLCHAIN_FILE="$bd/mingw-toolchain.cmake" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS"
|
|
|
|
[[ -f "bin/ObsidianDragon.exe" ]] || { err "Windows build failed"; exit 1; }
|
|
info "Binary: $(du -h bin/ObsidianDragon.exe | cut -f1)"
|
|
|
|
# ── Package: release/windows/ ────────────────────────────────────────────
|
|
rm -rf "$out"
|
|
mkdir -p "$out"
|
|
|
|
local DIST="ObsidianDragon-${VERSION}-Windows-x64"
|
|
local dist_dir="$out/$DIST"
|
|
mkdir -p "$dist_dir"
|
|
cp bin/ObsidianDragon.exe "$dist_dir/"
|
|
|
|
local DD="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win"
|
|
for f in dragonxd.exe dragonx-cli.exe dragonx-tx.exe; do
|
|
[[ -f "$DD/$f" ]] && cp "$DD/$f" "$dist_dir/"
|
|
done
|
|
|
|
# Bundle Sapling params + asmap for the zip distribution
|
|
# (The single-file exe has these embedded via INCBIN, but the zip
|
|
# needs them on disk so the daemon can find them in its work dir.)
|
|
for f in sapling-spend.params sapling-output.params asmap.dat; do
|
|
[[ -f "$DD/$f" ]] && cp "$DD/$f" "$dist_dir/"
|
|
done
|
|
|
|
# Bundle xmrig for mining support
|
|
local XMRIG_WIN="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig.exe"
|
|
[[ -f "$XMRIG_WIN" ]] && { cp "$XMRIG_WIN" "$dist_dir/"; info "Bundled xmrig.exe"; } || warn "xmrig.exe not found — mining unavailable in zip"
|
|
|
|
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
|
|
|
# ── Single-file exe (all resources embedded) ────────────────────────────
|
|
cp bin/ObsidianDragon.exe "$out/ObsidianDragon-${VERSION}.exe"
|
|
info "Single-file exe: $out/ObsidianDragon-${VERSION}.exe ($(du -h "$out/ObsidianDragon-${VERSION}.exe" | cut -f1))"
|
|
|
|
# ── Zip ──────────────────────────────────────────────────────────────────
|
|
if command -v zip &>/dev/null; then
|
|
(cd "$out" && zip -r "$DIST.zip" "$DIST")
|
|
info "Zip: $out/$DIST.zip ($(du -h "$out/$DIST.zip" | cut -f1))"
|
|
fi
|
|
rm -rf "$dist_dir"
|
|
|
|
info "Windows release artifacts: $out/"
|
|
ls -lh "$out/"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# RELEASE: macOS — .app bundle + DMG
|
|
#
|
|
# Cross-compile from Linux:
|
|
# 1. Install osxcross: https://github.com/tpoechtrager/osxcross
|
|
# export OSXCROSS="$HOME/osxcross" (or wherever you installed it)
|
|
# 2. For DMG: sudo apt install genisoimage
|
|
# 3. For .icns icons: sudo apt install icnsutils
|
|
# 4. Place macOS daemon binaries in prebuilt-binaries/dragonxd-mac/ (optional)
|
|
#
|
|
# Native on macOS:
|
|
# Works out of the box. brew install create-dmg for prettier DMGs.
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
build_release_mac() {
|
|
local IS_CROSS=false
|
|
local MAC_ARCH="x86_64"
|
|
|
|
# Detect cross-compilation from Linux
|
|
if [[ "$(uname -s)" == "Linux" ]]; then
|
|
IS_CROSS=true
|
|
info "Cross-compiling for macOS from Linux"
|
|
|
|
# Find osxcross
|
|
if [[ -z "${OSXCROSS:-}" ]]; then
|
|
# Try common locations
|
|
for try_path in "$SCRIPT_DIR/external/osxcross" "$HOME/osxcross" "/opt/osxcross" "/usr/local/osxcross"; do
|
|
if [[ -d "$try_path/target" ]]; then
|
|
OSXCROSS="$try_path"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -z "${OSXCROSS:-}" || ! -d "${OSXCROSS}/target" ]]; then
|
|
err "osxcross not found! Set OSXCROSS=/path/to/osxcross or install it:"
|
|
echo ""
|
|
echo " git clone https://github.com/tpoechtrager/osxcross"
|
|
echo " cd osxcross"
|
|
echo " # Place MacOSX SDK (e.g. MacOSX13.0.sdk.tar.xz) in tarballs/"
|
|
echo " UNATTENDED=1 ./build.sh"
|
|
echo " export OSXCROSS=\$PWD"
|
|
echo ""
|
|
exit 1
|
|
fi
|
|
info "Using osxcross at: $OSXCROSS"
|
|
export PATH="$OSXCROSS/target/bin:$PATH"
|
|
|
|
# Find the right compiler triple
|
|
local OSXCROSS_CC="" OSXCROSS_CXX="" OSXCROSS_TRIPLE=""
|
|
|
|
# Look for full-triple compilers first (e.g. x86_64-apple-darwin22.4-clang++)
|
|
local found_triple
|
|
found_triple=$(ls "$OSXCROSS/target/bin/"x86_64-apple-darwin*-clang++ 2>/dev/null | head -1)
|
|
if [[ -n "$found_triple" ]]; then
|
|
OSXCROSS_TRIPLE=$(basename "$found_triple" | sed 's/-clang++$//')
|
|
OSXCROSS_CC="$OSXCROSS/target/bin/${OSXCROSS_TRIPLE}-clang"
|
|
OSXCROSS_CXX="$OSXCROSS/target/bin/${OSXCROSS_TRIPLE}-clang++"
|
|
MAC_ARCH="x86_64"
|
|
fi
|
|
# Prefer arm64 if available (Apple Silicon)
|
|
found_triple=$(ls "$OSXCROSS/target/bin/"aarch64-apple-darwin*-clang++ 2>/dev/null | head -1)
|
|
if [[ -n "$found_triple" ]]; then
|
|
OSXCROSS_TRIPLE=$(basename "$found_triple" | sed 's/-clang++$//')
|
|
OSXCROSS_CC="$OSXCROSS/target/bin/${OSXCROSS_TRIPLE}-clang"
|
|
OSXCROSS_CXX="$OSXCROSS/target/bin/${OSXCROSS_TRIPLE}-clang++"
|
|
MAC_ARCH="arm64"
|
|
fi
|
|
# Also try o64/oa64 wrapper — but resolve the underlying triple
|
|
if [[ -z "$OSXCROSS_CXX" ]]; then
|
|
if command -v o64-clang++ &>/dev/null; then
|
|
# Resolve the x86_64 triple from binutils
|
|
OSXCROSS_TRIPLE=$(ls "$OSXCROSS/target/bin/"x86_64-apple-darwin*-ar 2>/dev/null | head -1 | xargs basename | sed 's/-ar$//')
|
|
OSXCROSS_CC="o64-clang"
|
|
OSXCROSS_CXX="o64-clang++"
|
|
MAC_ARCH="x86_64"
|
|
elif command -v oa64-clang++ &>/dev/null; then
|
|
OSXCROSS_TRIPLE=$(ls "$OSXCROSS/target/bin/"aarch64-apple-darwin*-ar 2>/dev/null | head -1 | xargs basename | sed 's/-ar$//')
|
|
OSXCROSS_CC="oa64-clang"
|
|
OSXCROSS_CXX="oa64-clang++"
|
|
MAC_ARCH="arm64"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$OSXCROSS_CXX" ]]; then
|
|
err "Could not find osxcross compilers in PATH"
|
|
echo " Ensure \$OSXCROSS/target/bin contains *-apple-darwin*-clang++"
|
|
exit 1
|
|
fi
|
|
info "macOS cross-compiler: $OSXCROSS_CXX (arch: $MAC_ARCH)"
|
|
else
|
|
# Native macOS: build universal binary (arm64 + x86_64)
|
|
MAC_ARCH="universal"
|
|
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
|
fi
|
|
|
|
header "Release: macOS ($MAC_ARCH$(${IS_CROSS} && echo ' — cross-compile'))"
|
|
local bd="$SCRIPT_DIR/build/mac"
|
|
local out="$SCRIPT_DIR/release/mac"
|
|
|
|
if $CLEAN; then
|
|
info "Cleaning $bd ..."; rm -rf "$bd"
|
|
fi
|
|
mkdir -p "$bd" && cd "$bd"
|
|
|
|
# ── Compile ──────────────────────────────────────────────────────────────
|
|
if $IS_CROSS; then
|
|
# OSXCROSS_TRIPLE already resolved during compiler detection above
|
|
local OSXCROSS_SDK_PATH
|
|
OSXCROSS_SDK_PATH=$(ls -d "$OSXCROSS"/target/SDK/MacOSX*.sdk 2>/dev/null | head -1)
|
|
|
|
# Generate osxcross toolchain file
|
|
cat > "$bd/osxcross-toolchain.cmake" <<TOOLCHAIN
|
|
set(CMAKE_SYSTEM_NAME Darwin)
|
|
set(CMAKE_SYSTEM_PROCESSOR ${MAC_ARCH})
|
|
|
|
# Compilers
|
|
set(CMAKE_C_COMPILER ${OSXCROSS_CC})
|
|
set(CMAKE_CXX_COMPILER ${OSXCROSS_CXX})
|
|
set(CMAKE_OBJC_COMPILER ${OSXCROSS_CC})
|
|
set(CMAKE_OBJCXX_COMPILER ${OSXCROSS_CXX})
|
|
|
|
# SDK sysroot
|
|
set(CMAKE_OSX_SYSROOT "${OSXCROSS_SDK_PATH}")
|
|
|
|
# Cross-compilation binutils
|
|
set(CMAKE_AR "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-ar" CACHE FILEPATH "ar")
|
|
set(CMAKE_RANLIB "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-ranlib" CACHE FILEPATH "ranlib")
|
|
set(CMAKE_STRIP "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-strip" CACHE FILEPATH "strip")
|
|
set(CMAKE_INSTALL_NAME_TOOL "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-install_name_tool" CACHE FILEPATH "install_name_tool")
|
|
set(CMAKE_OTOOL "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-otool" CACHE FILEPATH "otool")
|
|
set(CMAKE_LIBTOOL "${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-libtool" CACHE FILEPATH "libtool")
|
|
|
|
# Search paths
|
|
set(CMAKE_FIND_ROOT_PATH
|
|
"${OSXCROSS_SDK_PATH}"
|
|
"${OSXCROSS}/target/macports/pkgs"
|
|
"${OSXCROSS}/target/macports/pkgs/opt/local"
|
|
)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|
|
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
|
TOOLCHAIN
|
|
|
|
# Find compiler-rt builtins for macOS
|
|
local COMPILER_RT=""
|
|
local CLANG_RESOURCE_DIR
|
|
CLANG_RESOURCE_DIR=$("$OSXCROSS_CC" -print-resource-dir 2>/dev/null || echo "")
|
|
if [[ -n "$CLANG_RESOURCE_DIR" && -f "$CLANG_RESOURCE_DIR/lib/darwin/libclang_rt.osx.a" ]]; then
|
|
COMPILER_RT="$CLANG_RESOURCE_DIR/lib/darwin/libclang_rt.osx.a"
|
|
elif [[ -f "$OSXCROSS/build/compiler-rt/compiler-rt/build/lib/darwin/libclang_rt.osx.a" ]]; then
|
|
COMPILER_RT="$OSXCROSS/build/compiler-rt/compiler-rt/build/lib/darwin/libclang_rt.osx.a"
|
|
fi
|
|
|
|
# Fetch libsodium for macOS if needed
|
|
if [[ ! -f "$SCRIPT_DIR/libs/libsodium-mac/lib/libsodium.a" ]]; then
|
|
info "Fetching libsodium for macOS ..."
|
|
"$SCRIPT_DIR/scripts/fetch-libsodium.sh" --mac
|
|
fi
|
|
|
|
info "Configuring (cross-compile via osxcross) ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_TOOLCHAIN_FILE="$bd/osxcross-toolchain.cmake" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
|
${COMPILER_RT:+-DOSXCROSS_COMPILER_RT="$COMPILER_RT"}
|
|
else
|
|
# Build libsodium as universal if needed
|
|
local need_sodium=false
|
|
if [[ ! -f "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" ]] && \
|
|
[[ ! -f "$SCRIPT_DIR/libs/libsodium-mac/lib/libsodium.a" ]]; then
|
|
need_sodium=true
|
|
elif [[ -f "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" ]]; then
|
|
# Rebuild if existing lib is not universal (single-arch won't link)
|
|
if ! lipo -info "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" 2>/dev/null | grep -q "arm64.*x86_64\|x86_64.*arm64"; then
|
|
info "Existing libsodium is not universal — rebuilding ..."
|
|
rm -rf "$SCRIPT_DIR/libs/libsodium"
|
|
need_sodium=true
|
|
fi
|
|
fi
|
|
if $need_sodium; then
|
|
info "Building libsodium (universal) ..."
|
|
"$SCRIPT_DIR/scripts/fetch-libsodium.sh"
|
|
fi
|
|
|
|
info "Configuring (native universal arm64+x86_64) ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
|
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
|
fi
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS"
|
|
|
|
[[ -f "bin/ObsidianDragon" ]] || { err "macOS build failed"; exit 1; }
|
|
|
|
# Strip — use osxcross strip for cross-builds
|
|
if $IS_CROSS; then
|
|
local STRIP_CMD="${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-strip"
|
|
if [[ -x "$STRIP_CMD" ]]; then
|
|
info "Stripping (osxcross) ..."
|
|
"$STRIP_CMD" bin/ObsidianDragon
|
|
else
|
|
warn "osxcross strip not found at $STRIP_CMD — skipping"
|
|
fi
|
|
else
|
|
info "Stripping ..."
|
|
strip bin/ObsidianDragon
|
|
# Verify universal binary
|
|
if command -v lipo &>/dev/null; then
|
|
info "Architecture info:"
|
|
lipo -info bin/ObsidianDragon
|
|
fi
|
|
fi
|
|
info "Binary: $(du -h bin/ObsidianDragon | cut -f1)"
|
|
|
|
# ── Create .app bundle ───────────────────────────────────────────────────
|
|
rm -rf "$out"
|
|
mkdir -p "$out"
|
|
|
|
local APP="$out/ObsidianDragon.app"
|
|
local CONTENTS="$APP/Contents"
|
|
local MACOS="$CONTENTS/MacOS"
|
|
local RESOURCES="$CONTENTS/Resources"
|
|
local FRAMEWORKS="$CONTENTS/Frameworks"
|
|
|
|
mkdir -p "$MACOS" "$RESOURCES/res" "$FRAMEWORKS"
|
|
|
|
# Main binary
|
|
cp bin/ObsidianDragon "$MACOS/"
|
|
chmod +x "$MACOS/ObsidianDragon"
|
|
|
|
# Resources
|
|
cp -r bin/res/* "$RESOURCES/res/" 2>/dev/null || true
|
|
|
|
# Daemon binaries (macOS native, from dragonxd-mac/)
|
|
local daemon_dir="$SCRIPT_DIR/prebuilt-binaries/dragonxd-mac"
|
|
if [[ -d "$daemon_dir" ]]; then
|
|
for f in dragonxd dragonx-cli dragonx-tx; do
|
|
[[ -f "$daemon_dir/$f" ]] && { cp "$daemon_dir/$f" "$MACOS/"; chmod +x "$MACOS/$f"; info " Bundled $f"; }
|
|
done
|
|
for f in sapling-spend.params sapling-output.params; do
|
|
[[ -f "$daemon_dir/$f" ]] && { cp "$daemon_dir/$f" "$MACOS/"; info " Bundled $f"; }
|
|
done
|
|
elif ! $IS_CROSS; then
|
|
# Native macOS: try standard paths
|
|
local daemon_paths=(
|
|
"$SCRIPT_DIR/../dragonxd"
|
|
"$HOME/dragonx/src/dragonxd"
|
|
)
|
|
for p in "${daemon_paths[@]}"; do
|
|
[[ -f "$p" ]] && { cp "$p" "$MACOS/dragonxd"; chmod +x "$MACOS/dragonxd"; info " Bundled dragonxd"; break; }
|
|
done
|
|
local cli_paths=(
|
|
"$SCRIPT_DIR/../dragonx-cli"
|
|
"$HOME/dragonx/src/dragonx-cli"
|
|
)
|
|
for p in "${cli_paths[@]}"; do
|
|
[[ -f "$p" ]] && { cp "$p" "$MACOS/dragonx-cli"; chmod +x "$MACOS/dragonx-cli"; info " Bundled dragonx-cli"; break; }
|
|
done
|
|
else
|
|
warn "prebuilt-binaries/dragonxd-mac/ not found — place macOS daemon binaries there for bundling"
|
|
fi
|
|
|
|
# xmrig binary (from prebuilt-binaries/xmrig-hac/)
|
|
local XMRIG_MAC="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
|
if [[ -f "$XMRIG_MAC" ]]; then
|
|
cp "$XMRIG_MAC" "$MACOS/xmrig"
|
|
chmod +x "$MACOS/xmrig"
|
|
info " Bundled xmrig"
|
|
else
|
|
warn "xmrig not found — mining unavailable in .app"
|
|
fi
|
|
|
|
# asmap.dat — placed in MacOS/ so the daemon finds it next to its binary
|
|
find_asmap 2>/dev/null && {
|
|
cp "$ASMAP_DAT" "$MACOS/asmap.dat"
|
|
info " Bundled asmap.dat"
|
|
}
|
|
|
|
# Bundle SDL3 dylib
|
|
local sdl_dylib=""
|
|
for candidate in \
|
|
"$bd/_deps/sdl3-build/libSDL3.dylib" \
|
|
"$bd/_deps/sdl3-build/libSDL3.0.dylib" \
|
|
"/usr/local/lib/libSDL3.dylib" \
|
|
"/opt/homebrew/lib/libSDL3.dylib"; do
|
|
if [[ -f "$candidate" ]]; then
|
|
sdl_dylib="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -n "$sdl_dylib" ]]; then
|
|
cp "$sdl_dylib" "$FRAMEWORKS/"
|
|
local sdl_name=$(basename "$sdl_dylib")
|
|
# Fix the rpath so the binary finds SDL3 in Frameworks/
|
|
if $IS_CROSS; then
|
|
local INSTALL_NAME_TOOL="${OSXCROSS}/target/bin/${OSXCROSS_TRIPLE}-install_name_tool"
|
|
[[ -x "$INSTALL_NAME_TOOL" ]] && "$INSTALL_NAME_TOOL" -change "@rpath/$sdl_name" "@executable_path/../Frameworks/$sdl_name" "$MACOS/ObsidianDragon" 2>/dev/null || true
|
|
else
|
|
install_name_tool -change "@rpath/$sdl_name" "@executable_path/../Frameworks/$sdl_name" "$MACOS/ObsidianDragon" 2>/dev/null || true
|
|
fi
|
|
info " Bundled $sdl_name"
|
|
fi
|
|
|
|
# Launcher script (ensures working dir + dylib path)
|
|
mv "$MACOS/ObsidianDragon" "$MACOS/ObsidianDragon.bin"
|
|
cat > "$MACOS/ObsidianDragon" <<'LAUNCH'
|
|
#!/bin/bash
|
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
export DYLD_LIBRARY_PATH="$DIR/../Frameworks:$DYLD_LIBRARY_PATH"
|
|
export DRAGONX_RES_PATH="$DIR/../Resources/res"
|
|
cd "$DIR/../Resources"
|
|
exec "$DIR/ObsidianDragon.bin" "$@"
|
|
LAUNCH
|
|
chmod +x "$MACOS/ObsidianDragon"
|
|
|
|
# Info.plist
|
|
cat > "$CONTENTS/Info.plist" <<PLIST
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleName</key>
|
|
<string>DragonX Wallet</string>
|
|
<key>CFBundleDisplayName</key>
|
|
<string>DragonX Wallet</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>is.hush.dragonx</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>${VERSION}</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>${VERSION}</string>
|
|
<key>CFBundleExecutable</key>
|
|
<string>ObsidianDragon</string>
|
|
<key>CFBundleIconFile</key>
|
|
<string>ObsidianDragon</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>APPL</string>
|
|
<key>CFBundleSignature</key>
|
|
<string>DRGX</string>
|
|
<key>LSMinimumSystemVersion</key>
|
|
<string>11.0</string>
|
|
<key>NSHighResolutionCapable</key>
|
|
<true/>
|
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
|
<true/>
|
|
<key>LSApplicationCategoryType</key>
|
|
<string>public.app-category.finance</string>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
|
|
# ── Icon (.icns) ─────────────────────────────────────────────────────────
|
|
if [[ -f "$SCRIPT_DIR/res/img/ObsidianDragon.icns" ]]; then
|
|
cp "$SCRIPT_DIR/res/img/ObsidianDragon.icns" "$RESOURCES/ObsidianDragon.icns"
|
|
elif [[ -f "$SCRIPT_DIR/res/icons/dragonx-256.png" ]]; then
|
|
if command -v iconutil &>/dev/null && command -v sips &>/dev/null; then
|
|
# Native macOS: sips + iconutil
|
|
local iconset="$bd/ObsidianDragon.iconset"
|
|
mkdir -p "$iconset"
|
|
local src="$SCRIPT_DIR/res/icons/dragonx-256.png"
|
|
for sz in 16 32 64 128 256; do
|
|
sips -z $sz $sz "$src" --out "$iconset/icon_${sz}x${sz}.png" &>/dev/null || true
|
|
done
|
|
for sz in 16 32 128; do
|
|
local r=$((sz * 2))
|
|
sips -z $r $r "$src" --out "$iconset/icon_${sz}x${sz}@2x.png" &>/dev/null || true
|
|
done
|
|
iconutil -c icns "$iconset" -o "$RESOURCES/ObsidianDragon.icns" 2>/dev/null || true
|
|
rm -rf "$iconset"
|
|
elif command -v png2icns &>/dev/null; then
|
|
# Linux: png2icns from icnsutils (sudo apt install icnsutils)
|
|
info "Creating .icns with png2icns ..."
|
|
local src="$SCRIPT_DIR/res/icons/dragonx-256.png"
|
|
# png2icns needs specific sizes; resize with ImageMagick if available
|
|
if command -v convert &>/dev/null; then
|
|
local icondir="$bd/icon-sizes"
|
|
mkdir -p "$icondir"
|
|
for sz in 16 32 48 128 256; do
|
|
convert "$src" -resize ${sz}x${sz} "$icondir/icon_${sz}.png" 2>/dev/null || true
|
|
done
|
|
png2icns "$RESOURCES/ObsidianDragon.icns" "$icondir"/icon_*.png 2>/dev/null || true
|
|
rm -rf "$icondir"
|
|
else
|
|
# Just use the 256px as-is
|
|
png2icns "$RESOURCES/ObsidianDragon.icns" "$src" 2>/dev/null || true
|
|
fi
|
|
else
|
|
warn "No .icns tool found (install: sudo apt install icnsutils)"
|
|
fi
|
|
fi
|
|
|
|
info ".app bundle created: $APP"
|
|
|
|
# ── Zip the .app bundle ──────────────────────────────────────────────────
|
|
local APP_ZIP="ObsidianDragon-${VERSION}-macOS-${MAC_ARCH}.app.zip"
|
|
if command -v zip &>/dev/null; then
|
|
(cd "$out" && zip -r "$APP_ZIP" "ObsidianDragon.app")
|
|
info "App zip: $out/$APP_ZIP ($(du -h "$out/$APP_ZIP" | cut -f1))"
|
|
fi
|
|
|
|
# ── Create DMG ───────────────────────────────────────────────────────────
|
|
local DMG_NAME="DragonX_Wallet-${VERSION}-macOS-${MAC_ARCH}.dmg"
|
|
|
|
if command -v create-dmg &>/dev/null; then
|
|
# create-dmg (works on macOS; also available on Linux via npm)
|
|
info "Creating DMG with create-dmg ..."
|
|
create-dmg \
|
|
--volname "DragonX Wallet" \
|
|
--volicon "$RESOURCES/ObsidianDragon.icns" \
|
|
--window-pos 200 120 \
|
|
--window-size 600 400 \
|
|
--icon-size 100 \
|
|
--icon "ObsidianDragon.app" 150 190 \
|
|
--app-drop-link 450 190 \
|
|
--no-internet-enable \
|
|
"$out/$DMG_NAME" \
|
|
"$APP" 2>/dev/null && {
|
|
info "DMG: $out/$DMG_NAME ($(du -h "$out/$DMG_NAME" | cut -f1))"
|
|
} || warn "create-dmg failed — .app bundle still available"
|
|
elif command -v hdiutil &>/dev/null; then
|
|
# Native macOS
|
|
info "Creating DMG with hdiutil ..."
|
|
local staging="$bd/dmg-staging"
|
|
rm -rf "$staging"
|
|
mkdir -p "$staging"
|
|
cp -a "$APP" "$staging/"
|
|
ln -s /Applications "$staging/Applications"
|
|
hdiutil create -volname "DragonX Wallet" \
|
|
-srcfolder "$staging" \
|
|
-ov -format UDZO \
|
|
"$out/$DMG_NAME" 2>/dev/null && {
|
|
info "DMG: $out/$DMG_NAME ($(du -h "$out/$DMG_NAME" | cut -f1))"
|
|
} || warn "hdiutil failed — .app bundle still available"
|
|
rm -rf "$staging"
|
|
elif command -v genisoimage &>/dev/null; then
|
|
# Linux fallback: genisoimage produces a hybrid ISO/DMG that macOS can open
|
|
info "Creating DMG with genisoimage (Linux) ..."
|
|
local staging="$bd/dmg-staging"
|
|
rm -rf "$staging"
|
|
mkdir -p "$staging"
|
|
cp -a "$APP" "$staging/"
|
|
# Can't create a real symlink to /Applications in an ISO, but the .app
|
|
# is the important part — users drag it to Applications manually.
|
|
genisoimage -V "DragonX Wallet" \
|
|
-D -R -apple -no-pad \
|
|
-o "$out/$DMG_NAME" \
|
|
"$staging" 2>/dev/null && {
|
|
info "DMG: $out/$DMG_NAME ($(du -h "$out/$DMG_NAME" | cut -f1))"
|
|
info " (ISO/HFS hybrid — mountable on macOS)"
|
|
} || warn "genisoimage failed — .app bundle still available"
|
|
rm -rf "$staging"
|
|
else
|
|
warn "No DMG tool found — .app bundle still available"
|
|
echo ""
|
|
echo " To create DMGs on Linux, install one of:"
|
|
echo " sudo apt install genisoimage # Basic DMG (recommended)"
|
|
echo " sudo apt install icnsutils # For .icns icon creation"
|
|
echo ""
|
|
fi
|
|
|
|
info "macOS release artifacts: $out/"
|
|
ls -lhR "$out/" 2>/dev/null | head -30
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# MAIN
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
echo -e "${GREEN}DragonX Wallet v${VERSION} — Unified Build${NC}"
|
|
echo "─────────────────────────────────────────"
|
|
|
|
$DO_DEV && build_dev
|
|
$DO_LINUX && build_release_linux
|
|
$DO_WIN && build_release_win
|
|
$DO_MAC && build_release_mac
|
|
|
|
header "Done"
|
|
if $DO_LINUX || $DO_WIN || $DO_MAC; then
|
|
echo -e " Release artifacts in: ${GREEN}$SCRIPT_DIR/release/${NC}"
|
|
[[ -d "$SCRIPT_DIR/release/linux" ]] && echo -e " ${CYAN}linux/${NC} — AppImage + binary"
|
|
[[ -d "$SCRIPT_DIR/release/windows" ]] && echo -e " ${CYAN}windows/${NC} — .exe + .zip"
|
|
[[ -d "$SCRIPT_DIR/release/mac" ]] && echo -e " ${CYAN}mac/${NC} — .app + .dmg"
|
|
fi
|