summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Maltese <michaeljosephmaltese@gmail.com>2017-02-03 16:10:59 -0800
committerMichael Maltese <michaeljosephmaltese@gmail.com>2017-02-03 22:27:19 -0800
commit38816bf51da004e85ab75e9aa7e884df653bf508 (patch)
tree54c3f48863615892a50fd7d8d877a775dc5c8c56
parent529dc6aa5330cdd50e17ccbdbe35600f29573c7a (diff)
CMake: use BundleUtilities to fix up Dolphin.app
-rw-r--r--CMakeLists.txt3
-rw-r--r--CMakeTests/DolphinPostprocessBundle.cmake46
-rw-r--r--Source/Core/DolphinQt2/CMakeLists.txt19
-rw-r--r--Source/Core/DolphinQt2/qt.conf0
-rw-r--r--Source/Core/DolphinWX/CMakeLists.txt7
-rwxr-xr-xTools/deploy-mac.py162
6 files changed, 61 insertions, 176 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4a340db313..c3a2fcae8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -255,9 +255,6 @@ else()
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
- # This doesn't play well with the packaging script that doesn't understand @rpath
- set(CMAKE_MACOSX_RPATH OFF)
-
if(NOT OSX_USE_DEFAULT_SEARCH_PATH)
# Hack up the path to prioritize the path to built-in OS libraries to
# increase the chance of not depending on a bunch of copies of them
diff --git a/CMakeTests/DolphinPostprocessBundle.cmake b/CMakeTests/DolphinPostprocessBundle.cmake
new file mode 100644
index 0000000000..4d2eee175e
--- /dev/null
+++ b/CMakeTests/DolphinPostprocessBundle.cmake
@@ -0,0 +1,46 @@
+# This module can be used in two different ways.
+#
+# When invoked as `cmake -P DolphinPostprocessBundle.cmake`, it fixes up an
+# application folder to be standalone. It bundles all required libraries from
+# the system and fixes up library IDs. Any additional shared libraries, like
+# plugins, that are found under Contents/MacOS/ will be made standalone as well.
+#
+# When called with `include(DolphinPostprocessBundle)`, it defines a helper
+# function `dolphin_postprocess_bundle` that sets up the command form of the
+# module as a post-build step.
+
+if(CMAKE_GENERATOR)
+ # Being called as include(DolphinPostprocessBundle), so define a helper function.
+ set(_DOLPHIN_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
+ function(dolphin_postprocess_bundle target)
+ add_custom_command(TARGET ${target} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -DDOLPHIN_BUNDLE_PATH="$<TARGET_FILE_DIR:${target}>/../.."
+ -P "${_DOLPHIN_POSTPROCESS_BUNDLE_MODULE_LOCATION}"
+ )
+ endfunction()
+ return()
+endif()
+
+get_filename_component(DOLPHIN_BUNDLE_PATH "${DOLPHIN_BUNDLE_PATH}" REALPATH)
+message(STATUS "Fixing up application bundle: ${DOLPHIN_BUNDLE_PATH}")
+
+# Make sure to fix up any additional shared libraries (like plugins) that are
+# needed.
+file(GLOB_RECURSE extra_libs "${DOLPHIN_BUNDLE_PATH}/Contents/MacOS/*.dylib")
+
+# BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which
+# makes it sometimes break on libraries that do weird things with @rpath. Specify
+# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
+# is fixed and in our minimum CMake version.
+set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib")
+
+# BundleUtilities is overly verbose, so disable most of its messages
+function(message)
+ if(NOT ARGV MATCHES "^STATUS;")
+ _message(${ARGV})
+ endif()
+endfunction()
+
+include(BundleUtilities)
+set(BU_CHMOD_BUNDLE_ITEMS ON)
+fixup_bundle("${DOLPHIN_BUNDLE_PATH}" "${extra_libs}" "${extra_dirs}")
diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt
index feb0bb0cf4..877d1ff02d 100644
--- a/Source/Core/DolphinQt2/CMakeLists.txt
+++ b/Source/Core/DolphinQt2/CMakeLists.txt
@@ -49,13 +49,16 @@ if(APPLE)
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)
- # Update library references to make the bundle portable
- add_custom_command(TARGET ${DOLPHINQT2_BINARY} POST_BUILD
- COMMAND echo "Fixing up application bundle: ${BUNDLE_PATH}"
- COMMAND echo ${CMAKE_SOURCE_DIR}/Tools/deploy-mac.py $<TARGET_FILE_DIR:${DOLPHINQT2_BINARY}>/../..
- )
+ # Copy qt.conf into the bundle
+ target_sources(${DOLPHINQT2_BINARY} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/qt.conf")
+ set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/qt.conf" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+ # Copy Qt plugins into the bundle
+ get_target_property(qtcocoa_location Qt5::QCocoaIntegrationPlugin LOCATION)
+ target_sources(${DOLPHINQT2_BINARY} PRIVATE "${qtcocoa_location}")
+ set_source_files_properties("${qtcocoa_location}" PROPERTIES MACOSX_PACKAGE_LOCATION MacOS/platforms)
- # Copy resources in the bundle
+ # Copy resources into the bundle
file(GLOB_RECURSE resources RELATIVE "${CMAKE_SOURCE_DIR}/Data" "${CMAKE_SOURCE_DIR}/Data/Sys/*")
foreach(res ${resources})
target_sources(${DOLPHINQT2_BINARY} PRIVATE "${CMAKE_SOURCE_DIR}/Data/${res}")
@@ -64,6 +67,10 @@ if(APPLE)
MACOSX_PACKAGE_LOCATION "Resources/${resdir}")
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
endforeach()
+
+ # Update library references to make the bundle portable
+ include(DolphinPostprocessBundle)
+ dolphin_postprocess_bundle(${DOLPHINQT2_BINARY})
else()
install(TARGETS ${DOLPHINQT2_BINARY} RUNTIME DESTINATION ${bindir})
endif()
diff --git a/Source/Core/DolphinQt2/qt.conf b/Source/Core/DolphinQt2/qt.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Source/Core/DolphinQt2/qt.conf
diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt
index 1d4b958b04..020b7bbd59 100644
--- a/Source/Core/DolphinWX/CMakeLists.txt
+++ b/Source/Core/DolphinWX/CMakeLists.txt
@@ -176,11 +176,8 @@ if(wxWidgets_FOUND)
endforeach()
# Update library references to make the bundle portable
- add_custom_command(TARGET ${DOLPHIN_EXE} POST_BUILD
- COMMAND echo "Fixing up application bundle: ${BUNDLE_PATH}"
- COMMAND ${CMAKE_SOURCE_DIR}/Tools/deploy-mac.py $<TARGET_FILE_DIR:${DOLPHIN_EXE}>/../..
- )
-
+ include(DolphinPostprocessBundle)
+ dolphin_postprocess_bundle(${DOLPHIN_EXE})
# Install bundle into systemwide /Applications directory.
install(TARGETS ${DOLPHIN_EXE} DESTINATION /Applications)
diff --git a/Tools/deploy-mac.py b/Tools/deploy-mac.py
deleted file mode 100755
index 674b663525..0000000000
--- a/Tools/deploy-mac.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/env python
-from __future__ import print_function
-import argparse
-import errno
-import os
-import re
-import shutil
-import subprocess
-
-qtPath = None
-verbose = False
-
-def splitPath(path):
- folders = []
- while True:
- path, folder = os.path.split(path)
- if folder != '':
- folders.append(folder)
- else:
- if path != '':
- folders.append(path)
- break
- folders.reverse()
- return folders
-
-def joinPath(path):
- return reduce(os.path.join, path, '')
-
-def findFramework(path):
- child = []
- while path and not path[-1].endswith('.framework'):
- child.append(path.pop())
- child.reverse()
- return path, child
-
-def findQtPath(path):
- parent, child = findFramework(splitPath(path))
- return joinPath(parent[:-2])
-
-def makedirs(path):
- split = splitPath(path)
- accum = []
- split.reverse()
- while split:
- accum.append(split.pop())
- newPath = joinPath(accum)
- if newPath == '/':
- continue
- try:
- os.mkdir(newPath)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
-
-
-def parseOtoolLine(line, execPath, root):
- if not line.startswith('\t'):
- return None, None, None, None
- line = line[1:]
- match = re.match('([@/].*) \(compatibility version.*\)', line)
- path = match.group(1)
- split = splitPath(path)
- newExecPath = ['@executable_path', '..', 'Frameworks']
- newPath = execPath[:-1]
- newPath.append('Frameworks')
- if split[:3] == ['/', 'usr', 'lib'] or split[:2] == ['/', 'System']:
- return None, None, None, None
- if split[0] == '@executable_path':
- split[:1] = execPath
- if split[0] == '/' and not os.access(joinPath(split), os.F_OK):
- split[:1] = root
- oldPath = os.path.realpath(joinPath(split))
- split = splitPath(oldPath)
- isFramework = False
- if not split[-1].endswith('.dylib'):
- isFramework = True
- split, framework = findFramework(split)
- newPath.append(split[-1])
- newExecPath.append(split[-1])
- if isFramework:
- newPath.extend(framework)
- newExecPath.extend(framework)
- split.extend(framework)
- newPath = joinPath(newPath)
- newExecPath = joinPath(newExecPath)
- return joinPath(split), newPath, path, newExecPath
-
-def updateMachO(bin, execPath, root):
- global qtPath
- otoolOutput = subprocess.check_output([otool, '-L', bin])
- toUpdate = []
- for line in otoolOutput.split('\n'):
- oldPath, newPath, oldExecPath, newExecPath = parseOtoolLine(line, execPath, root)
- if not newPath:
- continue
- if os.access(newPath, os.F_OK):
- if verbose:
- print('Skipping copying {}, already done.'.format(oldPath))
- newPath = None
- elif os.path.abspath(oldPath) != os.path.abspath(newPath):
- if verbose:
- print('Copying {} to {}...'.format(oldPath, newPath))
- parent, child = os.path.split(newPath)
- makedirs(parent)
- shutil.copy2(oldPath, newPath)
- os.chmod(newPath, 0o644)
- toUpdate.append((newPath, oldExecPath, newExecPath))
- if not qtPath and 'Qt' in oldPath:
- qtPath = findQtPath(oldPath)
- if verbose:
- print('Found Qt path at {}.'.format(qtPath))
- args = [installNameTool]
- for path, oldExecPath, newExecPath in toUpdate:
- if path != bin:
- if path:
- updateMachO(path, execPath, root)
- if verbose:
- print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath))
- args.extend(['-change', oldExecPath, newExecPath])
- else:
- if verbose:
- print('Updating Mach-O id from {} to {}...'.format(oldExecPath, newExecPath))
- args.extend(['-id', newExecPath])
- args.append(bin)
-
- # Check we actually have to update some paths
- if len(args) > 2:
- subprocess.check_call(args)
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument('-R', '--root', metavar='ROOT', default='/', help='root directory to search')
- parser.add_argument('-I', '--install-name-tool', metavar='INSTALL_NAME_TOOL', default='install_name_tool', help='path to install_name_tool')
- parser.add_argument('-O', '--otool', metavar='OTOOL', default='otool', help='path to otool')
- parser.add_argument('-p', '--qt-plugins', metavar='PLUGINS', default='', help='Qt plugins to include (comma-separated)')
- parser.add_argument('-v', '--verbose', action='store_true', default=False, help='output more information')
- parser.add_argument('bundle', help='application bundle to deploy')
- args = parser.parse_args()
-
- otool = args.otool
- installNameTool = args.install_name_tool
- verbose = args.verbose
-
- for executable in os.listdir(os.path.join(args.bundle, 'Contents/MacOS')):
- if executable.endswith('.dSYM'):
- continue
- fullPath = os.path.join(args.bundle, 'Contents/MacOS/', executable)
- updateMachO(fullPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))
- if args.qt_plugins:
- makedirs(os.path.join(args.bundle, 'Contents/PlugIns'))
- makedirs(os.path.join(args.bundle, 'Contents/Resources'))
- with open(os.path.join(args.bundle, 'Contents/Resources/qt.conf'), 'w') as conf:
- conf.write('[Paths]\nPlugins = PlugIns\n')
- plugins = args.qt_plugins.split(',')
- for plugin in plugins:
- plugin = plugin.strip()
- kind, plug = os.path.split(plugin)
- newDir = os.path.join(args.bundle, 'Contents/PlugIns/', kind)
- makedirs(newDir)
- newPath = os.path.join(newDir, plug)
- shutil.copy2(os.path.join(qtPath, 'plugins', plugin), newPath)
- updateMachO(newPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))