#!/bin/bash

# A macOS executable linked to dynamic libraries will either link to a full path
# or an rpath (a series of which will be specified in the binary). In order to
# have a self-contained app bundle, we need to copy the external libraries into
# the bundle, and then update the executable to use @executable_path to point
# within the bundle.

if [[ $(uname) != "Darwin" ]]; then
    echo "This script is only useful for macOS"
    exit 1
fi

if [[ $# != 1 ]]; then
    echo "Usage: $0 path/to/name.app"
    exit 1
fi

# Find the path to the actual executable in the app bundle
appBundle=$1
exeDir="$appBundle/Contents/MacOS"
exe=$(find "$exeDir" -type f -perm +111 | grep -v dylib)
if [[ ! -f $exe ]]; then
    echo "No executable file in app bundle ($appBundle/Contents/MacOS)"
    exit 1
fi

# Find the rpath paths
rpaths=$(otool -l "$exe" | grep "path " | awk '{print $2}')
if [[ $rpaths != "" ]]; then
    echo "@rpath:"
    for rp in $rpaths; do
        echo "    $rp"
    done
fi

# Set IFS so that newlines don't become spaces; helps parsing the otool -L output
IFS='
'

# Copy any external libraries and change the library paths to @executable_path
libs=$(otool -L "$exe" | grep -v "$exe" | grep -v /usr/lib | grep -v /System | awk '{ print $1; }')
for lib in $libs; do
    if [[ ${lib:0:1} != "@" ]]; then # external library with a regular path
        libname=$(basename $lib)
        # copy the external library, resolve symlink chain
        cp -aRL "$lib" "$exeDir/$libname"
        # change its path in the executable
        newpath="@executable_path/$libname"
        echo "$lib -> $newpath"
        install_name_tool -change "$lib" "$newpath" "$exe"

    elif [[ $lib == @rpath/* ]]; then # external library with @rpath
        libname=${lib:7}
        # copy the external library. Since it uses an rpath, we need to
        # prepend each rpath to see which one gives a valid path
        for rp in $rpaths; do
            if [[ -f "$rp/$libname" ]]; then
                cp -aRL "$rp/$libname" "$exeDir/$libname"
                break
            fi
        done

        # change its path in the executable
        newpath="@executable_path/$libname"
        echo "$lib -> $newpath"
        install_name_tool -change "$lib" "$newpath" $exe
    fi
done

# Remove rpaths
for rp in $rpaths; do
    install_name_tool -delete_rpath "$rp" "$exe"
done
