10

アプリが正しく機能するために必要な共有ライブラリを見つけるために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を再帰的に実行する方法はありますか?つまり、アプリが必要とするフレームワークを取得してから、それらの各フレームワークに依存関係を検索しますか?

4

4 に答える 4

11

いいえ、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
于 2009-10-04T23:06:00.803 に答える
1

このトピックに関する私の見解は次のとおりです。私のスクリプトは、アプリのメイン実行可能ファイルから開始し、すべてのフレームワークを再帰的にトラバースすることを目的としています。私の用途は、アプリが参照するフレームワークが Xcode によって埋め込まれたフレームワークと一致するかどうかを確認することです。非システム フレームワークに焦点を当てるために私が行った主な仮定は次のとおりです。

  • インポートパスはで始まる必要があります@rpath
  • X.framework/Xフォーマットのフレームワークでなければなりません
  • weakフレームワークは無視されます

これらのいずれかが必要ない場合は、awk 正規表現/weak\)$/ { next }; match($1, /^@rpath.*(.framework)/) { ... }を変更できます。
最初にシェルスクリプトを書きました:

#/bin/sh
recursiveFrameworksParseStep() {
#fail on 1st otool error
set -e
set -o pipefail #not really POSIX compliant but good enough in MacOS where sh is emulated by bash
otool -L $1|awk -v pwd=${PWD} '/weak\)$/ { next }; match($1, /^@rpath.*(.framework)/) { gsub("@rpath",pwd"/MyApp.app/Frameworks",$1); print $1 }'| while read line; do
   if [ $1 != $line ]; then #safety check for otool -L output not to self reference resulting in infinite loop
      recursiveFrameworksParseStep $line
   fi
done
}
recursiveFrameworksParseStep MyApp.app/MyApp

ファイルシステムで見つからない最初の参照フレームワークでは失敗します。これで問題ありませんが、欠点は、アクセスしたフレームワークの追跡がなく、重複したチェックが多数発生する可能性があることです。シェルは、それを追跡するためのグローバル辞書のような構造には特に適していません。
そのため、Python ラッパーを使用してこのスクリプトを書き直しました。

#!/usr/bin/python
import subprocess
import os.path
from sys import exit

visitedFrameworks = set()

def fn(executableToProcess):
    try:
        otoolOut = subprocess.check_output(['otool','-L',executableToProcess])
    except subprocess.CalledProcessError: 
        exit(-1)

    pipeOutput = subprocess.Popen(['awk', '-v', os.path.expandvars('pwd=$PWD'),'/weak\)$/ { next };match($1, /@rpath.*(.framework)/) { gsub(\"@rpath\",pwd\"/MyApp.app/MyApp\",$1); print $1 }'], 
        stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(otoolOut)

    lines = pipeOutput[0].split('\n')

    for outputLine in lines[1:-1]:
        if executableToProcess != outputLine:
            if outputLine not in visitedFrameworks:
                visitedFrameworks.add(outputLine)
                fn(outputLine)

fn("MyApp.app/MyApp")

概念上の唯一の違いは、訪問したフレームワークを追跡することです。これにより、経過時間が劇的に短縮されます (私の場合は 7 ~ 8 秒から 1 秒未満に短縮されます)。

最後に、これを Target ビルド プロセスで Xcode シェル スクリプトにすることができます (同様に、シェル インタープリターを に設定し/usr/bin/pythonます)。

import subprocess
import os.path
from sys import exit

visitedFrameworks = set()
numberOfMissingFrameworks = 0

def fn(executableToProcess):
    global numberOfMissingFrameworks
    try:
        otoolOut = subprocess.check_output(['otool','-L',executableToProcess])
    except subprocess.CalledProcessError: 
        numberOfMissingFrameworks += 1
        return


    pipeOutput = subprocess.Popen(['awk', '-v', os.path.expandvars('frameworkPath=$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH'),'/weak\)$/ { next };match($1, /@rpath.*(.framework)/) { gsub(\"@rpath\",frameworkPath,$1); print $1 }'], 
        stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(otoolOut)

    lines = pipeOutput[0].split('\n')

    for outputLine in lines[1:-1]:
        if executableToProcess != outputLine:
            if outputLine not in visitedFrameworks:
                visitedFrameworks.add(outputLine)
                fn(outputLine)

fn(os.path.expandvars('$TARGET_BUILD_DIR/$EXECUTABLE_PATH'))
exit(numberOfMissingFrameworks)
于 2021-10-13T18:00:04.920 に答える