Browse files

Extend macOS script

macdeployqt seems to work even less in Qt 5.7.1.  It still copies all
library dependencies into the bundle but does not reliably update shared
library IDs or change absolute paths to bundle relative paths.

Extend further to handle these tasks.

Signed-off-by: Stefan Hajnoczi <>
  • Loading branch information...
stefanha committed Jan 2, 2017
1 parent 9e60738 commit cc1a831579926f3952e918ab6b7c6eb99d2c49a5
Showing with 45 additions and 11 deletions.
  1. +45 −11 qtclient/installer/macosx/
@@ -25,45 +25,79 @@
import os
import re
DEPENDENCY_RE = re.compile(r'\s+(/usr/local/.+\.dylib) \(compatibility version [^,]+, current version [^)]+\)')
DEPENDENCY_RE = re.compile(r'\s+(/usr/local/[^\s]+) \(compatibility version [^,]+, current version [^)]+\)')
executable_paths = {}
processed = set()
def usage(argv):
print 'usage: %s APP_BUNDLE_PATH' % argv[0]
def process_dylib(filename):
def get_executable_rel_path(filename):
components = filename.split(os.sep)
if 'lib' in components:
idx = components.index('lib')
libdir = 'Frameworks'
elif 'plugins' in components:
idx = components.index('plugins')
libdir = 'PlugIns'
raise ValueError('get_bundle_path: unable to handle file %s' % filename)
return os.path.join('..', libdir, *components[idx + 1:])
def process_macho(filename):
if filename in processed:
print 'Processing %s...' % filename
# Update shared library id
output = subprocess.check_output(['otool', '-D', filename], stderr=subprocess.STDOUT)
macho_id = output.splitlines()[-1]
if macho_id.startswith('/usr/local/'):
new_id = os.path.join('@executable_path', '..', get_executable_rel_path(macho_id))
print 'Updating shared library id %s to %s...' % (macho_id, new_id)
subprocess.check_call(['install_name_tool', '-id', new_id, filename])
# Change dependencies to bundle-relative paths
pending = set()
output = subprocess.check_output(['otool', '-L', filename], stderr=subprocess.STDOUT)
for line in output.splitlines():
m = DEPENDENCY_RE.match(line)
if not m:
dependency =
new_path = executable_paths[os.path.basename(dependency)]
rel_path = get_executable_rel_path(dependency)
pending.add(os.path.join(executable_dir, rel_path))
new_path = os.path.join('@executable_path', rel_path)
print 'Changing %s to %s...' % (dependency, new_path)
subprocess.check_call(['install_name_tool', '-change', dependency, new_path, filename])
for path in pending:
def main(argv):
global executable_dir
if len(argv) != 2:
executable_dir = os.path.join(argv[1], 'Contents', 'MacOS')
dylibs = []
for dirpath, dirnames, filenames in os.walk(executable_dir):
for filename in filenames:
path = os.path.join(dirpath, filename)
for dirpath, dirnames, filenames in os.walk(argv[1]):
for filename in filter(lambda s: s.endswith('.dylib'), filenames):
path = os.path.join(dirpath, filename)
rel_path = os.path.relpath(path, executable_dir)
executable_paths[filename] = os.path.join('@executable_path', rel_path)
for filename in dylibs:
return 0

0 comments on commit cc1a831

Please sign in to comment.