The screenshot sweep and the full UI sweep are dev tools. The full sweep injects demo data into the live wallet state and the real data and config dirs, and the tab sweep screenshots real balances and addresses. Both were compiled into every build: the Settings buttons were reachable in full-node releases, and the DRAGONX_FULL_SWEEP env hook worked in every build, Lite releases included. A default-OFF option alone would fail open: dev and release builds share build/linux (and build/mac), so a release configured without --clean would inherit a cached ON from the last dev configure. - CMake: option(DRAGONX_ENABLE_UI_SWEEP ... OFF), passed to ObsidianDragon and ObsidianDragonTests as a compile definition. - build.sh passes it explicitly: ON in the dev build, OFF in every release configure (Linux, Windows, macOS osxcross and native). - Packaging guard: while the cache says ON, dragonx_release depends on dragonx_release_sweep_guard, which fails, so a hand-configured ON tree cannot package a release. - startSweepImpl is a no-op unless the option is ON (an undefined macro counts as 0, so it fails closed). The enabled branch logs the sentinel "[Sweep] DRAGONX_UI_SWEEP_COMPILED_IN", so a release can be checked with `strings <bin> | grep -c DRAGONX_UI_SWEEP_COMPILED_IN` (must be 0). - main.cpp: the DRAGONX_FULL_SWEEP hook is compiled out (autoSweep is false). It must be: with the sweep a no-op, the hook would quit the app once its warm-up frames ran out. - Settings > Node > Debug options: both sweep buttons, open-dir, the seed-demo-chat button and the current-theme checkbox are compiled only into dev builds. The daemon debug categories are unchanged. No i18n change: the existing strings stay in the tables. Verified with an isolated release (DRAGONX_BUILD_ROOT/DRAGONX_RELEASE_ROOT in a scratch dir) whose tree was first configured by hand with ON: build.sh --linux-release without --clean leaves the cache OFF, and the sentinel count is 0 in the built binary, the zip and the AppImage. With the cache set back to ON by hand, --target dragonx_release fails at the guard before compiling anything. Dev trees configured ON keep the sweep (sentinel present; a one-surface DRAGONX_FULL_SWEEP run against the fake daemon captured and exited cleanly). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1507 lines
75 KiB
Bash
Executable File
1507 lines
75 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 is resolved per-variant from CMakeLists.txt (the single source of truth) after arg
|
|
# parsing — see the APP_BASENAME block below. Placeholder until then.
|
|
VERSION=""
|
|
|
|
# ── 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
|
|
DO_LITE=false
|
|
DO_LITE_BACKEND=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/
|
|
--lite Build ObsidianDragonLite variant (no embedded daemon/full-node features)
|
|
--lite-backend Like --lite, and link the real SDXL litelib backend artifact
|
|
(auto-discovers build/lite-backend/<platform>/; build it with
|
|
scripts/build-lite-backend-artifact.sh, or set DRAGONX_LITE_BACKEND_DIR)
|
|
|
|
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 --lite-backend --mac-release # macOS ObsidianDragonLite.app + DMG (lite backend)
|
|
$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 ;;
|
|
--lite) DO_LITE=true; shift ;;
|
|
--lite-backend) DO_LITE=true; DO_LITE_BACKEND=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
|
|
|
|
APP_BASENAME="ObsidianDragon"
|
|
CMAKE_LITE_ARGS=()
|
|
# Always set the variant flag EXPLICITLY (ON and OFF) so switching variants in a shared build dir
|
|
# can't reuse a stale cached value (e.g. a prior --lite build leaving DRAGONX_BUILD_LITE=ON).
|
|
if $DO_LITE; then
|
|
APP_BASENAME="ObsidianDragonLite"
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_BUILD_LITE=ON")
|
|
# Lite never embeds/launches a daemon; set it explicitly too for cache hygiene.
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_ENABLE_EMBEDDED_DAEMON=OFF")
|
|
info "Lite mode enabled: building ${APP_BASENAME}"
|
|
else
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_BUILD_LITE=OFF")
|
|
# Re-assert the embedded daemon ON for full-node builds, EXPLICITLY, so a build dir whose cache
|
|
# was poisoned OFF by a prior --lite configure (or any stale value) is healed — otherwise the
|
|
# full-node app extracts dragonxd but never launches it (isUsingEmbeddedDaemon() == false).
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_ENABLE_EMBEDDED_DAEMON=ON")
|
|
fi
|
|
|
|
# Resolve the release version string for the active variant from CMakeLists.txt (single source of
|
|
# truth): the full-node app uses project() VERSION + DRAGONX_VERSION_SUFFIX; ObsidianDragonLite uses
|
|
# DRAGONX_LITE_VERSION + DRAGONX_LITE_VERSION_SUFFIX.
|
|
_cml="$SCRIPT_DIR/CMakeLists.txt"
|
|
_full_ver=$(sed -n 's/^[[:space:]]*VERSION[[:space:]][[:space:]]*\([0-9][0-9.]*\).*/\1/p' "$_cml" | head -1)
|
|
_full_suffix=$(sed -n 's/^set(DRAGONX_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
|
_lite_ver=$(sed -n 's/^set(DRAGONX_LITE_VERSION[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
|
_lite_suffix=$(sed -n 's/^set(DRAGONX_LITE_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
|
if $DO_LITE; then
|
|
VERSION="${_lite_ver}${_lite_suffix}"
|
|
else
|
|
VERSION="${_full_ver}${_full_suffix}"
|
|
fi
|
|
[ -n "$_full_ver" ] && [ -n "$VERSION" ] || { err "Could not parse version from CMakeLists.txt"; exit 1; }
|
|
info "Release version: ${VERSION} (${APP_BASENAME})"
|
|
|
|
# ── Lite backend (real SDXL litelib) linking ─────────────────────────────────
|
|
# Enables DRAGONX_ENABLE_LITE_BACKEND with an imported artifact produced by
|
|
# scripts/build-lite-backend-artifact.sh. Auto-discovers build/lite-backend/<platform>/;
|
|
# override the directory with DRAGONX_LITE_BACKEND_DIR.
|
|
if $DO_LITE_BACKEND; then
|
|
# Artifact platform follows the cross target when exactly one non-host release is requested,
|
|
# so `--lite-backend --win-release` links the Windows backend (not the host's) automatically.
|
|
case "$(uname -s)" in
|
|
Linux) lb_platform="linux" ;;
|
|
Darwin) lb_platform="macos" ;;
|
|
*) lb_platform="linux" ;;
|
|
esac
|
|
if $DO_WIN && ! $DO_LINUX && ! $DO_MAC; then lb_platform="windows"; fi
|
|
if $DO_MAC && ! $DO_LINUX && ! $DO_WIN; then lb_platform="macos"; fi
|
|
lb_dir="${DRAGONX_LITE_BACKEND_DIR:-$SCRIPT_DIR/build/lite-backend/$lb_platform}"
|
|
lb_lib=""
|
|
for cand in "$lb_dir"/libsilentdragonxlite.a "$lb_dir"/libsilentdragonxlite.so "$lb_dir"/silentdragonxlite.lib; do
|
|
[[ -f "$cand" ]] && { lb_lib="$cand"; break; }
|
|
done
|
|
lb_symbols="$lb_dir/lite-backend-symbols.txt"
|
|
lb_manifest="$lb_dir/lite-backend-artifact-manifest.json"
|
|
if [[ -z "$lb_lib" || ! -f "$lb_symbols" ]]; then
|
|
err "Lite backend artifact not found under: $lb_dir"
|
|
err "Build it first: ./scripts/build-lite-backend-artifact.sh --platform $lb_platform"
|
|
err "Or set DRAGONX_LITE_BACKEND_DIR to an existing artifact directory."
|
|
exit 1
|
|
fi
|
|
CMAKE_LITE_ARGS+=(
|
|
"-DDRAGONX_ENABLE_LITE_BACKEND=ON"
|
|
"-DDRAGONX_LITE_BACKEND_LIBRARY=$lb_lib"
|
|
"-DDRAGONX_LITE_BACKEND_SYMBOLS_FILE=$lb_symbols"
|
|
"-DDRAGONX_LITE_BACKEND_LINK_MODE=imported"
|
|
"-DDRAGONX_LITE_BACKEND_ABI=sdxl-c-v1"
|
|
)
|
|
[[ -f "$lb_manifest" ]] && CMAKE_LITE_ARGS+=("-DDRAGONX_LITE_BACKEND_MANIFEST=$lb_manifest")
|
|
# A Rust x86_64-pc-windows-gnu staticlib pulls in Win32 system libs (rustls/schannel, ring,
|
|
# dirs, std) that the app doesn't already link. The set is rustc's `--print native-static-libs`
|
|
# for the backend (winapi_* shims mapped to the real mingw import libs); all exist in mingw-w64.
|
|
if [[ "$lb_platform" == "windows" ]]; then
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_LITE_BACKEND_EXTRA_LIBS=advapi32;ws2_32;kernel32;bcrypt;cfgmgr32;credui;crypt32;cryptnet;fwpuclnt;gdi32;msimg32;ncrypt;ntdll;ole32;opengl32;secur32;shell32;synchronization;user32;winspool;userenv")
|
|
fi
|
|
info "Lite backend enabled ($lb_platform): $lb_lib"
|
|
else
|
|
# Explicit OFF so a prior --lite-backend configure in a shared build dir can't leave it ON
|
|
# (which would then fail the BUILD_LITE=OFF guard in CMake).
|
|
CMAKE_LITE_ARGS+=("-DDRAGONX_ENABLE_LITE_BACKEND=OFF")
|
|
fi
|
|
|
|
should_bundle_full_node_assets() {
|
|
! $DO_LITE
|
|
}
|
|
|
|
# The offline wallet-rebuild helper is the ONLY thing that repairs a genuinely BDB-inconsistent
|
|
# wallet.dat — plain "Restore" just re-triggers the daemon's salvage cascade. A full-node release must
|
|
# NEVER ship without it (the in-app "Repair automatically" option silently disappears otherwise), so
|
|
# treat a missing helper as a HARD build failure instead of degrading recovery to Restore-only.
|
|
# $1 = built helper path (e.g. bin/dragonx-wallet-rebuild[.exe]); $2 = the BDB depends dir for the hint.
|
|
require_wallet_rebuild_helper() {
|
|
local helper="$1" depends="$2"
|
|
should_bundle_full_node_assets || return 0 # lite builds have no BDB wallet.dat to rebuild
|
|
if [[ ! -f "$helper" ]]; then
|
|
err "wallet-rebuild helper was NOT built: $helper"
|
|
err " → the recovery 'Repair automatically' option would be MISSING from this release."
|
|
err " Cause: the vendored Berkeley DB depends are absent, so CMake skipped the dragonx-wallet-rebuild target."
|
|
err " Fix: provide ${depends}/{lib/libdb-6.2.a,include/db.h} (same static libdb the daemon links), then rebuild."
|
|
exit 1
|
|
fi
|
|
info " wallet-rebuild helper present: $helper"
|
|
}
|
|
|
|
# Vendored Berkeley DB depends directory for the macOS wallet-rebuild helper, by TARGET arch. The daemon
|
|
# links a static libdb-6.2; the helper must match the build arch, so derive the triple from it rather than
|
|
# hardcoding one. Single-arch: x86_64 -> x86_64-apple-darwin, arm64 -> aarch64-apple-darwin. "universal"
|
|
# (or anything unexpected) falls back to the host arch — a universal .app needs a universal libdb, so if
|
|
# only a single-arch libdb is vendored, build single-arch via DRAGONX_MAC_ARCHS.
|
|
mac_bdb_dir() {
|
|
local arch="$1" triple
|
|
case "$arch" in
|
|
x86_64) triple="x86_64-apple-darwin" ;;
|
|
arm64) triple="aarch64-apple-darwin" ;;
|
|
*) [[ "$(uname -m)" == "arm64" ]] && triple="aarch64-apple-darwin" || triple="x86_64-apple-darwin" ;;
|
|
esac
|
|
printf '%s/external/dragonx/depends/%s\n' "$SCRIPT_DIR" "$triple"
|
|
}
|
|
|
|
# ── 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"
|
|
|
|
# (The dragonx-wallet-rebuild recovery helper is built into bin/ by CMake and packaged explicitly
|
|
# by each release path — required via require_wallet_rebuild_helper — so it is not copied here.)
|
|
|
|
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 \
|
|
-DDRAGONX_ENABLE_UI_SWEEP=ON \
|
|
"${CMAKE_LITE_ARGS[@]}"
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS"
|
|
|
|
[[ -f "bin/${APP_BASENAME}" ]] || { err "Build failed"; exit 1; }
|
|
info "Dev binary: $bd/bin/${APP_BASENAME} ($(du -h "bin/${APP_BASENAME}" | cut -f1))"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# RELEASE: LINUX — build + strip + bundle daemon + zip + AppImage
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
build_release_linux() {
|
|
header "Release: Linux x86_64"
|
|
local bd="${DRAGONX_BUILD_ROOT:-$SCRIPT_DIR/build}/linux"
|
|
local out="${DRAGONX_RELEASE_ROOT:-$SCRIPT_DIR/release}/linux"
|
|
|
|
if $CLEAN; then
|
|
info "Cleaning $bd ..."; rm -rf "$bd"
|
|
fi
|
|
mkdir -p "$bd" && cd "$bd"
|
|
|
|
# ── Compile ──────────────────────────────────────────────────────────────
|
|
# Point the wallet-rebuild helper at the vendored static Berkeley DB (same libdb the daemon links)
|
|
# so its output is a v6.2 btree the bundled dragonxd reads. Pass the paths EXPLICITLY (bypasses
|
|
# find_library + its cache); the helper target is simply not built if the depends tree is absent.
|
|
local lin_bdb="$SCRIPT_DIR/external/dragonx/depends/x86_64-unknown-linux-gnu"
|
|
local BDB_ARGS=()
|
|
if [[ -f "$lin_bdb/lib/libdb-6.2.a" && -f "$lin_bdb/include/db.h" ]]; then
|
|
BDB_ARGS=( -DBDB_INCLUDE_DIR="$lin_bdb/include" -DBDB_LIBRARY="$lin_bdb/lib/libdb-6.2.a" )
|
|
elif should_bundle_full_node_assets; then
|
|
err "Vendored Berkeley DB depends missing at $lin_bdb — the wallet-rebuild recovery helper cannot be built."
|
|
err " A full-node release must ship it; aborting rather than degrading recovery to Restore-only."
|
|
exit 1
|
|
fi
|
|
info "Configuring ..."
|
|
# UI sweep OFF explicitly: build/linux is shared with dev builds, which cache it ON.
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=ON \
|
|
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
|
"${BDB_ARGS[@]}" \
|
|
"${CMAKE_LITE_ARGS[@]}"
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS" --target dragonx_release
|
|
|
|
[[ -f "bin/${APP_BASENAME}" ]] || { err "Linux build failed"; exit 1; }
|
|
|
|
# A full-node release MUST include the recovery helper — fail loudly, never ship without it.
|
|
require_wallet_rebuild_helper "bin/dragonx-wallet-rebuild" "$lin_bdb"
|
|
|
|
info "Stripping ..."
|
|
strip "bin/${APP_BASENAME}"
|
|
[[ -f "bin/dragonx-wallet-rebuild" ]] && strip "bin/dragonx-wallet-rebuild"
|
|
info "Binary: $(du -h "bin/${APP_BASENAME}" | cut -f1)"
|
|
|
|
if should_bundle_full_node_assets; then
|
|
# ── 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"
|
|
else
|
|
info "Lite mode: skipping daemon and Sapling/asmap bundling"
|
|
fi
|
|
|
|
# ── Package: release/linux/ ──────────────────────────────────────────────
|
|
# Remove only THIS variant's prior artifacts so full-node and lite releases can coexist in the
|
|
# same output dir (both ObsidianDragon* and ObsidianDragonLite* end up under release/linux/).
|
|
mkdir -p "$out"
|
|
rm -rf "$out/${APP_BASENAME}-"* "$out/${APP_BASENAME}.AppImage"
|
|
|
|
local DIST="${APP_BASENAME}-${VERSION}-Linux-x64"
|
|
local dist_dir="$out/$DIST"
|
|
mkdir -p "$dist_dir"
|
|
|
|
cp "bin/${APP_BASENAME}" "$dist_dir/"
|
|
if should_bundle_full_node_assets; then
|
|
[[ -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/"
|
|
# Offline wallet-rebuild recovery helper — required (asserted above); ships next to the app.
|
|
cp bin/dragonx-wallet-rebuild "$dist_dir/" && chmod +x "$dist_dir/dragonx-wallet-rebuild"
|
|
info " Bundled dragonx-wallet-rebuild"
|
|
fi
|
|
# Bundle xmrig for mining support
|
|
local XMRIG_LINUX="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/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/${APP_BASENAME}" "$APPDIR/usr/bin/"
|
|
cp -r bin/res/* "$APPDIR/usr/share/ObsidianDragon/res/" 2>/dev/null || true
|
|
|
|
if should_bundle_full_node_assets; then
|
|
[[ -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/"
|
|
# Offline wallet-rebuild recovery helper — required (asserted above); ships next to the app.
|
|
cp bin/dragonx-wallet-rebuild "$APPDIR/usr/bin/" && chmod +x "$APPDIR/usr/bin/dragonx-wallet-rebuild"
|
|
fi
|
|
# Bundle xmrig for mining support
|
|
local XMRIG_LINUX_AI="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/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=${APP_BASENAME}
|
|
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/${APP_BASENAME}" "\$@"
|
|
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 — pinned to a tagged release and SHA-256 verified before we exec it.
|
|
# The old "continuous" tag is a MOVING build fetched over the network and run on the release
|
|
# builder; a compromised/MITM'd artifact would execute here. Verify, or refuse to package.
|
|
local APPIMAGETOOL_URL="https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage"
|
|
local APPIMAGETOOL_SHA256="46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1"
|
|
local APPIMAGETOOL=""
|
|
if command -v appimagetool &>/dev/null; then
|
|
APPIMAGETOOL="appimagetool" # maintainer's own trusted system install
|
|
else
|
|
local at="$bd/appimagetool-x86_64.AppImage"
|
|
# Re-verify any cached copy too; a stale unverified download must not be trusted.
|
|
if [[ ! -f "$at" ]] || ! echo "${APPIMAGETOOL_SHA256} ${at}" | sha256sum -c --status; then
|
|
info "Downloading appimagetool 1.9.0 (pinned) ..."
|
|
wget -q -O "$at" "$APPIMAGETOOL_URL"
|
|
if ! echo "${APPIMAGETOOL_SHA256} ${at}" | sha256sum -c --status; then
|
|
err "appimagetool SHA-256 verification failed — refusing to use it"
|
|
rm -f "$at"
|
|
return 1
|
|
fi
|
|
chmod +x "$at"
|
|
fi
|
|
APPIMAGETOOL="$at"
|
|
fi
|
|
|
|
local ARCH
|
|
ARCH=$(uname -m)
|
|
cd "$bd"
|
|
ARCH="$ARCH" "$APPIMAGETOOL" "$APPDIR" "${APP_BASENAME}-${VERSION}-${ARCH}.AppImage" 2>/dev/null && {
|
|
cp "${APP_BASENAME}-${VERSION}-${ARCH}.AppImage" "$out/${APP_BASENAME}-${VERSION}.AppImage"
|
|
info "AppImage: $out/${APP_BASENAME}-${VERSION}.AppImage ($(du -h "$out/${APP_BASENAME}-${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="${DRAGONX_BUILD_ROOT:-$SCRIPT_DIR/build}/windows"
|
|
local out="${DRAGONX_RELEASE_ROOT:-$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=""
|
|
local EMBED_NODE_DATA=false
|
|
should_bundle_full_node_assets && EMBED_NODE_DATA=true
|
|
if $EMBED_NODE_DATA; then
|
|
find_sapling_params || true
|
|
find_asmap || true
|
|
fi
|
|
|
|
# Lite needs no Sapling params/asmap (its backend embeds its own), so it always gets a header for
|
|
# images/themes/xmrig; the full node still requires the params to be found.
|
|
if ! $EMBED_NODE_DATA || [[ -n "$SAPLING_SPEND" && -n "$SAPLING_OUTPUT" ]]; then
|
|
cat > "$GEN/embedded_data.h" <<HDR
|
|
// Auto-generated — DO NOT EDIT (build.sh --win-release)
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include "incbin.h"
|
|
|
|
HDR
|
|
if $EMBED_NODE_DATA; then
|
|
cp -f "$SAPLING_SPEND" "$RES/sapling-spend.params"
|
|
cp -f "$SAPLING_OUTPUT" "$RES/sapling-output.params"
|
|
cat >> "$GEN/embedded_data.h" <<HDR
|
|
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
|
|
else
|
|
info "Lite mode: not embedding Sapling params/asmap (the SDXL backend embeds its own)"
|
|
for sym in sapling_spend_params sapling_output_params asmap_dat; do
|
|
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"
|
|
done
|
|
fi
|
|
|
|
# ── Daemon binaries ──────────────────────────────────────────────
|
|
local DD="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win"
|
|
if should_bundle_full_node_assets; then
|
|
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
|
|
else
|
|
info "Lite mode: skipping embedded daemon binaries"
|
|
fi
|
|
|
|
# ── Wallet-rebuild recovery helper ───────────────────────────────
|
|
# Built in-tree (not a prebuilt like the daemon), so compile it standalone HERE — before the
|
|
# main app compiles embedded_resources.cpp — and INCBIN it, so a bare, self-extracting
|
|
# ObsidianDragon.exe carries the recovery tool exactly like it does the daemon.
|
|
if should_bundle_full_node_assets; then
|
|
local WBDB="$SCRIPT_DIR/external/dragonx/depends/x86_64-w64-mingw32"
|
|
if [[ -f "$WBDB/lib/libdb-6.2.a" && -f "$WBDB/include/db.h" ]]; then
|
|
info "Compiling + embedding wallet-rebuild helper ..."
|
|
x86_64-w64-mingw32-g++ -std=c++17 -O2 -static -static-libgcc -static-libstdc++ \
|
|
-I"$SCRIPT_DIR/src" -I"$WBDB/include" \
|
|
"$SCRIPT_DIR/tools/wallet_rebuild/main.cpp" \
|
|
"$WBDB/lib/libdb-6.2.a" -lws2_32 \
|
|
-o "$RES/dragonx-wallet-rebuild.exe" \
|
|
|| { err "wallet-rebuild helper failed to compile for embedding"; exit 1; }
|
|
x86_64-w64-mingw32-strip "$RES/dragonx-wallet-rebuild.exe" 2>/dev/null || true
|
|
echo -e "\n#define HAS_EMBEDDED_WALLET_REBUILD 1" >> "$GEN/embedded_data.h"
|
|
echo "INCBIN(dragonx_wallet_rebuild_exe, \"$RES/dragonx-wallet-rebuild.exe\");" >> "$GEN/embedded_data.h"
|
|
info " Embedded dragonx-wallet-rebuild.exe ($(du -h "$RES/dragonx-wallet-rebuild.exe" | cut -f1))"
|
|
else
|
|
err "Vendored mingw Berkeley DB missing at $WBDB — cannot embed the wallet-rebuild recovery helper."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ── xmrig binary (from prebuilt-binaries/drg-xmrig/) ────────────────
|
|
local XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig"
|
|
# The published DRG-XMRig archives ship the binary inside a versioned subdir, not as a flat
|
|
# xmrig.exe. Extract it from the matching win-x64 zip if it isn't already staged — otherwise
|
|
# the embed below never fires (HAS_EMBEDDED_XMRIG stays undefined) and the wallet ships with
|
|
# no miner ("xmrig binary not found" at runtime).
|
|
if [[ ! -f "$XMRIG_DIR/xmrig.exe" ]]; then
|
|
local _xz; _xz=$(ls "$XMRIG_DIR"/drg-xmrig-*-win-x64.zip 2>/dev/null | head -1)
|
|
if [[ -n "$_xz" ]] && command -v unzip >/dev/null 2>&1; then
|
|
local _xtmp; _xtmp=$(mktemp -d)
|
|
# -j flattens the versioned subdir; check the file (not unzip's exit code, which is
|
|
# non-zero if a pattern matches nothing).
|
|
unzip -j -o "$_xz" '*xmrig.exe' -d "$_xtmp" >/dev/null 2>&1 || true
|
|
if [[ -f "$_xtmp/xmrig.exe" ]]; then
|
|
cp -f "$_xtmp/xmrig.exe" "$XMRIG_DIR/xmrig.exe"
|
|
info " Extracted xmrig.exe from $(basename "$_xz")"
|
|
fi
|
|
rm -rf "$_xtmp"
|
|
fi
|
|
fi
|
|
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)"
|
|
rm -f "$GEN/embedded_data.h" # never compile a stale header left by the other variant in this shared build dir
|
|
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
|
|
|
|
# ── FreeType for Windows (color-emoji rendering) ───────────────────────
|
|
# The mingw toolchain ships no FreeType; cross-build a minimal static one (COLR/CPAL, no external
|
|
# deps). Failure is non-fatal — the wallet just falls back to monochrome emoji.
|
|
local FT_MINGW_PREFIX="$SCRIPT_DIR/third_party/freetype-mingw"
|
|
if [[ ! -f "$FT_MINGW_PREFIX/lib/libfreetype.a" ]]; then
|
|
info "Cross-building FreeType for Windows (color emoji) ..."
|
|
"$SCRIPT_DIR/scripts/build-freetype-mingw.sh" "$FT_MINGW_PREFIX" \
|
|
|| warn "FreeType cross-build failed — Windows build will use monochrome emoji"
|
|
fi
|
|
local FT_CMAKE_ARG=()
|
|
if [[ -f "$FT_MINGW_PREFIX/lib/libfreetype.a" ]]; then
|
|
FT_CMAKE_ARG=(-DDRAGONX_MINGW_FREETYPE_PREFIX="$FT_MINGW_PREFIX")
|
|
fi
|
|
|
|
# ── CMake + build ────────────────────────────────────────────────────────
|
|
# The wallet-rebuild helper links the vendored mingw static Berkeley DB (the mingw toolchain's
|
|
# find_library is sysroot-only, so pass the depends paths EXPLICITLY to bypass the search). Only
|
|
# enabled if the depends tree is present; guarded with -DBDB_* left empty otherwise.
|
|
local win_bdb="$SCRIPT_DIR/external/dragonx/depends/x86_64-w64-mingw32"
|
|
local BDB_ARGS=()
|
|
if [[ -f "$win_bdb/lib/libdb-6.2.a" && -f "$win_bdb/include/db.h" ]]; then
|
|
BDB_ARGS=( -DBDB_INCLUDE_DIR="$win_bdb/include" -DBDB_LIBRARY="$win_bdb/lib/libdb-6.2.a" )
|
|
elif should_bundle_full_node_assets; then
|
|
err "Vendored Berkeley DB depends missing at $win_bdb — the wallet-rebuild recovery helper cannot be built."
|
|
err " A full-node release must ship it; aborting rather than degrading recovery to Restore-only."
|
|
exit 1
|
|
fi
|
|
info "Configuring (cross-compile) ..."
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_TOOLCHAIN_FILE="$bd/mingw-toolchain.cmake" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
|
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
|
"${BDB_ARGS[@]}" \
|
|
"${FT_CMAKE_ARG[@]}" \
|
|
"${CMAKE_LITE_ARGS[@]}"
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS" --target dragonx_release
|
|
|
|
[[ -f "bin/${APP_BASENAME}.exe" ]] || { err "Windows build failed"; exit 1; }
|
|
# Strip the app exe — the Linux and macOS release paths already strip theirs, and even the Windows
|
|
# helper (dragonx-wallet-rebuild.exe) is stripped, but the main app exe was shipping unstripped
|
|
# (~5MB of symbols on the full node, ~19MB on the params-heavy lite build). Best-effort.
|
|
x86_64-w64-mingw32-strip --strip-all "bin/${APP_BASENAME}.exe" 2>/dev/null \
|
|
|| warn " strip unavailable — shipping unstripped ${APP_BASENAME}.exe"
|
|
info "Binary: $(du -h "bin/${APP_BASENAME}.exe" | cut -f1)"
|
|
|
|
# A full-node release MUST include the recovery helper — fail loudly, never ship without it.
|
|
require_wallet_rebuild_helper "bin/dragonx-wallet-rebuild.exe" "$win_bdb"
|
|
|
|
# ── Package: release/windows/ ────────────────────────────────────────────
|
|
# Remove only THIS variant's prior artifacts so full-node and lite releases coexist here.
|
|
mkdir -p "$out"
|
|
rm -rf "$out/${APP_BASENAME}-"* "$out/${APP_BASENAME}.exe"
|
|
|
|
local DIST="${APP_BASENAME}-${VERSION}-Windows-x64"
|
|
local dist_dir="$out/$DIST"
|
|
mkdir -p "$dist_dir"
|
|
cp "bin/${APP_BASENAME}.exe" "$dist_dir/"
|
|
|
|
local DD="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win"
|
|
if should_bundle_full_node_assets; then
|
|
for f in dragonxd.exe dragonx-cli.exe dragonx-tx.exe; do
|
|
[[ -f "$DD/$f" ]] && cp "$DD/$f" "$dist_dir/"
|
|
done
|
|
# dragonx-wallet-rebuild helper (offline recovery for a BDB-inconsistent wallet.dat) — required.
|
|
cp "bin/dragonx-wallet-rebuild.exe" "$dist_dir/" && info " Bundled dragonx-wallet-rebuild.exe"
|
|
[[ -f "$dist_dir/dragonx-wallet-rebuild.exe" ]] || { err "Failed to bundle dragonx-wallet-rebuild.exe"; exit 1; }
|
|
|
|
# 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
|
|
else
|
|
info "Lite mode: skipping daemon and Sapling/asmap assets in Windows zip"
|
|
fi
|
|
|
|
# The exe embeds xmrig (HAS_EMBEDDED_XMRIG) and getXmrigPath() always prefers / re-extracts the copy in
|
|
# %APPDATA%\<app>\dragonx, so a loose exe-dir xmrig.exe is never used. Ship it loose only when not embedded.
|
|
if grep -q '^#define HAS_EMBEDDED_XMRIG 1' "$GEN/embedded_data.h" 2>/dev/null; then
|
|
info "xmrig.exe is embedded in the exe — not copied loose into the zip"
|
|
else
|
|
local XMRIG_WIN="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/xmrig.exe"
|
|
[[ -f "$XMRIG_WIN" ]] && { cp "$XMRIG_WIN" "$dist_dir/"; info "Bundled xmrig.exe"; } || warn "xmrig.exe not found — mining unavailable in zip"
|
|
fi
|
|
|
|
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
|
|
|
# ── Single-file exe (all resources embedded) ────────────────────────────
|
|
cp "bin/${APP_BASENAME}.exe" "$out/${APP_BASENAME}-${VERSION}.exe"
|
|
info "Single-file exe: $out/${APP_BASENAME}-${VERSION}.exe ($(du -h "$out/${APP_BASENAME}-${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 (arm64 + x86_64) by default. Override with
|
|
# DRAGONX_MAC_ARCHS (e.g. "x86_64").
|
|
MAC_ARCHS="${DRAGONX_MAC_ARCHS:-arm64;x86_64}"
|
|
# When linking the real lite backend, the app can only include architectures
|
|
# the backend static library actually provides. Its pinned ring 0.16.11 has no
|
|
# Apple-Silicon assembly, so that artifact is x86_64-only — constrain the app
|
|
# arch to the backend's (unless the user explicitly forced DRAGONX_MAC_ARCHS),
|
|
# otherwise the arm64 slice fails to link.
|
|
if $DO_LITE_BACKEND && [[ -z "${DRAGONX_MAC_ARCHS:-}" && -n "${lb_lib:-}" ]] && command -v lipo &>/dev/null; then
|
|
local _backend_archs; _backend_archs=$(lipo -archs "$lb_lib" 2>/dev/null | tr ' ' ';')
|
|
if [[ -n "$_backend_archs" && "$_backend_archs" != "$MAC_ARCHS" ]]; then
|
|
warn "Lite backend provides only [$_backend_archs] — building the app for that instead of universal."
|
|
MAC_ARCHS="$_backend_archs"
|
|
fi
|
|
fi
|
|
if [[ "$MAC_ARCHS" == *";"* || "$MAC_ARCHS" == *","* ]]; then
|
|
MAC_ARCH="universal"
|
|
else
|
|
MAC_ARCH="$MAC_ARCHS"
|
|
fi
|
|
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
|
fi
|
|
|
|
header "Release: macOS ($MAC_ARCH$(${IS_CROSS} && echo ' — cross-compile'))"
|
|
local bd="${DRAGONX_BUILD_ROOT:-$SCRIPT_DIR/build}/mac"
|
|
local out="${DRAGONX_RELEASE_ROOT:-$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) ..."
|
|
# Vendored static libdb for the recovery helper (full-node only), by target arch; see mac_bdb_dir.
|
|
local mac_bdb; mac_bdb="$(mac_bdb_dir "$MAC_ARCH")"
|
|
local BDB_ARGS=()
|
|
if should_bundle_full_node_assets && [[ -f "$mac_bdb/lib/libdb-6.2.a" && -f "$mac_bdb/include/db.h" ]]; then
|
|
BDB_ARGS=( -DBDB_INCLUDE_DIR="$mac_bdb/include" -DBDB_LIBRARY="$mac_bdb/lib/libdb-6.2.a" )
|
|
fi
|
|
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 \
|
|
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
|
${COMPILER_RT:+-DOSXCROSS_COMPILER_RT="$COMPILER_RT"} \
|
|
"${BDB_ARGS[@]}" \
|
|
"${CMAKE_LITE_ARGS[@]}"
|
|
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 -Eq "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 macOS, arch: $MAC_ARCHS) ..."
|
|
# Point CMake at the vendored static libdb for the recovery helper (full-node only); see mac_bdb_dir.
|
|
local mac_bdb; mac_bdb="$(mac_bdb_dir "$MAC_ARCH")"
|
|
local BDB_ARGS=()
|
|
if should_bundle_full_node_assets && [[ -f "$mac_bdb/lib/libdb-6.2.a" && -f "$mac_bdb/include/db.h" ]]; then
|
|
BDB_ARGS=( -DBDB_INCLUDE_DIR="$mac_bdb/include" -DBDB_LIBRARY="$mac_bdb/lib/libdb-6.2.a" )
|
|
fi
|
|
cmake "$SCRIPT_DIR" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
|
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
|
-DCMAKE_OSX_ARCHITECTURES="$MAC_ARCHS" \
|
|
"${BDB_ARGS[@]}" \
|
|
"${CMAKE_LITE_ARGS[@]}"
|
|
fi
|
|
|
|
info "Building with $JOBS jobs ..."
|
|
cmake --build . -j "$JOBS" --target dragonx_release
|
|
|
|
[[ -f "bin/${APP_BASENAME}" ]] || { err "macOS build failed"; exit 1; }
|
|
|
|
# A full-node release MUST include the recovery helper. macOS needs a static libdb-6.2 (Homebrew
|
|
# berkeley-db for a native build, or a vendored external/dragonx/depends/<triple>) — otherwise CMake
|
|
# skips the target and this fails loudly rather than shipping a mac release with no recovery option.
|
|
require_wallet_rebuild_helper "bin/dragonx-wallet-rebuild" "$(mac_bdb_dir "$MAC_ARCH")"
|
|
|
|
# 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/${APP_BASENAME}"
|
|
else
|
|
warn "osxcross strip not found at $STRIP_CMD — skipping"
|
|
fi
|
|
else
|
|
info "Stripping ..."
|
|
strip "bin/${APP_BASENAME}"
|
|
# Verify universal binary
|
|
if command -v lipo &>/dev/null; then
|
|
info "Architecture info:"
|
|
lipo -info "bin/${APP_BASENAME}"
|
|
fi
|
|
fi
|
|
info "Binary: $(du -h "bin/${APP_BASENAME}" | cut -f1)"
|
|
|
|
# ── Create .app bundle ───────────────────────────────────────────────────
|
|
mkdir -p "$out"
|
|
# Clean only THIS variant's prior artifacts so full-node and lite releases can
|
|
# coexist in release/mac/ (Linux/Windows scope their cleanup the same way). The
|
|
# "ObsidianDragon-" glob never matches "ObsidianDragonLite-" (and vice versa),
|
|
# and the ".app" names are exact.
|
|
rm -rf "$out/${APP_BASENAME}.app" "$out/${APP_BASENAME}-"*.app.zip "$out/${APP_BASENAME}-"*.dmg
|
|
|
|
local APP="$out/${APP_BASENAME}.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/${APP_BASENAME}" "$MACOS/"
|
|
chmod +x "$MACOS/${APP_BASENAME}"
|
|
|
|
# Resources
|
|
cp -r bin/res/* "$RESOURCES/res/" 2>/dev/null || true
|
|
|
|
if should_bundle_full_node_assets; then
|
|
# 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
|
|
# Offline wallet-rebuild recovery helper — required (asserted after build); next to the daemon.
|
|
cp "bin/dragonx-wallet-rebuild" "$MACOS/" && chmod +x "$MACOS/dragonx-wallet-rebuild" && info " Bundled dragonx-wallet-rebuild"
|
|
else
|
|
info "Lite mode: skipping macOS daemon and Sapling/asmap bundling"
|
|
fi
|
|
|
|
# xmrig binary (from prebuilt-binaries/drg-xmrig/)
|
|
local XMRIG_MAC="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/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
|
|
|
|
if should_bundle_full_node_assets; then
|
|
# 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"
|
|
}
|
|
fi
|
|
|
|
# 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/${APP_BASENAME}" 2>/dev/null || true
|
|
else
|
|
install_name_tool -change "@rpath/$sdl_name" "@executable_path/../Frameworks/$sdl_name" "$MACOS/${APP_BASENAME}" 2>/dev/null || true
|
|
fi
|
|
info " Bundled $sdl_name"
|
|
fi
|
|
|
|
# Launcher script (ensures working dir + dylib path). Uses ${APP_BASENAME} so the lite
|
|
# variant (ObsidianDragonLite) gets a correctly-named launcher + .bin pair.
|
|
mv "$MACOS/${APP_BASENAME}" "$MACOS/${APP_BASENAME}.bin"
|
|
cat > "$MACOS/${APP_BASENAME}" <<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/${APP_BASENAME}.bin" "\$@"
|
|
LAUNCH
|
|
chmod +x "$MACOS/${APP_BASENAME}"
|
|
|
|
# Info.plist — display name + bundle id differ per variant so lite and full-node .apps
|
|
# can coexist; the executable matches the launcher (${APP_BASENAME}); the icon is shared.
|
|
local APP_DISPLAY_NAME="DragonX Wallet"
|
|
local APP_BUNDLE_ID="is.hush.dragonx"
|
|
if $DO_LITE; then
|
|
APP_DISPLAY_NAME="DragonX Wallet Lite"
|
|
APP_BUNDLE_ID="is.hush.dragonx.lite"
|
|
fi
|
|
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>${APP_DISPLAY_NAME}</string>
|
|
<key>CFBundleDisplayName</key>
|
|
<string>${APP_DISPLAY_NAME}</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>${APP_BUNDLE_ID}</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>${VERSION}</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>${VERSION}</string>
|
|
<key>CFBundleExecutable</key>
|
|
<string>${APP_BASENAME}</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="${APP_BASENAME}-${VERSION}-macOS-${MAC_ARCH}.app.zip"
|
|
if command -v zip &>/dev/null; then
|
|
(cd "$out" && zip -r "$APP_ZIP" "${APP_BASENAME}.app")
|
|
info "App zip: $out/$APP_ZIP ($(du -h "$out/$APP_ZIP" | cut -f1))"
|
|
fi
|
|
|
|
# ── Create DMG ───────────────────────────────────────────────────────────
|
|
# DMG filename matches the app bundle name (ObsidianDragon / ObsidianDragonLite).
|
|
# The mounted volume + CFBundleName keep the "DragonX Wallet" display branding
|
|
# (APP_DISPLAY_NAME above).
|
|
local DMG_BASENAME="${APP_BASENAME}"
|
|
local DMG_NAME="${DMG_BASENAME}-${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 "${APP_DISPLAY_NAME}" \
|
|
--volicon "$RESOURCES/ObsidianDragon.icns" \
|
|
--window-pos 200 120 \
|
|
--window-size 600 400 \
|
|
--icon-size 100 \
|
|
--icon "${APP_BASENAME}.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 "${APP_DISPLAY_NAME}" \
|
|
-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 "${APP_DISPLAY_NAME}" \
|
|
-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
|
|
|
|
# Reaching here means the build completed (real failures exit 1 at their point of failure).
|
|
# Exit 0 explicitly: the final `[[ -d release/mac ]] && echo` above returns non-zero on a
|
|
# non-mac build — and since set -e exempts the left side of an &&, that status would otherwise
|
|
# become the script's exit code and make a successful build report failure (e.g. to CI).
|
|
exit 0
|