support for fully independent .app build on Mac

The resulting Mac wX .app build is now completely independent and
redistributable, only needs to be codesigned.

Necessary dylibs are bundled and linked in a POST_BUILD step using
third_party_libs_tool (included) for which I created a separate repo
here as well:

http://github.com/rkitover/mac-third-party-libs-tool

Turn off Cairo on Mac because it does not work for now.

Set RPATH on the executable to @loader_path/../Frameworks, the bundling
tool also does this.

Update .gitignore for Finder .DS_Store files.

TOOD:

* write a ./quickbuild for Mac and other platforms such as msys2 and
  linux
This commit is contained in:
Rafael Kitover 2016-10-27 22:06:06 -07:00
parent 2d9ec99c11
commit d18afb982f
3 changed files with 283 additions and 1 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ build/
# vim swap files # vim swap files
*.sw? *.sw?
# mac finder crap
*.DS_Store

View File

@ -1,8 +1,17 @@
#Do not use this file directly. Always use the top level CMakeLists.txt file #Do not use this file directly. Always use the top level CMakeLists.txt file
# This build is much easier if we just do it here. # This build is much easier if we just do it here.
SET( CMAKE_CXX_FLAGS -std=gnu++11 ) SET( CMAKE_CXX_FLAGS -std=gnu++11 )
# not yet implemented # not yet implemented
option( ENABLE_CAIRO "Enable Cairo rendering for the wxWidgets port" ON ) IF(APPLE)
# does not work, no reason to link to it
SET(CAIRO_DEFAULT OFF)
ELSE(APPLE)
SET(CAIRO_DEFAULT ON)
ENDIF(APPLE)
option(ENABLE_CAIRO "Enable Cairo rendering for the wxWidgets port" ${CAIRO_DEFAULT})
if( WIN32 ) if( WIN32 )
# not yet implemented # not yet implemented
option( ENABLE_DIRECT3D "Enable Direct3D rendering for the wxWidgets port" ON ) option( ENABLE_DIRECT3D "Enable Direct3D rendering for the wxWidgets port" ON )
@ -201,6 +210,11 @@ SET(VBAM_ICON vbam.icns)
SET(VBAM_ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/icons/${VBAM_ICON}) SET(VBAM_ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/icons/${VBAM_ICON})
IF(APPLE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
SET(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
ENDIF(APPLE)
ADD_EXECUTABLE ( ADD_EXECUTABLE (
visualboyadvance-m visualboyadvance-m
WIN32 WIN32
@ -232,6 +246,9 @@ if(APPLE)
SET_PROPERTY(TARGET visualboyadvance-m APPEND PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/wxplist.in) SET_PROPERTY(TARGET visualboyadvance-m APPEND PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/wxplist.in)
SET(MACOSX_BUNDLE_ICON_FILE ${VBAM_ICON}) SET(MACOSX_BUNDLE_ICON_FILE ${VBAM_ICON})
SET_SOURCE_FILES_PROPERTIES(${VBAM_ICON_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) SET_SOURCE_FILES_PROPERTIES(${VBAM_ICON_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
ADD_CUSTOM_COMMAND(TARGET visualboyadvance-m POST_BUILD
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/osx/third_party_libs_tool "$<TARGET_FILE_DIR:visualboyadvance-m>/../..")
endif(APPLE) endif(APPLE)
SET(WX_EXE_NAME visualboyadvance-m-wx${CMAKE_EXECUTABLE_SUFFIX}) SET(WX_EXE_NAME visualboyadvance-m-wx${CMAKE_EXECUTABLE_SUFFIX})

View File

@ -0,0 +1,262 @@
#!/bin/sh
version=0.1
main() {
# parse options
list=
while [ $# -gt 0 ]; do
case "$1" in
-h|--help|--usage)
usage
quit 0
;;
-v|--version)
echo "third_party_libs_tool $version"
quit 0
;;
-l|--list)
list=1
shift
;;
*)
break
;;
esac
done
if [ $# -ne 1 ]; then
usage
quit 1
fi
mktmp
app_bundle=$(echo "$1" | fully_resolve_links | sed 's,/*$,,')
case "$app_bundle" in
*.app|*.APP)
if [ ! -d "$app_bundle" ]; then
usage
quit 1
fi
;;
*)
usage
quit 1
;;
esac
set --
OLDIFS=$IFS
IFS='
'
for file in $(find "$app_bundle/Contents/MacOS" -type f); do
case "$file" in
*.dylib)
set -- "$@" "$file"
;;
*)
[ -x "$file" ] && set -- "$@" "$file"
;;
esac
done
IFS=$OLDIFS
frameworks="$app_bundle/Contents/Frameworks"
mkdir -p "$frameworks" 2>/dev/null
lib_scan "$@" | fully_resolve_links | sort -u | \
while read lib; do
if [ -n "$list" ]; then
echo "$lib"
else
cp -f "$lib" "$frameworks" 2>/dev/null
fi
done
# fix dynamic link info in executables and just copied libs
[ -z "$list" ] && relink_all "$@"
quit 0
}
usage() {
cat <<'EOF'
Usage: third_party_libs_tool [OPTION] BUNDLE.app
Bundle third party dylibs into BUNDLE.app and fix up linkages.
Binaries are searched for in BUNDLE.app/Contents/MacOS .
The dylibs are copied into BUNDLE.app/Contents/Frameworks .
-h, --help, --usage Show this help screen and exit.
-v, --version Show version information and exit.
-l, --list Only list dylibs used by binaries, do not copy or link.
Examples:
third_party_libs_tool ./MyApp.app # bundle and link ./MyApp.app
third_party_libs_tool --list ./MyApp.app # list third party libs used by ./MyApp.app
Project homepage and documentation: <http://github.com/rkitover/mac-third-party-libs-tool>
EOF
}
mktmp() {
tmp="/tmp/third_party_libs_tool_$$"
mkdir "$tmp" || quit 1
chmod 700 "$tmp" 2>/dev/null
trap "quit 1" PIPE HUP INT QUIT ILL TRAP KILL BUS TERM
}
quit() {
[ -n "$tmp" ] && rm -rf "$tmp" 2>/dev/null
exit ${1:-0}
}
lib_scan() {
for bin in "$@"; do
case "$bin" in
*.dylib)
;;
*)
[ ! -x "$bin" ] && continue
;;
esac
set --
OLDIFS=$IFS
IFS='
'
for lib in $(otool -L "$bin" 2>/dev/null | sed -n 's/^ \([^ ]*\).*/\1/p' | grep -v '^/System/' | grep -v '^/usr/lib/'); do
[ "$lib" = "$bin" ] && continue
echo "$lib"
set -- "$@" "$lib"
done
IFS=$OLDIFS
# recurse
[ $# -ne 0 ] && lib_scan "$@"
done
}
fully_resolve_links() {
while read -r file; do
OLDIFS=$IFS
IFS='
'
path=
for part in $(echo "$file" | sed 's,^/*,,; s,/*$,,; s,//*,\
,g'); do
path=$(resolve_link "$path/$part")
done
IFS=$OLDIFS
# remove '..' path parts
while :; do
case "$path" in
*/../*|*/..)
path=$(echo "$path" | sed 's,//*[^/][^/]*//*\.\./*,/,g')
;;
*)
break
;;
esac
done
echo "$path"
done
}
resolve_link() {
file="$1"
while [ -h "$file" ]; do
ls0=`ls -l "$file"`
new_link=`expr "$ls0" : '.* -> \(.*\)$'`
if expr "$new_link" : '/.*' > /dev/null; then
file="$new_link"
else
file="${file%/*}"/"$new_link"
fi
done
echo "$file"
}
relink_all() {
for exe in "$@"; do
# dylib search path for executable
install_name_tool -add_rpath '@loader_path/../Frameworks' "$exe"
OLDIFS=$IFS
IFS='
'
set --
for lib in $(find "$app_bundle/Contents/Frameworks" -name '*.dylib'); do
set -- "$@" "$lib"
done
IFS=$OLDIFS
for lib in "$@"; do
# make lib writable
chmod u+w "$lib"
# change id of lib
install_name_tool -id "@rpath/${lib##*/}" "$lib"
# set search path of lib
install_name_tool -add_rpath '@loader_path/../Frameworks' "$lib"
# relink executable and all other libs to this lib
for target in "$exe" "$@"; do
relink "$lib" "$target"
done
done
done
}
relink() {
lib=$1
target=$2
lib_basename=${lib##*/}
lib_basename_unversioned_re=$(echo "$lib_basename" | sed 's/[0-9.-]*\.dylib$//; s/\./\\./g')
# remove full path and version of lib in executable
lib_link_path=$(
otool -l "$target" 2>/dev/null | \
sed -n 's,^ *name \(/.*/*'"$lib_basename_unversioned_re"'[0-9.-]*\.dylib\) (offset .*,\1,p' | \
head -1
)
if [ -n "$lib_link_path" ]; then
install_name_tool -change "$lib_link_path" "@rpath/$lib_basename" "$target"
fi
}
# try with sudo in case it fails,
# also suppress duplicate path errors
install_name_tool() {
out_file="$tmp/install_name_tool.out"
if ! command install_name_tool "$@" >"$out_file" 2>&1; then
if grep -Eq -i 'permission denied|bad file descriptor' "$out_file"; then
if ! command sudo install_name_tool "$@" >"$out_file" 2>&1; then
cat "$out_file" >&2
return 1
fi
elif ! grep -Eq -i 'would duplicate path' "$out_file"; then
cat "$out_file" >&2
return 1
fi
fi
return 0
}
main "$@"