باستخدام Otool (متكرر) للعثور على مكتبات مشتركة يحتاجها تطبيق

StackOverflow https://stackoverflow.com/questions/1517614

سؤال

لدي تطبيق Cocoa يستخدم Otool للعثور على مكتبات مشتركة مطلوبة يحتاج التطبيق إلى العمل بشكل صحيح. على سبيل المثال، أقول أنني أركض Otool -L على تطبيق يستخدم Qtkit.Framework. أحصل على قائمة بالمكتبات المشتركة المستخدمة من قبل البرنامج (بما في ذلك الأطر الأساسية مثل 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. ومع ذلك، إذا كنت أستخدم "Otool -l" مرة أخرى على ثنائي ل Qtkit.Framework (/System /Library/frameworks/qtkit.framework/versions/a/qtkit) أحصل على هذا:

/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)

هذا يدل على تحميل المزيد من الأطر التي أظهرت إخراج Otool الأصلي على التطبيق ثنائي. هل هناك طريقة للحصول على تشغيل Otool بشكل متكرر، مما يعني أنه يمسك الأطر التي يحتاجها التطبيق، ثم يذهب وتبحث في كل من هذه الأطر للتبعية؟

هل كانت مفيدة؟

المحلول

لا، سيكون عليك تشغيل Otool مرارا وتكرارا، أو دمج رمز تحليله (هنا). لا تنسى التعامل @executable_path.

هنا في بيثون (بدون @executable_path, أو canonicalization، أو أسماء الملفات مع المساحات المدعومة)، لأن هذا كان أسهل من محاولة تصحيح الأخشاب pseud

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 في مجلد Framework، ولكنها فشل في إصلاح المسارات.

https://github.com/jveitchmichaelis/deeplabel/blob/master/fix_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 مجلد، لكنني لم أصادف موقفا فيه ديليب مفقود، إنه مرتبط فقط خطأ.

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top