diff --git a/CMakeLists.txt b/CMakeLists.txt index 58ffce8..5570548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,13 @@ if(DRAGONX_ENABLE_LITE_BACKEND) set_target_properties(dragonx_lite_backend PROPERTIES IMPORTED_LOCATION "${DRAGONX_LITE_BACKEND_LIBRARY}" ) + if(APPLE) + # The Rust backend's TLS stack (security-framework / core-foundation crates) + # references Secure Transport (SSL*) + CoreFoundation symbols. Link the frameworks + # that provide them, or the static lib leaves ~130 symbols undefined at link time. + set_property(TARGET dragonx_lite_backend APPEND PROPERTY + INTERFACE_LINK_LIBRARIES "-framework Security" "-framework CoreFoundation") + endif() if(DRAGONX_LITE_BACKEND_INCLUDE_DIR) if(NOT IS_DIRECTORY "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}") message(FATAL_ERROR "DRAGONX_LITE_BACKEND_INCLUDE_DIR does not exist: ${DRAGONX_LITE_BACKEND_INCLUDE_DIR}") @@ -284,6 +291,15 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/webmproject/libwebp.git GIT_TAG v1.4.0 GIT_SHALLOW TRUE + # libwebp's cpu.cmake applies -mno-sse2/-mno-sse4.1 to its scalar reference DSP + # files when it can't probe SSE support. Under a macOS universal build + # (-arch arm64;x86_64) that probe fails, so the flags land on the x86_64 slice, + # where -mno-sse2 disables _Float16 and breaks the SDK's . Neutralize + # those disable flags (SSE2 is x86_64 baseline). Portable + idempotent; a no-op + # for single-arch Linux/Windows/x86_64 builds. See cmake/patch-libwebp-simd.cmake. + PATCH_COMMAND ${CMAKE_COMMAND} + -DCPU_CMAKE=/cmake/cpu.cmake + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patch-libwebp-simd.cmake ) set(WEBP_LINK_STATIC ON CACHE BOOL "" FORCE) set(WEBP_BUILD_ANIM_UTILS OFF CACHE BOOL "" FORCE) diff --git a/build.sh b/build.sh index 3995da0..2acb73b 100755 --- a/build.sh +++ b/build.sh @@ -131,7 +131,7 @@ fi # 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:]]\+\([0-9][0-9.]*\).*/\1/p' "$_cml" | head -1) +_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) @@ -901,8 +901,26 @@ build_release_mac() { fi info "macOS cross-compiler: $OSXCROSS_CXX (arch: $MAC_ARCH)" else - # Native macOS: build universal binary (arm64 + x86_64) - MAC_ARCH="universal" + # 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 @@ -990,7 +1008,7 @@ TOOLCHAIN 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 + 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 @@ -1001,13 +1019,13 @@ TOOLCHAIN "$SCRIPT_DIR/scripts/fetch-libsodium.sh" fi - info "Configuring (native universal arm64+x86_64) ..." + info "Configuring (native macOS, arch: $MAC_ARCHS) ..." 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" \ + -DCMAKE_OSX_ARCHITECTURES="$MAC_ARCHS" \ "${CMAKE_LITE_ARGS[@]}" fi diff --git a/cmake/patch-libwebp-simd.cmake b/cmake/patch-libwebp-simd.cmake new file mode 100644 index 0000000..dfb1b63 --- /dev/null +++ b/cmake/patch-libwebp-simd.cmake @@ -0,0 +1,31 @@ +# patch-libwebp-simd.cmake — portable, idempotent FetchContent patch for libwebp. +# +# libwebp's cmake/cpu.cmake compiles its scalar *reference* DSP files with the +# SSE-disable flags "-mno-sse4.1;-mno-sse2" whenever it can't positively detect +# SSE support. Under a macOS *universal* build (-arch arm64;x86_64) the per-arch +# SSE flag probe fails (a flag valid for x86_64 is invalid for arm64), so those +# disable flags get applied to the x86_64 slice. clang gates the _Float16 type on +# SSE2 for x86_64, and the macOS 15+/26 SDK's declares _Float16 math +# functions unconditionally — so any TU including fails to compile with +# "_Float16 is not supported on this target". +# +# SSE2 is part of the x86_64 baseline ABI, so disabling it on the reference files +# is unnecessary on every platform we target. Blanking the SSE entries (indices +# must stay aligned with WEBP_SIMD_FLAGS) fixes the universal build and is a no-op +# for single-arch Linux/Windows/x86_64 builds. Idempotent: re-running is a no-op. +if(NOT DEFINED CPU_CMAKE OR NOT EXISTS "${CPU_CMAKE}") + message(FATAL_ERROR "patch-libwebp-simd: cpu.cmake not found at '${CPU_CMAKE}'") +endif() + +file(READ "${CPU_CMAKE}" _contents) +string(REPLACE + "set(SIMD_DISABLE_FLAGS \"-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa\")" + "set(SIMD_DISABLE_FLAGS \";;;-mno-dspr2;;-mno-msa\")" + _patched "${_contents}") + +if(_patched STREQUAL _contents) + message(STATUS "patch-libwebp-simd: no change (already patched or pattern absent)") +else() + file(WRITE "${CPU_CMAKE}" "${_patched}") + message(STATUS "patch-libwebp-simd: neutralized x86 SSE-disable flags in cpu.cmake") +endif() diff --git a/scripts/build-lite-backend-artifact.sh b/scripts/build-lite-backend-artifact.sh index 66292c1..f83072c 100755 --- a/scripts/build-lite-backend-artifact.sh +++ b/scripts/build-lite-backend-artifact.sh @@ -1,5 +1,17 @@ #!/usr/bin/env bash +# This script uses bash 4+ features (mapfile, safe empty-array expansion under +# `set -u`). macOS ships bash 3.2, so re-exec under a newer bash when one is +# present (Homebrew), and fail with a clear message otherwise. +if [ "${BASH_VERSINFO:-0}" -lt 4 ]; then + for _newer_bash in /opt/homebrew/bin/bash /usr/local/bin/bash; do + [ -x "$_newer_bash" ] && exec "$_newer_bash" "$0" "$@" + done + echo "ERROR: build-lite-backend-artifact.sh requires bash 4+ (found ${BASH_VERSION:-unknown})." >&2 + echo " On macOS: brew install bash" >&2 + exit 1 +fi + set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/setup.sh b/setup.sh index 4020627..e488168 100755 --- a/setup.sh +++ b/setup.sh @@ -133,7 +133,7 @@ pkgs_core_arch="base-devel cmake git pkg-config libxkbcommon wayland libsodium curl autoconf automake libtool wget python xxd" -pkgs_core_macos="cmake python xxd" +pkgs_core_macos="bash cmake python xxd" # Windows cross-compile (from Linux) pkgs_win_debian="mingw-w64 zip"