(再帰的に)コマンドotoolを使用すると、アプリケーションが必要とする共有ライブラリを検索します

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

質問

私はアプリが正常に機能するために必要な必要な共有ライブラリを見つけるために、コマンド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)

すなわちアプリバイナリ上の元のコマンドotool出力を示した負荷以上のフレームワークを示します。 を再帰的に実行するコマンドotool持つ方法は、アプリケーションのニーズ、その後に行くの枠組みをつかみ、依存関係のため、これらのフレームワークのそれぞれを検索する意味がありますか?

役に立ちましたか?

解決

いいえ、あなたはここに(繰り返しコマンドotool実行、またはその解析コードを組み込む必要があります)。 @executable_pathの取り扱いを忘れないでください。

ここでは、これはデバッグ擬似コードにしようとするよりも簡単だったので、(@executable_path、正規化、またはファイル名-スペースをサポートせずに)Pythonであります

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する

私は、もう少し使えるようにニコラススクリプトを変更した - それは@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)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top