Merge pull request #2536 from endrift/deploy-mac
Tools: Use non-CMake tool for fixing up OS X bundles
This commit is contained in:
commit
9ed7e3bd3e
|
@ -66,7 +66,7 @@ if(APPLE)
|
||||||
message(\"(Note: This is only necessary to produce a redistributable binary.\")
|
message(\"(Note: This is only necessary to produce a redistributable binary.\")
|
||||||
message(\"To skip, pass -DSKIP_POSTPROCESS_BUNDLE=1 to cmake.)\")
|
message(\"To skip, pass -DSKIP_POSTPROCESS_BUNDLE=1 to cmake.)\")
|
||||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||||
fixup_bundle(\"${BUNDLE_PATH}\" \"\" \"\")
|
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/Tools/deploy-mac.py -p platforms/libqcocoa.dylib \"${BUNDLE_PATH}\")
|
||||||
file(INSTALL ${CMAKE_SOURCE_DIR}/Data/Sys
|
file(INSTALL ${CMAKE_SOURCE_DIR}/Data/Sys
|
||||||
DESTINATION ${BUNDLE_PATH}/Contents/Resources
|
DESTINATION ${BUNDLE_PATH}/Contents/Resources
|
||||||
)
|
)
|
||||||
|
|
|
@ -157,7 +157,7 @@ elseif(wxWidgets_FOUND)
|
||||||
message(\"(Note: This is only necessary to produce a redistributable binary.\")
|
message(\"(Note: This is only necessary to produce a redistributable binary.\")
|
||||||
message(\"To skip, pass -DSKIP_POSTPROCESS_BUNDLE=1 to cmake.)\")
|
message(\"To skip, pass -DSKIP_POSTPROCESS_BUNDLE=1 to cmake.)\")
|
||||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||||
fixup_bundle(\"${BUNDLE_PATH}\" \"\" \"\")
|
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/Tools/deploy-mac.py \"${BUNDLE_PATH}\")
|
||||||
file(INSTALL \"${CMAKE_SOURCE_DIR}/Data/Sys\"
|
file(INSTALL \"${CMAKE_SOURCE_DIR}/Data/Sys\"
|
||||||
DESTINATION \"${BUNDLE_PATH}/Contents/Resources\"
|
DESTINATION \"${BUNDLE_PATH}/Contents/Resources\"
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
#!/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))
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(os.path.join(args.bundle, 'Contents/Frameworks/'))
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
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:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(os.path.join(args.bundle, 'Contents/PlugIns/'))
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
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))
|
Loading…
Reference in New Issue