all repos — mgba @ 0c6c6fdcbc4fa5d81b1d5b07028bab570108f017

mGBA Game Boy Advance Emulator

tools/deploy-mac.py (view raw)

  1#!/usr/bin/env python
  2from __future__ import print_function
  3import argparse
  4import errno
  5import os
  6import re
  7import shutil
  8import subprocess
  9
 10qtPath = None
 11verbose = False
 12
 13def splitPath(path):
 14	folders = []
 15	while True:
 16		path, folder = os.path.split(path)
 17		if folder != '':
 18			folders.append(folder)
 19		else:
 20			if path != '':
 21				folders.append(path)
 22			break
 23	folders.reverse()
 24	return folders
 25
 26def joinPath(path):
 27	return reduce(os.path.join, path, '')
 28
 29def findFramework(path):
 30	child = []
 31	while path and not path[-1].endswith('.framework'):
 32		child.append(path.pop())
 33	child.reverse()
 34	return path, child
 35
 36def findQtPath(path):
 37	parent, child = findFramework(splitPath(path))
 38	return joinPath(parent[:-2])
 39
 40def makedirs(path):
 41	split = splitPath(path)
 42	accum = []
 43	split.reverse()
 44	while split:
 45		accum.append(split.pop())
 46		newPath = joinPath(accum)
 47		if newPath == '/':
 48			continue
 49		try:
 50			os.mkdir(newPath)
 51		except OSError as e:
 52			if e.errno != errno.EEXIST:
 53				raise
 54
 55
 56def parseOtoolLine(line, execPath, root):
 57	if not line.startswith('\t'):
 58		return None, None, None, None
 59	line = line[1:]
 60	match = re.match('([@/].*) \(compatibility version.*\)', line)
 61	path = match.group(1)
 62	split = splitPath(path)
 63	newExecPath = ['@executable_path', '..', 'Frameworks']
 64	newPath = execPath[:-1]
 65	newPath.append('Frameworks')
 66	if split[:3] == ['/', 'usr', 'lib'] or split[:2] == ['/', 'System']:
 67		return None, None, None, None
 68	if split[0] == '@executable_path':
 69		split[:1] = execPath
 70	if split[0] == '/' and not os.access(joinPath(split), os.F_OK):
 71		split[:1] = root
 72	try:
 73		oldPath = joinPath(split)
 74		while True:
 75			linkPath = os.readlink(os.path.abspath(oldPath))
 76			oldPath = os.path.join(os.path.dirname(oldPath), linkPath)
 77	except OSError as e:
 78		if e.errno != errno.EINVAL:
 79			raise
 80		split = splitPath(oldPath)
 81	isFramework = False
 82	if not split[-1].endswith('.dylib'):
 83		isFramework = True
 84		split, framework = findFramework(split)
 85	newPath.append(split[-1])
 86	newExecPath.append(split[-1])
 87	if isFramework:
 88		newPath.extend(framework)
 89		newExecPath.extend(framework)
 90		split.extend(framework)
 91	newPath = joinPath(newPath)
 92	newExecPath = joinPath(newExecPath)
 93	return joinPath(split), newPath, path, newExecPath
 94
 95def updateMachO(bin, execPath, root):
 96	global qtPath
 97	otoolOutput = subprocess.check_output([otool, '-L', bin])
 98	toUpdate = []
 99	for line in otoolOutput.split('\n'):
100		oldPath, newPath, oldExecPath, newExecPath = parseOtoolLine(line, execPath, root)
101		if not newPath:
102			continue
103		if os.access(newPath, os.F_OK):
104			if verbose:
105				print('Skipping copying {}, already done.'.format(oldPath))
106		elif os.path.abspath(oldPath) != os.path.abspath(newPath):
107			if verbose:
108				print('Copying {} to {}...'.format(oldPath, newPath))
109			parent, child = os.path.split(newPath)
110			makedirs(parent)
111			shutil.copy2(oldPath, newPath)
112			os.chmod(newPath, 0o644)
113		toUpdate.append((newPath, oldExecPath, newExecPath))
114		if not qtPath and 'Qt' in oldPath:
115			qtPath = findQtPath(oldPath)
116			if verbose:
117				print('Found Qt path at {}.'.format(qtPath))
118	for path, oldExecPath, newExecPath in toUpdate:
119		if path != bin:
120			updateMachO(path, execPath, root)
121			if verbose:
122				print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath))
123			subprocess.check_call([installNameTool, '-change', oldExecPath, newExecPath, bin])
124		else:
125			if verbose:
126				print('Updating Mach-O id from {} to {}...'.format(oldExecPath, newExecPath))
127			subprocess.check_call([installNameTool, '-id', newExecPath, bin])
128
129if __name__ == '__main__':
130	parser = argparse.ArgumentParser()
131	parser.add_argument('-R', '--root', metavar='ROOT', default='/', help='root directory to search')
132	parser.add_argument('-I', '--install-name-tool', metavar='INSTALL_NAME_TOOL', default='install_name_tool', help='path to install_name_tool')
133	parser.add_argument('-O', '--otool', metavar='OTOOL', default='otool', help='path to otool')
134	parser.add_argument('-p', '--qt-plugins', metavar='PLUGINS', default='', help='Qt plugins to include (comma-separated)')
135	parser.add_argument('-v', '--verbose', action='store_true', default=False, help='output more information')
136	parser.add_argument('bundle', help='application bundle to deploy')
137	args = parser.parse_args()
138
139	otool = args.otool
140	installNameTool = args.install_name_tool
141	verbose = args.verbose
142
143	try:
144		shutil.rmtree(os.path.join(args.bundle, 'Contents/Frameworks/'))
145	except OSError as e:
146		if e.errno != errno.ENOENT:
147			raise
148
149	for executable in os.listdir(os.path.join(args.bundle, 'Contents/MacOS')):
150		if executable.endswith('.dSYM'):
151			continue
152		fullPath = os.path.join(args.bundle, 'Contents/MacOS/', executable)
153		updateMachO(fullPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))
154	if args.qt_plugins:
155		try:
156			shutil.rmtree(os.path.join(args.bundle, 'Contents/PlugIns/'))
157		except OSError as e:
158			if e.errno != errno.ENOENT:
159				raise
160		makedirs(os.path.join(args.bundle, 'Contents/PlugIns'))
161		makedirs(os.path.join(args.bundle, 'Contents/Resources'))
162		with open(os.path.join(args.bundle, 'Contents/Resources/qt.conf'), 'w') as conf:
163			conf.write('[Paths]\nPlugins = PlugIns\n')
164		plugins = args.qt_plugins.split(',')
165		for plugin in plugins:
166			plugin = plugin.strip()
167			kind, plug = os.path.split(plugin)
168			newDir = os.path.join(args.bundle, 'Contents/PlugIns/', kind)
169			makedirs(newDir)
170			newPath = os.path.join(newDir, plug)
171			shutil.copy2(os.path.join(qtPath, 'plugins', plugin), newPath)
172			updateMachO(newPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))