Usando otool (recursivamente) para encontrar las bibliotecas compartidas necesarias para una aplicación

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

Pregunta

Tengo una aplicación que utiliza cacao otool para encontrar las bibliotecas compartidas requeridas que una aplicación necesita para funcionar correctamente. Por ejemplo, digamos que corro -L otool en una aplicación que utiliza QTKit.framework. Puedo obtener una lista de las bibliotecas compartidas utilizadas por el programa (incluyendo los marcos básicos como Cocoa.framework y 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

Lo que demuestra que la aplicación utiliza QTKit.framework. Sin embargo si uso "-L otool" de nuevo en el binario para QTKit.framework (/System/Library/Frameworks/QTKit.framework/Versions/A/QTKit) me sale esto:

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

que muestra una carga más marcos que la salida otool original en el binario aplicación mostró. ¿Hay una manera de tener otool ejecutar de forma recursiva, lo que significa que agarra los marcos que las necesidades de aplicaciones, a continuación, entra y busca en cada uno de esos marcos para las dependencias?

¿Fue útil?

Solución

No, usted tendrá que ejecutar repetidamente otool, o incorporar su código de análisis ( aquí ). No se olvide de manejo @executable_path.

Aquí está en Python (sin @executable_path, canónicos, o nombres de archivo con espacios compatibles), ya que esto era más fácil que intentar pseudocódigo de depuración:

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

Otros consejos

Aquí está mi solución que utilizo para fijar la salida del macdeployqt al utilizar bibliotecas de Homebrew instalado. Lo que he encontrado es que macdeployqt hace un buen trabajo de poner los dylibs en la carpeta de Marco, pero que no fija los caminos.

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

guión Nicholas

He modificado a ser un poco más fácil de usar - se corrige @executable_path, @rpath y @loader_path. Esto no es exactamente el código de producción, pero me ha dejado correr aplicaciones en otros equipos Mac sin ningún tipo de dependencias ya instalados.

Ejecutar con: python fix_paths_mac.py ./path/to/your.app/Contents/MacOS/your_exe. es decir, el punto al binario dentro de un paquete de la aplicación y va a averiguar el resto.

he asumido que la mayoría de los problemas provienen de la materia vinculada a /usr/local. Así que si el código detecta que hay una dependencia que apunta a un archivo en /usr/local, que va a corregir los caminos de manera apropiada. Se podría cambiar la declaración pass para copiar en un archivo si no está en la carpeta Frameworks, pero no he encontrado una situación donde hay una falta dylib, es sólo vinculado 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top