Skip to content

Commit

Permalink
Extend macOS fix-install-names.py script
Browse files Browse the repository at this point in the history
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 fix-install-names.py further to handle these tasks.

Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
  • Loading branch information
stefanha committed Jan 2, 2017
1 parent 9e60738 commit cc1a831
Showing 1 changed file with 45 additions and 11 deletions.
56 changes: 45 additions & 11 deletions qtclient/installer/macosx/fix-install-names.py
Expand Up @@ -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]
sys.exit(1)

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'
else:
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:
return
processed.add(filename)

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:
continue

dependency = m.group(1)
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:
process_macho(path)

def main(argv):
global executable_dir

if len(argv) != 2:
usage(argv)

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)
process_macho(path)

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)
dylibs.append(path)

for filename in dylibs:
process_dylib(filename)
process_macho(path)

return 0

Expand Down

0 comments on commit cc1a831

Please sign in to comment.