Last active
September 16, 2024 02:41
-
-
Save NQNStudios/7145bcf6621891f5176c8caa165d6b93 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
import subprocess | |
import sys | |
from os.path import join, basename | |
import os | |
from pathlib import Path | |
app_bundle = sys.argv[1] | |
dry_run = '--dry' in sys.argv | |
# Accept the inner binary path as valid input: | |
if '/Contents/MacOS/' in app_bundle: | |
app_bundle = app_bundle[0:app_bundle.find('/Contents/MacOS/')] | |
# Allow a / after .app: | |
if app_bundle.endswith('/'): | |
app_bundle = app_bundle[0:-1] | |
app_binary = join(app_bundle, 'Contents/MacOS', basename(app_bundle).split(".")[0]) | |
app_frameworks = join(app_bundle, 'Contents/Frameworks') | |
binaries_unfiltered = [app_binary] + [join(app_frameworks, library) for library in os.listdir(app_frameworks)] | |
binaries = [] | |
# Extend .framework paths to the actual binary | |
for binary in binaries_unfiltered: | |
if binary.endswith('.framework'): | |
current_symlink = join(binary, 'Versions/Current/' + basename(binary).split(".")[0]) | |
current_realpath = str(Path(current_symlink).resolve()) | |
binaries.append(current_realpath) | |
else: | |
binaries.append(binary) | |
dependency_map = {binary:subprocess.check_output(["otool", "-L", binary], text=True).splitlines()[1:] for binary in binaries} | |
for binary, dependencies in dependency_map.items(): | |
print(binary) | |
is_framework = '.framework' in binary | |
if binary == app_binary: | |
# check if a new rpath is needed | |
new_rpath = '@loader_path/../Frameworks' | |
has_rpath = False | |
lines = reversed(subprocess.check_output(["otool", "-l", binary], text=True).splitlines()) | |
for line in lines: | |
if line.strip().startswith('path '): | |
existing_rpath = line.strip()[len('path '):line.strip().find('(') - 1] | |
if existing_rpath == new_rpath: | |
has_rpath = True | |
break | |
if not has_rpath: | |
print('\tAdding rpath: ' + new_rpath) | |
if not dry_run: | |
print('\t' + subprocess.check_output(['install_name_tool', '-add_rpath', new_rpath, binary], text=True)) | |
for dependency in dependencies: | |
end_index = dependency.index('(') | |
dep = dependency[0:end_index].strip() | |
dep_short_name = dep[dep.rfind('/')+1:] | |
if '.framework' in dep: | |
index = dep.find('.framework') | |
dep_short_name = dep[dep.rfind('/', 0, index)+1:] | |
if dep == binary or basename(dep_short_name) == basename(binary): | |
continue | |
for key in dependency_map: | |
if key.endswith(dep_short_name): | |
if binary == app_binary: | |
new_path = f'@rpath/{dep_short_name}' | |
if new_path == dep: | |
continue | |
print(f'\t{dep} -> {new_path}') | |
if not dry_run: | |
print('\t' + subprocess.check_output(['install_name_tool', '-change', dep, new_path, binary], text=True)) | |
else: | |
new_path = f'@loader_path/{dep_short_name}' | |
if is_framework: | |
new_path = f'@loader_path/../../../{dep_short_name}' | |
if new_path == dep: | |
continue | |
print(f'\t{dep} -> {new_path}') | |
if not dry_run: | |
print('\t' + subprocess.check_output(['install_name_tool', '-change', dep, new_path, binary], text=True)) | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment