假设我在没有使用 Xcode 的情况下制作了一个 osX 应用程序。使用 GCC 编译后,我得到一个链接到其他几个库的可执行文件。其中一些库可能会再次动态链接到其他非标准系统库

是否有任何工具可以通过首先创建所需的目录结构,然后递归地复制/检查/修复链接来创建 OSX 应用程序包,以确保所有动态依赖项也在应用程序包中?

我想我可以尝试写这样的东西,但我想知道这样的东西是否已经存在。

有帮助吗?

解决方案

有两种方式来创建一个应用程序捆绑在MacOSX,易和丑恶。

简单的方法是只要使用载。完成。

问题是有时候,你不能。

在我的情况下,我要建一个应用程序,建立的其他应用程序。我不能假定用户已经低的安装。我也使用 MacPorts 建立的图书馆我的应用取决。我需要确保这些dylibs得到捆绑在一起的应用程序之前,我分发。

免责声明: 我完全不合格的编写此后,一切都在 已被闪闪发光的苹果文件、摘除了现有的应用程序和 试验和错误。这对我的作品,但是最有可能是错误的。请 给我发电子邮件,如果你有任何更正。

第一件事你应该知道的是,一个应用程序包仅仅是一个目录。
让我们检查结构的一个假设的foo。应用程序。

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

信息。plist是一个普通的XML文件。你可以编辑一个文本编辑器或财产清单的编辑程序附带载。(这是在/Developer/应用程序/工具/目录)。

关键的事情你需要包括:

CFBundleName -这个名字的程序。

CFBundleIcon -一个图标文件假定的内容/资源目录。使用的标作曲家应用创建的图标。(它还在开发工具/应用程序/工具/目录) 你可以只是拖png到它的窗口,并应自动产生的mip-水平。

CFBundleExecutable -名字的可执行文件假定的内容/mac os/子文件夹。

有很多更多的选择,上面列出的仅仅是最低限度。这里的一些苹果文件, 信息。plist 文件和 应用程序包的结构.

此外,这样品的信息。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>

在一个完美的世界你可以放下你可执行的成本 内容/mac os/dir和做。然而,如果应用有任何 非标准dylib依赖它不会的工作。像窗户,Mac Os 带有它自己的特殊类型的 DLL地狱.

如果你使用 MacPorts 建立图书馆,你的链接反对该地点的dylibs将很难编码进入你可执行的。如果运行的应用程序的计算机上有dylibs在完全相同的位置,它将运行罚款。然而,大多数用户不会让他们安装;当他们双击你的程序,它会只是崩溃。

在你之前分发为你执行你会需要收集所有的dylibs它加载和复制他们进入应用程序包。你也会需要编辑的可执行,这样,它将寻找dylibs在正确的位置。即复制他们。

手工编辑一个可执行的听起来很危险对吗?幸运的是有命令行的工具,以帮助。

otool -L executable_name

这种命令将列出所有dylibs你的应用取决。如果你看到任何不在系统/图书馆或usr/lib文件夹,这些都是那些你需要复制到应用程序包。复制他们进入/Contents/mac os/文件夹。下一步则需要编辑的可执行以使用的新dylibs.

第一,需要确保,你的链接使用的-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。应用/用文件夹中的那么当前的目录当你启动程序将是该/应用程序的文件夹。不/Applications/foo。应用程序/Contents/mac os/为你可能期望。

你可以改变你的应用程序考虑到这一点,或者您可以使用这个神奇的小启动脚本,这将改变目前的目录和启动程序。

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

确保调整信息。plist文件,以便 CFBundleExecutable 点到启动脚本,而不是向以前可执行的。

好了,现在做的。幸运的是,一旦你知道所有这些东西你把它埋在一个建立脚本。

其他提示

其实,我发现了一个很方便的工具,值得一些信贷... NO - 我没有开发这个;)

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

这将顺利解决您的应用程序捆绑所有的依赖关系,并“修复”你的可执行文件,以及您dylib文件的工作。

...它还会检查你的相关动态库的依赖关系:d

最简单的解决方法是:创建一次不做改变的Xcode项目(即保持简单的一个窗口应用程序,Xcode创建你),建造它,并把它复制为您创建的包。然后,编辑文件(特别是Info.plist中),以满足您的内容,并把你自己的二进制的内容/ MacOS的/目录。

我在Makefile中使用这个......它创建了一个应用程序包。阅读它,了解它,因为你需要一个PNG图标文件的MacOSX与PKGINFO沿着/文件夹和文件的Info.plist我这里包括...

“它的作品在我的电脑” ......我用这对小牛...

多个应用程序
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

有一些开源工具可以帮助为特定环境构建具有依赖库的应用程序包,例如, py2应用程序 用于基于 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