Вот мое решение, которое я использую для исправления вывода macdeployqt
при использовании библиотек, установленных Homebrew. Что я обнаружил, так это то, что macdeployqt
хорошо справляется с помещением dylibs в папку Framework, но не может исправить пути.
https://github.com/jveitchmichaelis/deeplabel/blob/master/fix_paths_mac.py
Я изменил сценарий Николаса, чтобы он стал немного более удобным - он исправляет @executable_path
, @rpath
и @loader_path
. Это не совсем рабочий код, но он позволяет мне запускать приложения на других компьютерах Mac без каких-либо зависимостей.
Запуск с: python fix_paths_mac.py ./path/to/your.app/Contents/MacOS/your_exe
. то есть, укажите его на двоичный файл внутри пакета приложения, и он выяснит остальное.
Я предположил, что большинство проблем связано с тем, что связано с /usr/local
. Поэтому, если код обнаружит, что существует зависимость, указывающая на файл в /usr/local
, он исправит пути соответствующим образом. Вы можете изменить оператор pass
для копирования в файл, если он не находится в папке Frameworks
, но я не сталкивался с ситуацией, когда отсутствует отсутствующий dylib, он просто неверно связан.
import subprocess
import os
import sys
from shutil import copyfile
executable = sys.argv[1]
app_folder = os.path.join(*executable.split('/')[:-3])
content_folder = os.path.join(app_folder, "Contents")
framework_path = os.path.join(content_folder, "Frameworks")
print(executable)
print("Working in {} ".format(app_folder))
def file_in_folder(file, folder):
return os.path.exists(os.path.join(folder, file))
def otool(s):
o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)
for l in o.stdout:
l = l.decode()
if l[0] == '\t':
path = l.split(' ', 1)[0][1:]
if "@executable_path" in path:
path = path.replace("@executable_path", "")
# fudge here to strip /../ from the start of the path.
path = os.path.join(content_folder, path[4:])
if "@loader_path" in path:
path = path.replace("@loader_path", framework_path)
if "@rpath" in path:
path = path.replace("@rpath", framework_path)
dependency_dylib_name = os.path.split(path)[-1]
if "usr/local" in path:
if app_folder in s:
print("Warning: {} depends on {}".format(s, path))
if file_in_folder(dependency_dylib_name, framework_path):
print("Dependent library {} is already in framework folder".format(dependency_dylib_name))
print("Running install name tool to fix {}.".format(s))
if dependency_dylib_name == os.path.split(s)[-1]:
_ = subprocess.Popen(['install_name_tool', '-id', os.path.join("@loader_path", dependency_dylib_name), s], stdout=subprocess.PIPE)
_ = subprocess.Popen(['install_name_tool', '-change', path, os.path.join("@loader_path", dependency_dylib_name), s], stdout=subprocess.PIPE)
else:
# Potentially you could copy in the offending dylib here.
pass
yield path
need = set([executable])
done = set()
while need:
needed = set(need)
need = set()
for f in needed:
need.update(otool(f))
done.update(needed)
need.difference_update(done)