From 2d9ec99c11068de936acc14cc7cb11c027271084 Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Wed, 26 Oct 2016 16:58:21 -0700 Subject: [PATCH 1/2] Mac build improvements Move closer to allowing an "out of the box" distributable build for Mac: * fix the icon for the .app * link SDL2 statically, a PR has been sent to the original repo here: https://github.com/tcbrindle/sdl2-cmake-scripts/pull/9 * link SFML statically TODO: For a releasable build on OS X, system libs must be linked dynamically while third party libs are linked statically or bundled. The goal is to link them statically. The two remaining libs that need static linking are PNG and wX. For PNG a similar approach to the one used for SDL2 using pkg-config should work fine and be simple to implement. For wX things are more complicated. The default build of wX does not include static libs, I will need to submit a PR for Homebrew to change the default build to include both dynamic and static versions. --- .gitignore | 2 + CMakeLists.txt | 30 ++++++++------- CMakeScripts/FindSDL2.cmake | 75 ++++++++++++++++++++++++++++++++----- src/wx/CMakeLists.txt | 16 ++++++-- 4 files changed, 97 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 7f07a2a68..539471f9e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ src/wx/cmdtab.cpp src/wx/wxvbam.xrs build/ +# vim swap files +*.sw? diff --git a/CMakeLists.txt b/CMakeLists.txt index 11a3a1222..62d889457 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,13 +65,6 @@ ELSE() ADD_DEFINITIONS(-DNDEBUG) ENDIF() -# Fill in SDLMAIN_LIBRARY on OS X manually to avoid using SDLMain.m -# OS X users will have to compile and install SDL from source. -if( APPLE AND ENABLE_SDL ) - ADD_DEFINITIONS (-DwxMAC_USE_CORE_GRAPHICS ) - SET(SDL2MAIN_LIBRARY "-lSDL2main") -endif( APPLE AND ENABLE_SDL ) - # Add support for Homebrew, MacPorts and Fink on OS X # as well as for ObjectiveC code IF(APPLE) @@ -107,7 +100,7 @@ IF(APPLE) # and compile as Objective-C++ for ObjectiveC #ifdefs SET(CMAKE_CXX_COMPILE_OBJECT " -x objective-c++ -o -c ") -ENDIF() +ENDIF(APPLE) # We do not support amd64 asm yet IF((ENABLE_ASM_CORE OR ENABLE_ASM_SCALERS OR ENABLE_MMX) @@ -129,17 +122,26 @@ if( ENABLE_ASM_SCALERS ) endif( ENABLE_ASM_SCALERS ) # Look for some dependencies using CMake scripts -FIND_PACKAGE ( ZLIB REQUIRED ) -FIND_PACKAGE ( PNG REQUIRED ) -FIND_PACKAGE ( OpenGL REQUIRED ) -FIND_PACKAGE ( SDL2 REQUIRED ) +FIND_PACKAGE(ZLIB REQUIRED) +FIND_PACKAGE(OpenGL REQUIRED) + +# TODO: make static on mac +FIND_PACKAGE(PNG REQUIRED) + +IF(APPLE) + SET(SDL2_STATIC ON) +ENDIF(APPLE) + +FIND_PACKAGE(SDL2 REQUIRED) +ADD_DEFINITIONS(${SDL2_DEFINITIONS}) if( ENABLE_LINK ) - if( WIN32 ) + if(WIN32 OR APPLE) set(SFML_STATIC_LIBRARIES TRUE) - endif( WIN32 ) + endif(WIN32 OR APPLE) FIND_PACKAGE ( SFML 2 COMPONENTS network system ) endif( ENABLE_LINK ) + # set the standard libraries all ports use SET(VBAMCORE_LIBS vbamcore diff --git a/CMakeScripts/FindSDL2.cmake b/CMakeScripts/FindSDL2.cmake index 026dc34e7..78f823963 100644 --- a/CMakeScripts/FindSDL2.cmake +++ b/CMakeScripts/FindSDL2.cmake @@ -1,9 +1,22 @@ - # This module defines # SDL2_LIBRARY, the name of the library to link against # SDL2_FOUND, if false, do not try to link to SDL2 # SDL2_INCLUDE_DIR, where to find SDL.h # +# If you have pkg-config, these extra variables are also defined: +# SDL2_DEFINITIONS, extra CFLAGS +# SDL2_EXTRA_LIBS, extra link libs +# SDL2_LINKER_FLAGS, extra link flags +# +# The latter two are automatically added to SDL2_LIBRARY +# +# To use them, add code such as: +# +# # SET(SDL2_STATIC ON) # if you want to link SDL2 statically +# FIND_PACKAGE(SDL2 REQUIRED) +# ADD_DEFINITIONS(${SDL2_DEFINITIONS}) +# TARGET_LINK_LIBRARIES(your-executable-target ${SDL2_LIBRARY} ...) +# # This module responds to the the flag: # SDL2_BUILDING_LIBRARY # If this is defined, then no SDL2main will be linked in because @@ -12,6 +25,8 @@ # module will attempt to locate and set the the proper link flags # as part of the returned SDL2_LIBRARY variable. # +# If you want to link SDL2 statically, set SDL2_STATIC to ON. +# # Don't forget to include SDLmain.h and SDLmain.m your project for the # OS X framework based version. (Other versions link to -lSDL2main which # this module will try to find on your behalf.) Also for OS X, this @@ -47,7 +62,7 @@ # SDL2_LIBRARY to override this selection or set the CMake environment # CMAKE_INCLUDE_PATH to modify the search paths. # -# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# Note that the header path has changed from SDL3/SDL.h to just SDL.h # This needed to change because "proper" SDL convention # is #include "SDL.h", not . This is done for portability # reasons because not all systems place things in SDL2/ (see FreeBSD). @@ -86,11 +101,21 @@ FIND_PATH(SDL2_INCLUDE_DIR SDL.h PATHS ${SDL2_SEARCH_PATHS} ) +SET(CURRENT_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + +IF(SDL2_STATIC) + IF(WIN32) + SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a) + ELSE(WIN32) + SET(CMAKE_FIND_LIBRARY_SUFFIXES .a) + ENDIF(WIN32) +ENDIF(SDL2_STATIC) + FIND_LIBRARY(SDL2_LIBRARY_TEMP NAMES SDL2 HINTS $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib + PATH_SUFFIXES lib64 lib lib/x64 lib/x86 PATHS ${SDL2_SEARCH_PATHS} ) @@ -104,12 +129,15 @@ IF(NOT SDL2_BUILDING_LIBRARY) NAMES SDL2main HINTS $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib + PATH_SUFFIXES lib64 lib lib/x64 lib/x86 PATHS ${SDL2_SEARCH_PATHS} ) ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") ENDIF(NOT SDL2_BUILDING_LIBRARY) +SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CURRENT_FIND_LIBRARY_SUFFIXES}) +UNSET(CURRENT_FIND_LIBRARY_SUFFIXES) + # SDL2 may require threads on your system. # The Apple build may not need an explicit flag because one of the # frameworks may already provide it. @@ -118,11 +146,10 @@ IF(NOT APPLE) FIND_PACKAGE(Threads) ENDIF(NOT APPLE) -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) +# MinGW needs an additional link flag, -mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") + SET(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "mwindows for MinGW") ENDIF(MINGW) IF(SDL2_LIBRARY_TEMP) @@ -155,6 +182,37 @@ IF(SDL2_LIBRARY_TEMP) SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) ENDIF(MINGW) + # Add some stuff from pkg-config, if available + IF(NOT PKG_CONFIG_EXECUTABLE) + FIND_PACKAGE(PkgConfig QUIET) + ENDIF(NOT PKG_CONFIG_EXECUTABLE) + + IF(PKG_CONFIG_EXECUTABLE) + # get any definitions + EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --cflags-only-other sdl2 OUTPUT_VARIABLE SDL2_DEFINITIONS) + + SET(SDL2_DEFINITIONS ${SDL2_DEFINITIONS} CACHE STRING "Extra CFLAGS for SDL2 from pkg-config") + + # get any extra stuff needed for linking + IF(NOT SDL2_STATIC) + EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --libs-only-other sdl2 OUTPUT_VARIABLE SDL2_LINKER_FLAGS_RAW OUTPUT_STRIP_TRAILING_WHITESPACE) + + EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --libs-only-l sdl2 OUTPUT_VARIABLE SDL2_EXTRA_LIBS_RAW OUTPUT_STRIP_TRAILING_WHITESPACE) + ELSE(NOT SDL2_STATIC) + EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --static --libs-only-other sdl2 OUTPUT_VARIABLE SDL2_LINKER_FLAGS_RAW OUTPUT_STRIP_TRAILING_WHITESPACE) + EXECUTE_PROCESS(COMMAND ${PKG_CONFIG_EXECUTABLE} --static --libs-only-l sdl2 OUTPUT_VARIABLE SDL2_EXTRA_LIBS_RAW OUTPUT_STRIP_TRAILING_WHITESPACE) + ENDIF(NOT SDL2_STATIC) + + STRING(REGEX REPLACE "[^ ]+SDL2[^ ]*" "" SDL2_EXTRA_LIBS_RAW2 "${SDL2_EXTRA_LIBS_RAW}") + STRING(REGEX REPLACE " +" ";" SDL2_EXTRA_LIBS "${SDL2_EXTRA_LIBS_RAW2}") + STRING(REGEX REPLACE " +" ";" SDL2_LINKER_FLAGS "${SDL2_LINKER_FLAGS_RAW}") + + SET(SDL2_LINKER_FLAGS ${SDL2_LINKER_FLAGS} CACHE STRING "Linker flags for linking SDL2") + SET(SDL2_EXTRA_LIBS ${SDL2_EXTRA_LIBS} CACHE STRING "Extra libraries for linking SDL2") + + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${SDL2_EXTRA_LIBS} ${SDL2_LINKER_FLAGS}) + ENDIF(PKG_CONFIG_EXECUTABLE) + # Set the final string here so the GUI reflects the final state. SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") # Set the temp variable to INTERNAL so it is not seen in the CMake GUI @@ -166,4 +224,3 @@ message("") INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) - diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index d492fe7f6..027c2c1e5 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -10,6 +10,10 @@ if( WIN32 ) endif( WIN32 ) option( ENABLE_OPENAL "Enable OpenAL for the wxWidgets port" ON ) +IF(APPLE) + ADD_DEFINITIONS(-DwxMAC_USE_CORE_GRAPHICS) +ENDIF(APPLE) + if( NOT ENABLE_CAIRO ) ADD_DEFINITIONS (-DNO_CAIRO) endif( NOT ENABLE_CAIRO ) @@ -38,6 +42,10 @@ IF(CMAKE_BUILD_TYPE STREQUAL "Debug") ENDIF() ENDIF() +IF(APPLE) + SET(wxWidgets_USE_STATIC ON) +ENDIF(APPLE) + SET(wxWidgets_USE_UNICODE ON) # adv is for wxAboutBox # xml, html is for xrc @@ -189,7 +197,9 @@ ENDIF( WIN32 ) link_directories( ${CMAKE_BINARY_DIR} ) -SET(VBAM_ICON ${CMAKE_CURRENT_SOURCE_DIR}/icons/vbam.icns) +SET(VBAM_ICON vbam.icns) + +SET(VBAM_ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/icons/${VBAM_ICON}) ADD_EXECUTABLE ( visualboyadvance-m @@ -198,7 +208,7 @@ ADD_EXECUTABLE ( ${SRC_WX} ${HDR_WX} ${RES_WX} - ${VBAM_ICON} + ${VBAM_ICON_PATH} ${CM_STUFF} ) @@ -221,7 +231,7 @@ if(APPLE) # this should set ROM file types correctly 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_SOURCE_FILES_PROPERTIES(${VBAM_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + SET_SOURCE_FILES_PROPERTIES(${VBAM_ICON_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif(APPLE) SET(WX_EXE_NAME visualboyadvance-m-wx${CMAKE_EXECUTABLE_SUFFIX}) From d18afb982f2dc1f9e02c1969087024456a2e8cb8 Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Thu, 27 Oct 2016 22:06:06 -0700 Subject: [PATCH 2/2] 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 --- .gitignore | 3 + src/wx/CMakeLists.txt | 19 +- src/wx/tools/osx/third_party_libs_tool | 262 +++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 1 deletion(-) create mode 100755 src/wx/tools/osx/third_party_libs_tool diff --git a/.gitignore b/.gitignore index 539471f9e..9cd04a357 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ build/ # vim swap files *.sw? + +# mac finder crap +*.DS_Store diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index 027c2c1e5..9821028a5 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -1,8 +1,17 @@ #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. SET( CMAKE_CXX_FLAGS -std=gnu++11 ) + # 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 ) # not yet implemented 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}) +IF(APPLE) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) + SET(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks") +ENDIF(APPLE) + ADD_EXECUTABLE ( visualboyadvance-m 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(MACOSX_BUNDLE_ICON_FILE ${VBAM_ICON}) 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 "$/../..") endif(APPLE) SET(WX_EXE_NAME visualboyadvance-m-wx${CMAKE_EXECUTABLE_SUFFIX}) diff --git a/src/wx/tools/osx/third_party_libs_tool b/src/wx/tools/osx/third_party_libs_tool new file mode 100755 index 000000000..c3f8c85d4 --- /dev/null +++ b/src/wx/tools/osx/third_party_libs_tool @@ -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 "$@"