質問

Xcode を使用せずに osX アプリを作成したとします。GCC でコンパイルすると、他のいくつかのライブラリにリンクされた実行可能ファイルが得られます。これらのライブラリの一部は、他の非標準システム ライブラリに再び動的にリンクされる可能性があります。

最初に必要なディレクトリ構造を作成し、次にリンクを再帰的にコピー/チェック/修正して、すべての動的依存関係もアプリバンドルに含まれていることを確認することによって、OSX アプリバンドルを作成するツールは存在しますか?

このようなものを書いてみることができると思いますが、このようなものがすでに存在するかどうか疑問に思いました。

役に立ちましたか?

解決

MacOSX で App Bundle を作成するには、Easy と Ugly の 2 つの方法があります。

簡単な方法は、XCode を使用することです。終わり。

問題は、それができない場合があるということです。

私の場合、他のアプリを構築するアプリを構築しています。ユーザーが XCode をインストールしているとは考えられません。私も使っています Macポート アプリが依存するライブラリを構築します。アプリを配布する前に、これらの dylib がアプリにバンドルされていることを確認する必要があります。

免責事項: 私はこの記事を書く資格がまったくない。 はAppleのドキュメントから得たもので、既存のアプリをバラバラにしている。 試行錯誤。私にとっては機能しますが、おそらく間違っています。お願い 訂正があればメールをください。

まず知っておくべきことは、アプリ バンドルは単なるディレクトリであるということです。
仮想の foo.app の構造を調べてみましょう。

foo.app/
    Contents/
        Info.plist
        MacOS/
            foo
        Resources/
            foo.icns

Info.plist はプレーンな XML ファイルです。テキスト エディターまたは XCode にバンドルされているプロパティ リスト エディター アプリを使用して編集できます。(/Developer/Applications/Utilities/ ディレクトリにあります)。

含める必要がある主な内容は次のとおりです。

CFBバンドル名 - アプリの名前。

CFBundleアイコン - アイコンファイルはContents/Resourcesディレクトリにあると想定されます。Icon Composer アプリを使用してアイコンを作成します。(これも/Developer/Applications/Utilities/ディレクトリにあります)。 そのウィンドウにpngをドラッグ&ドロップするだけで、自動的にミップレベルを生成してくれる。

CFBundle実行可能ファイル - Content/MacOS/ サブフォルダーにあると想定される実行可能ファイルの名前。

他にもたくさんのオプションがありますが、上に挙げたものは最小限のものにすぎません。以下は Apple のドキュメントです。情報plist ファイルとアプリバンドルの構造.

また、これは Info.plist のサンプルです。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleGetInfoString</key>
  <string>Foo</string>
  <key>CFBundleExecutable</key>
  <string>foo</string>
  <key>CFBundleIdentifier</key>
  <string>com.your-company-name.www</string>
  <key>CFBundleName</key>
  <string>foo</string>
  <key>CFBundleIconFile</key>
  <string>foo.icns</string>
  <key>CFBundleShortVersionString</key>
  <string>0.01</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>IFMajorVersion</key>
  <integer>0</integer>
  <key>IFMinorVersion</key>
  <integer>1</integer>
</dict>
</plist>

完璧な世界では、実行ファイルを Contents/MacOS/dirで完了。ただし、アプリに 非標準のdylib依存関係は動作しない。ウィンドウズ同様、マックOS には独特のものがある。 DLL地獄.

使用している場合 Macポート リンク先のライブラリを構築するには、dylib の場所が実行可能ファイルにハードコーディングされます。まったく同じ場所に dylib があるマシンでアプリを実行すると、正常に実行されます。ただし、ほとんどのユーザーはそれらをインストールしていません。ユーザーがアプリをダブルクリックすると、アプリはクラッシュするだけです。

実行可能ファイルを配布する前に、ロードされるすべての dylib を収集し、アプリ バンドルにコピーする必要があります。また、正しい場所で dylib を検索できるように、実行可能ファイルを編集する必要があります。つまりコピーした場所。

実行ファイルを手動で編集するのは危険だと思いませんか?幸いなことに、役立つコマンド ライン ツールがあります。

otool -L executable_name

このコマンドは、アプリが依存するすべての dylib をリストします。System/Library フォルダーまたは usr/lib フォルダーにないものが見つかった場合は、それらをアプリ バンドルにコピーする必要があります。これらを /Contents/MacOS/ フォルダーにコピーします。次に、新しい dylib を使用するように実行可能ファイルを編集する必要があります。

まず、-headerpad_max_install_names フラグを使用してリンクしていることを確認する必要があります。これは、新しい dylib パスが前のパスよりも長い場合に、そのための余地があることを確認するだけです。

次に、install_name_tool を使用して、各 dylib パスを変更します。

install_name_tool -change existing_path_to_dylib @executable_path/blah.dylib executable_name

実用的な例として、アプリが次のようなものを使用しているとします。 libSDL, そして、otool はその場所を「/opt/local/lib/libSDL-1.2.0.dylib」としてリストします。

まずそれをアプリバンドルにコピーします。

cp /opt/local/lib/libSDL-1.2.0.dylib foo.app/Contents/MacOS/

次に、新しい場所を使用するように実行可能ファイルを編集します (注:-headerpad_max_install_names フラグを使用してビルドしたことを確認してください)

install_name_tool -change /opt/local/lib/libSDL-1.2.0.dylib @executable_path/libSDL-1.2.0.dylib foo.app/Contents/MacOS/foo

ふう、もうすぐ終わりです。現在の作業ディレクトリに小さな問題があります。

アプリを起動すると、現在のディレクトリはアプリケーションが配置されているディレクトリより上のディレクトリになります。例えば:foo.app を /Applcations フォルダーに配置すると、アプリを起動したときの現在のディレクトリは /Applications フォルダーになります。ご想像のとおり、/Applications/foo.app/Contents/MacOS/ ではありません。

これに対応するようにアプリを変更することも、現在のディレクトリを変更してアプリを起動するこの魔法の小さなランチャー スクリプトを使用することもできます。

#!/bin/bash
cd "${0%/*}"
./foo

次のように Info.plist ファイルを必ず調整してください。 CFBundle実行可能ファイル は、前の実行可能ファイルではなく、起動スクリプトを指します。

さて、これですべて完了です。幸いなことに、これらのことをすべて理解したら、それをビルド スクリプトに埋め込みます。

他のヒント

私は実際にいくつかの信用に値する非常に便利なツールを見つけた... NO - 私はこれを開発しませんでした;)

https://github.com/auriamg/macdylibbundler/する

それはあなたのアプリケーションバンドルにスムーズに仕事にすべての依存関係と「修正」実行可能ファイルだけでなく、あなたのdylibファイルを解決します。

...それはまたあなたの依存、動的のLIBSの依存関係を確認します:D

最も簡単な解決策は以下のとおりです、何も(すなわち、Xcodeのがあなたのために作成する簡単なワンウィンドウのアプリを保つ)変更することなく、Xcodeプロジェクト一度作成し、それを構築し、それはあなたのために作成したバンドルをコピーします。その後、編集ファイル(特にInfo.plistの)コンテンツに合わせて、内容/ MacOSの/ディレクトリに独自のバイナリを入れてます。

私は私のMakefileの中でこれを使用...それは、アプリケーションバンドルを作成します。それを読み、それを理解し、あなたがのpkginfoとのInfo.plistと一緒にMacOSXの/フォルダ内のpngアイコンファイルが必要になりますので、私はここに含まれたファイル...

「それは私のコンピュータ上で動作する」...私はマーベリックス...

上で複数のアプリケーションのためにこれを使用します
APPNAME=MyApp
APPBUNDLE=$(APPNAME).app
APPBUNDLECONTENTS=$(APPBUNDLE)/Contents
APPBUNDLEEXE=$(APPBUNDLECONTENTS)/MacOS
APPBUNDLERESOURCES=$(APPBUNDLECONTENTS)/Resources
APPBUNDLEICON=$(APPBUNDLECONTENTS)/Resources
appbundle: macosx/$(APPNAME).icns
    rm -rf $(APPBUNDLE)
    mkdir $(APPBUNDLE)
    mkdir $(APPBUNDLE)/Contents
    mkdir $(APPBUNDLE)/Contents/MacOS
    mkdir $(APPBUNDLE)/Contents/Resources
    cp macosx/Info.plist $(APPBUNDLECONTENTS)/
    cp macosx/PkgInfo $(APPBUNDLECONTENTS)/
    cp macosx/$(APPNAME).icns $(APPBUNDLEICON)/
    cp $(OUTFILE) $(APPBUNDLEEXE)/$(APPNAME)

macosx/$(APPNAME).icns: macosx/$(APPNAME)Icon.png
    rm -rf macosx/$(APPNAME).iconset
    mkdir macosx/$(APPNAME).iconset
    sips -z 16 16     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16.png
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16@2x.png
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32.png
    sips -z 64 64     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32@2x.png
    sips -z 128 128   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128.png
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128@2x.png
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256.png
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256@2x.png
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png
    cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/icon_512x512@2x.png
    iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset
    rm -r macosx/$(APPNAME).iconset

Info.plistの

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>MyApp</string>
    <key>CFBundleGetInfoString</key>
    <string>0.48.2, Copyright 2013 my company</string>
    <key>CFBundleIconFile</key>
    <string>MyApp.icns</string>
    <key>CFBundleIdentifier</key>
    <string>com.mycompany.MyApp</string>
    <key>CFBundleDocumentTypes</key>
    <array>
    </array>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>0.48.2</string>
    <key>CFBundleSignature</key>
    <string>MyAp</string>
    <key>CFBundleVersion</key>
    <string>0.48.2</string>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright 2013 my company.</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.3</string>
</dict>
</plist>

は、pkginfo

APPLMyAp

特定の環境向けに依存ライブラリを含むアプリ バンドルを構築するのに役立つオープン ソース ツールがいくつかあります。次に例を示します。 py2app Python ベースのアプリケーション向け。より一般的なものが見つからない場合は、ニーズに合わせて変更できる可能性があります。

私はこの記事以前見つけた希望....

ここで私は私のアプリのRun scriptバージョンをビルドするたびに呼び出されRelease段階を使用してこの問題を解決するための私の大雑把な方法があります:

# this is an array of my dependencies' libraries paths 
# which will be iterated in order to find those dependencies using otool -L
libpaths=("$NDNRTC_LIB_PATH" "$BOOST_LIB_PATH" "$NDNCHAT_LIB_PATH" "$NDNCPP_LIB_PATH" "/opt/local/lib")
frameworksDir=$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH
executable=$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH

#echo "libpaths $libpaths"
bRecursion=0
lRecursion=0

# this function iterates through libpaths array
# and checks binary with "otool -L" command for containment
# of dependency which has "libpath" path
# if such dependency has been found, it will be copied to Frameworks 
# folder and binary will be fixed with "install_name_tool -change" command
# to point to Frameworks/<dependency> library
# then, dependency is checked recursively with resolveDependencies function
function resolveDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$((lRecursion*20))
    printf "%s :\t%s\n" $prefix "resolving $binname..."

    for path in ${libpaths[@]}; do
        local temp=$path
        #echo "check lib path $path"
        local pattern="$path/([A-z0-9.-]+\.dylib)"
        while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
            local libname=${BASH_REMATCH[1]}
            otool -L ${binfile}
            #echo "found match $libname"
            printf "%s :\t%s\n" $prefix "fixing $libname..."
            local libpath="${path}/$libname"
            #echo "cp $libpath $frameworksDir"
            ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
            local installLibPath="@rpath/$libname"
            #echo "install_name_tool -change $libpath $installLibPath $binfile"
            if [ "$libname" == "$binname" ]; then
                install_name_tool -id "@rpath/$libname" $binfile
                printf "%s :\t%s\n" $prefix "fixed id for $libname."
            else
                install_name_tool -change $libpath $installLibPath $binfile
                printf "%s :\t%s\n" $prefix "$libname dependency resolved."
                let lRecursion++
                resolveDependencies "$frameworksDir/$libname" "$prefix>$libname"
                resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
                let lRecursion--
            fi
            path=$temp
        done # while
    done # for

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
} # resolveDependencies

# for some reason, unlike other dependencies which maintain full path
# in "otool -L" output, boost libraries do not - they just appear 
# as "libboost_xxxx.dylib" entries, without fully qualified path
# thus, resolveDependencies can't be used and a designated function is needed
# this function works pretty much in a similar way to resolveDependencies
# but targets only dependencies starting with "libboost_", copies them
# to the Frameworks folder and resolves them recursively
function resolveBoostDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$(((bRecursion+lRecursion)*20))
    printf "%s :\t%s\n" $prefix "resolving Boost for $(basename $binfile)..."

    local pattern="[[:space:]]libboost_([A-z0-9.-]+\.dylib)"
    while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
        local libname="libboost_${BASH_REMATCH[1]}"
        #echo "found match $libname"
        local libpath="${BOOST_LIB_PATH}/$libname"
        #echo "cp $libpath $frameworksDir"
        ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
        installLibPath="@rpath/$libname"
        #echo "install_name_tool -change $libname $installLibPath $binfile"
        if [ "$libname" == "$binname" ]; then
            install_name_tool -id "@rpath/$libname" $binfile
            printf "%s :\t%s\n" $prefix "fixed id for $libname."
        else
            install_name_tool -change $libname $installLibPath $binfile
            printf "%s :\t%s\n" $prefix "$libname Boost dependency resolved."
            let bRecursion++
            resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
            let bRecursion--
        fi
    done # while

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
}

resolveDependencies $executable $(basename $executable)
resolveBoostDependencies $executable $(basename $executable)

希望これが誰かの役に立つかもしれません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top