L'utilisation otool (récursive) pour trouver les bibliothèques partagées nécessaires pour une application

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

Question

J'ai une application Cocoa qui otool utilise pour trouver des bibliothèques partagées nécessaires qu'une application a besoin pour fonctionner correctement. Par exemple, supposons que je lance otool -L sur une application qui utilise QTKit.framework. Je reçois une liste des bibliothèques partagées utilisées par le programme (y compris les cadres de base comme Cocoa.framework et 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

Ce qui montre que l'application utilise QTKit.framework. Toutefois, si je l'utilise « otool -L » à nouveau sur le binaire pour QTKit.framework (/System/Library/Frameworks/QTKit.framework/Versions/A/QTKit) Je reçois ceci:

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

Cela montre une charge plus de cadres que la sortie otool d'origine sur l'application binaire montré. Est-il possible d'avoir otool récursivement, ce qui signifie qu'il saisit les cadres que les besoins d'applications, puis se et va chercher dans chacun de ces cadres pour les dépendances?

Était-ce utile?

La solution

Non, vous devez exécuter otool à plusieurs reprises, ou incorporer son code d'analyse syntaxique ( ici ). Ne pas oublier la manipulation @executable_path.

Ici, il est en Python (sans @executable_path, canonicalisation, ou les noms de fichiers avec-espaces pris en charge), car cela était plus facile que d'essayer de débogage 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

Autres conseils

Voici ma solution que j'utilise pour fixer la sortie macdeployqt lors de l'utilisation des bibliothèques Homebrew installé. Ce que j'ai trouvé que macdeployqt fait un bon travail de mettre les dylibs dans le dossier-cadre, mais il ne parvient pas à corriger les chemins.

https://github.com/jveitchmichaelis/deeplabel/blob/master /fix_paths_mac.py

J'ai modifié le script de Nicholas d'être un peu plus facile à utiliser - il corrige @executable_path, @rpath et @loader_path. Ce n'est pas exactement le code de production, mais il a laissé me exécuter des applications sur d'autres Mac sans dépendances déjà installés.

Exécuter avec: python fix_paths_mac.py ./path/to/your.app/Contents/MacOS/your_exe. à savoir le pointer vers le binaire à l'intérieur d'un paquet d'application et il va comprendre le reste.

Je suis supposé que la plupart des problèmes viennent de choses liées à /usr/local. Donc, si le code détecte qu'il ya une dépendance qui pointe vers un fichier dans /usr/local, il va fixer les chemins correctement. Vous pouvez modifier l'instruction pass copier dans un fichier si ce n'est pas dans le dossier Frameworks, mais je n'ai pas rencontré une situation où il y a un manque dylib, il est juste liée mal.

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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top