From aa1194b34cc935a36afe57e001cf33a80eab9538 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Wed, 4 Feb 2026 14:02:57 +0530 Subject: [PATCH] feat: integrate TDLib bundling into macOS build process - Updated package.json to include new scripts for bundling TDLib dylib during macOS builds. - Enhanced CI workflow to automate the bundling and re-signing of the macOS app after TDLib integration, addressing "Library not loaded" issues. - Introduced a new script for managing TDLib dependencies and ensuring proper installation paths within the app bundle. --- .github/workflows/package-and-publish.yml | 24 +++ package.json | 5 +- src-tauri/scripts/bundle-tdlib-macos.sh | 217 ++++++++++++++++++++++ 3 files changed, 244 insertions(+), 2 deletions(-) create mode 100755 src-tauri/scripts/bundle-tdlib-macos.sh diff --git a/.github/workflows/package-and-publish.yml b/.github/workflows/package-and-publish.yml index 40f030595..b2c4d9926 100644 --- a/.github/workflows/package-and-publish.yml +++ b/.github/workflows/package-and-publish.yml @@ -317,6 +317,30 @@ jobs: owner: alphahumanxyz repo: alphahuman + # Bundle TDLib dylib into macOS app (fixes "Library not loaded" crash) + - name: Bundle TDLib for macOS + if: matrix.settings.platform == 'macos-latest' + run: | + chmod +x ./src-tauri/scripts/bundle-tdlib-macos.sh + ./src-tauri/scripts/bundle-tdlib-macos.sh release + + # Re-sign the app after modifying the bundle (required for notarization) + - name: Re-sign macOS app after TDLib bundling + if: matrix.settings.platform == 'macos-latest' && secrets.APPLE_SIGNING_IDENTITY != '' + env: + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + run: | + APP_BUNDLE=$(find src-tauri/target/release/bundle/macos -name "*.app" -type d | head -1) + if [ -n "$APP_BUNDLE" ]; then + echo "Re-signing app bundle: $APP_BUNDLE" + # Sign the TDLib dylib + codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" \ + "$APP_BUNDLE/Contents/Frameworks/libtdjson.1.8.29.dylib" || true + # Re-sign the entire app bundle + codesign --force --deep --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$APP_BUNDLE" || true + echo "App re-signed successfully" + fi + - name: Get file info id: file-info shell: bash diff --git a/package.json b/package.json index 7b75e3993..b5893a0ad 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,10 @@ "compile": "tsc --noEmit", "preview": "vite preview", "tauri": "tauri", - "macos:build": "tauri build --debug --bundles app", + "macos:build": "tauri build --debug --bundles app && ./src-tauri/scripts/bundle-tdlib-macos.sh debug", + "macos:build:release": "tauri build --bundles app dmg && ./src-tauri/scripts/bundle-tdlib-macos.sh release", "macos:run": "open 'src-tauri/target/debug/bundle/macos/AlphaHuman.app'", - "macos:dev": "tauri build --debug --bundles app && open 'src-tauri/target/debug/bundle/macos/AlphaHuman.app'", + "macos:dev": "tauri build --debug --bundles app && ./src-tauri/scripts/bundle-tdlib-macos.sh debug && open 'src-tauri/target/debug/bundle/macos/AlphaHuman.app'", "android:dev": "tauri android dev", "android:build": "tauri android build", "dev:app": "RUST_BACKTRACE=1 RUST_LOG=info tauri dev", diff --git a/src-tauri/scripts/bundle-tdlib-macos.sh b/src-tauri/scripts/bundle-tdlib-macos.sh new file mode 100755 index 000000000..f04f1b7e1 --- /dev/null +++ b/src-tauri/scripts/bundle-tdlib-macos.sh @@ -0,0 +1,217 @@ +#!/bin/bash +# Script to bundle TDLib dylib and its dependencies into macOS app bundle +# This fixes the "Library not loaded: @rpath/libtdjson.1.8.29.dylib" error + +set -e + +TDLIB_VERSION="1.8.29" +DYLIB_NAME="libtdjson.${TDLIB_VERSION}.dylib" + +# Determine build type (debug or release) +BUILD_TYPE="${1:-release}" +# Optional: specific target (e.g., aarch64-apple-darwin, x86_64-apple-darwin) +TARGET="${2:-}" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TAURI_DIR="$(dirname "$SCRIPT_DIR")" + +# Determine target directory (handles cross-compilation targets) +if [ -n "$TARGET" ]; then + TARGET_DIR="${TAURI_DIR}/target/${TARGET}/${BUILD_TYPE}" +else + TARGET_DIR="${TAURI_DIR}/target/${BUILD_TYPE}" +fi + +# Fallback: if target-specific dir doesn't exist, try without target +if [ ! -d "$TARGET_DIR" ]; then + TARGET_DIR="${TAURI_DIR}/target/${BUILD_TYPE}" +fi + +echo "=== TDLib macOS Bundler ===" +echo "Build type: ${BUILD_TYPE}" +echo "Target: ${TARGET:-default}" +echo "Target dir: ${TARGET_DIR}" + +# Find the app bundle - check multiple possible locations +APP_BUNDLE="" +for search_dir in \ + "${TAURI_DIR}/target/${TARGET}/${BUILD_TYPE}/bundle/macos" \ + "${TAURI_DIR}/target/${BUILD_TYPE}/bundle/macos" \ + "${TAURI_DIR}/target/release/bundle/macos" \ + "${TAURI_DIR}/target/debug/bundle/macos"; do + if [ -d "$search_dir" ]; then + found=$(find "$search_dir" -name "*.app" -type d 2>/dev/null | head -1) + if [ -n "$found" ]; then + APP_BUNDLE="$found" + break + fi + fi +done + +if [ -z "$APP_BUNDLE" ]; then + echo "Warning: No .app bundle found" + echo "Searched in:" + echo " - ${TAURI_DIR}/target/${TARGET}/${BUILD_TYPE}/bundle/macos" + echo " - ${TAURI_DIR}/target/${BUILD_TYPE}/bundle/macos" + echo "This script should be run after 'tauri build'" + exit 0 +fi + +echo "Found app bundle: ${APP_BUNDLE}" + +# Create Frameworks directory +FRAMEWORKS_DIR="${APP_BUNDLE}/Contents/Frameworks" +mkdir -p "$FRAMEWORKS_DIR" +echo "Created Frameworks directory: ${FRAMEWORKS_DIR}" + +# Find the TDLib dylib in build artifacts - search in multiple locations +DYLIB_PATH="" +for search_dir in \ + "${TAURI_DIR}/target/${TARGET}/${BUILD_TYPE}/build" \ + "${TAURI_DIR}/target/${BUILD_TYPE}/build" \ + "${TAURI_DIR}/target/release/build" \ + "${TAURI_DIR}/target/debug/build"; do + if [ -d "$search_dir" ]; then + found=$(find "$search_dir" -name "${DYLIB_NAME}" -type f 2>/dev/null | head -1) + if [ -n "$found" ]; then + DYLIB_PATH="$found" + break + fi + fi +done + +if [ -z "$DYLIB_PATH" ]; then + echo "Error: Could not find ${DYLIB_NAME} in build artifacts" + echo "Searched in target/*/build directories" + echo "Make sure TDLib was built correctly." + exit 1 +fi + +echo "Found TDLib dylib: ${DYLIB_PATH}" + +# Copy the dylib to Frameworks +cp "$DYLIB_PATH" "${FRAMEWORKS_DIR}/" +echo "Copied ${DYLIB_NAME} to Frameworks" + +# Also copy the symlink version if it exists +DYLIB_SYMLINK="${DYLIB_PATH%/*}/libtdjson.dylib" +if [ -f "$DYLIB_SYMLINK" ] || [ -L "$DYLIB_SYMLINK" ]; then + cp "$DYLIB_SYMLINK" "${FRAMEWORKS_DIR}/" 2>/dev/null || true +fi + +# Find the main binary +BINARY_NAME=$(basename "${APP_BUNDLE}" .app) +BINARY_PATH="${APP_BUNDLE}/Contents/MacOS/${BINARY_NAME}" + +if [ ! -f "$BINARY_PATH" ]; then + echo "Error: Binary not found at ${BINARY_PATH}" + exit 1 +fi + +echo "Found binary: ${BINARY_PATH}" + +# Bundled dylib path +BUNDLED_DYLIB="${FRAMEWORKS_DIR}/${DYLIB_NAME}" + +# Function to bundle a dependency and fix references +bundle_dependency() { + local dep_path="$1" + local dep_name=$(basename "$dep_path") + + if [ ! -f "$dep_path" ]; then + echo "Warning: Dependency not found: $dep_path" + return 1 + fi + + # Copy to Frameworks if not already there + if [ ! -f "${FRAMEWORKS_DIR}/${dep_name}" ]; then + echo "Bundling dependency: ${dep_name}" + cp "$dep_path" "${FRAMEWORKS_DIR}/" + chmod 755 "${FRAMEWORKS_DIR}/${dep_name}" + + # Change the dependency's install name to use @rpath + install_name_tool -id "@rpath/${dep_name}" "${FRAMEWORKS_DIR}/${dep_name}" + fi + + return 0 +} + +# Function to fix references in a library +fix_references() { + local lib_path="$1" + local lib_name=$(basename "$lib_path") + + echo "Fixing references in: ${lib_name}" + + # Get all dependencies + local deps=$(otool -L "$lib_path" | tail -n +2 | awk '{print $1}') + + for dep in $deps; do + # Skip system libraries (they're fine as-is) + if [[ "$dep" == /usr/lib/* ]] || [[ "$dep" == /System/* ]] || [[ "$dep" == "@rpath/"* ]] || [[ "$dep" == "@executable_path/"* ]]; then + continue + fi + + local dep_name=$(basename "$dep") + + # Bundle the dependency if it's from Homebrew or a non-system path + if [[ "$dep" == /opt/homebrew/* ]] || [[ "$dep" == /usr/local/* ]]; then + if bundle_dependency "$dep"; then + # Update the reference to use @rpath + install_name_tool -change "$dep" "@rpath/${dep_name}" "$lib_path" + echo " Fixed reference: ${dep_name}" + fi + fi + done +} + +# Fix the TDLib dylib's install name +echo "" +echo "Fixing dylib install name..." +install_name_tool -id "@rpath/${DYLIB_NAME}" "$BUNDLED_DYLIB" + +# Bundle OpenSSL and other dependencies from TDLib +echo "" +echo "Bundling TDLib dependencies..." +fix_references "$BUNDLED_DYLIB" + +# Fix any cross-references between bundled dependencies +echo "" +echo "Fixing cross-references between dependencies..." +for dylib in "${FRAMEWORKS_DIR}"/*.dylib; do + if [ -f "$dylib" ]; then + fix_references "$dylib" + fi +done + +# Add rpath to the binary to look in Frameworks +echo "" +echo "Adding rpath to binary..." +# First, try to delete existing rpath if it exists (ignore errors) +install_name_tool -delete_rpath "@executable_path/../Frameworks" "$BINARY_PATH" 2>/dev/null || true + +# Add the correct rpath +install_name_tool -add_rpath "@executable_path/../Frameworks" "$BINARY_PATH" + +# Verify the changes +echo "" +echo "=== Verification ===" +echo "" +echo "Bundled libraries:" +ls -la "${FRAMEWORKS_DIR}/" + +echo "" +echo "TDLib dylib dependencies (should all be @rpath or system libs):" +otool -L "$BUNDLED_DYLIB" + +echo "" +echo "Binary library dependencies:" +otool -L "$BINARY_PATH" | grep -i tdjson || echo "No tdjson reference found" + +echo "" +echo "Binary rpaths:" +otool -l "$BINARY_PATH" | grep -A3 "cmd LC_RPATH" | head -8 || true + +echo "" +echo "=== TDLib bundling complete ===" +echo "The app bundle at ${APP_BUNDLE} now includes TDLib and its dependencies."