fix-install-names.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. from pathlib import Path
  2. import sys
  3. import argparse
  4. from collections import deque
  5. import subprocess
  6. import shutil
  7. def main(argv=tuple(sys.argv[1:])):
  8. arg_parser = argparse.ArgumentParser(description='Fix third party library paths in .app bundles.')
  9. arg_parser.add_argument('bundle', metavar='BUNDLE', type=str, nargs=1)
  10. arguments = arg_parser.parse_args(argv)
  11. bundle_path = Path(arguments.bundle[0])
  12. framework_path = bundle_path / 'Contents' / 'Frameworks'
  13. framework_libs = set(file.name for file in framework_path.glob('*.dylib')).union(set(file.name for file in framework_path.glob('*.so')))
  14. libs_to_fix = deque()
  15. libs_to_fix.extend(file for file in bundle_path.glob('**/*.dylib'))
  16. libs_to_fix.extend(file for file in bundle_path.glob('**/*.so'))
  17. while libs_to_fix:
  18. lib = libs_to_fix.popleft()
  19. # find the dependencies of the library
  20. result = subprocess.check_output(['otool', '-L', str(lib.resolve())], stderr=subprocess.STDOUT).decode('utf-8')
  21. for dependency in result.splitlines():
  22. dependency = dependency.strip().lstrip()
  23. if dependency.startswith('/usr/local'):
  24. # cut off trailing compatibility string
  25. dependency = Path(dependency.split(' (compatibility')[0])
  26. # if somehow macdeployqt didn't copy the lib for us, we do a manual copy
  27. if dependency.name not in framework_libs:
  28. shutil.copy(str(dependency.resolve()), str(framework_path.resolve()))
  29. framework_libs.add(dependency.name)
  30. # add the newly added library in to the to fix queue
  31. libs_to_fix.append(framework_path / dependency.name)
  32. print((framework_path / dependency.name).resolve())
  33. print(f'Copied {dependency} to {framework_path / dependency.name}')
  34. # now we fix the path using install_name_tool
  35. target = f'@executable_path/../Frameworks/{dependency.name}'
  36. print(f'Fixing dependency {dependency} of {lib} to {target}')
  37. subprocess.run(['install_name_tool', '-id', target, lib])
  38. subprocess.run(['install_name_tool', '-change', str(dependency), target, lib])
  39. if __name__ == '__main__':
  40. main()