123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #!/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))
|