otool (재귀 적으로)을 사용하여 앱에 필요한 공유 라이브러리를 찾습니다.
-
19-09-2019 - |
문제
OTool을 사용하여 앱이 제대로 작동 해야하는 필수 공유 라이브러리를 찾는 Cocoa 앱이 있습니다. 예를 들어, qtkit.framework를 사용하는 앱에서 otool -l을 실행한다고 가정 해보십시오. 프로그램에서 사용하는 공유 라이브러리 목록 (Cocoa.framework 및 AppKit.framework와 같은 기본 프레임 워크 포함)을 얻습니다.
/System/Library/Frameworks/QTKit.framework/Versions/A/QTKit (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.0.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.0.0)
..... and so on for a bunch of other frameworks
앱이 qtkit.framework를 사용 함을 보여줍니다. 그러나 qtkit.framework (/system/library/frameworks/qtkit.framework/versions/a/qtkit)의 바이너리에서 "otool -l"을 다시 사용하면 다음과 같습니다.
/System/Library/Frameworks/QTKit.framework/Versions/A/QTKit (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox (compatibility version 1.0.0, current version 1.0.0)
/System/Library/PrivateFrameworks/CoreMedia.framework/Versions/A/CoreMedia (compatibility version 1.0.0, current version 1.0.0)
/System/Library/PrivateFrameworks/MediaToolbox.framework/Versions/A/MediaToolbox (compatibility version 1.0.0, current version 1.0.0)
/System/Library/PrivateFrameworks/VideoToolbox.framework/Versions/A/VideoToolbox (compatibility version 1.0.0, current version 1.0.0)
/System/Library/PrivateFrameworks/CoreMediaIOServices.framework/Versions/A/CoreMediaIOServices (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.0.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1038.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime (compatibility version 1.0.0, current version 1584.0.0)
/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.6.0)
/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox (compatibility version 1.0.0, current version 435.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 123.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 44.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 550.0.0)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 38.0.0)
/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo (compatibility version 1.2.0, current version 1.6.0)
이는 App Binary의 원래 Otool 출력이 보여준로드 더 많은 프레임 워크를 보여줍니다. Otool을 재귀 적으로 실행하는 방법이 있습니까?
해결책
아니요, OTool을 반복적으로 실행하거나 구문 분석 코드를 통합해야합니다 (여기). 취급을 잊지 마십시오 @executable_path
.
여기에는 파이썬이 있습니다 (없음 @executable_path
, pseudocode를 디버그하는 것보다 쉬웠 기 때문에, 정식화 또는 공간이 지원되는 파일 이름) :
import subprocess
def otool(s):
o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)
for l in o.stdout:
if l[0] == '\t':
yield l.split(' ', 1)[0][1:]
need = set(['/Applications/iTunes.app/Contents/MacOS/iTunes'])
done = set()
while need:
needed = set(need)
need = set()
for f in needed:
need.update(otool(f))
done.update(needed)
need.difference_update(done)
for f in sorted(done):
print f
다른 팁
여기에 수정하는 데 사용하는 솔루션이 있습니다 macdeployqt
홈 브루 설치 라이브러리를 사용할 때의 출력. 내가 찾은 것은 그 것입니다 macdeployqt
Dylibs를 프레임 워크 폴더에 넣는 데 도움이되지만 경로를 수정하지 못합니다.
https://github.com/jveitchmichaelis/deeplabel/blob/master/fix_paths_mac.py
나는 Nicholas의 스크립트를 좀 더 사용할 수 있도록 수정했습니다. @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)