让Xcode嵌入必要的dylibs

21
我试图做一些非常简单和典型的事情,即在我的Xcode项目中使用动态链接库,然后嵌入所有必要的库进行部署。但是我一定做错了什么,因为Xcode 8不允许我嵌入.dylib文件,只能用框架!当我尝试将任何内容添加到“嵌入式二进制文件”时,下面的图片显示出现问题,dylibs没有显示出来,“添加其他…”将它们添加到项目但不包含在“嵌入式二进制文件”中。

Xcode not allowing me to add any of the dylibs as embedded binaries

我相信一定有一种非常简单的方法来做到这一点,但我就是找不到……

结语

所以显然,由于我确实需要运行调用install_lib_tool的脚本,我编写了一个非常通用的脚本,将路径中包含/local/的任何内容更改为嵌入式副本的路径:

#!/bin/sh

app=$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH
fw_path=$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH
app_dyl_list=(`ls $fw_path | grep dylib`)

function change_paths {
    local bin=$1
    echo change_path $bin
    dyl_list=(`otool -L $bin | grep local | awk '{print $1}'`)

    for dyl in ${dyl_list[*]}; do
        libname=$(basename $dyl)
        libname=${libname%%.*}
        actual_libname=(`ls $fw_path | grep $libname | xargs basename`)
        install_name_tool -change $dyl "@executable_path/../Frameworks/$actual_libname" $bin
        printf "\t%s edited\n" $actual_libname
    done
}

change_paths $app
for dyl_bin in ${app_dyl_list[*]}; do
    change_paths $fw_path/$dyl_bin
done

然后,只需要在将dylibs复制后添加一个运行脚本步骤,无需参数即可运行它(环境变量包含所需的一切)。

1
我通过 install_name_tool 使用了上述脚本,一切看起来都正确! 我的问题是 "LC_ID_DYLIB" 名称, 所以 "cmd LC_ID_DYLIB name /usr/local/opt/cpprestsdk/lib/libcpprest.2.10.dylib (offset 24)" 运行脚本后它们不会改变,我无法运行应用程序,所以我需要在 opt 文件夹和应用程序内的 framework dylibs 上创建符号链接! 有没有什么方法可以解决? - Mo Farhand
我也在Xcode的Build phases中使用了上述脚本作为运行脚本,但是没有结果。 @MichelRouzic 这个问题有什么更新吗? - peco
3个回答

18

我从未使用“嵌入式库”,而是使用复制阶段来复制所有需要的第三方库以及项目中各个目标创建的库。您可以将要复制的 dylibs 从“框架”和“产品”概述节点拖到此复制阶段中(或通过“+”按钮添加)。如果启用了自动签名(默认情况下已启用),XCode 还会自动为每个库签名:

enter image description here

还要看一下“安装目录”设置。您应该将此值设置为 @executable_path/../Frameworks(如果这是您实际的框架文件夹,至少是推荐的)。该值由 XCode 用于设置 dylib 的 ID,并且对于使它们正确加载非常重要。

enter image description here

您还可以使用诸如MacDependency这样的工具检查您的.app.dylib.framework包的详细信息。它还会显示库所依赖的其他 dylibs,以及在哪个路径下期望它们。该路径必须与链接库的 ID 相同,否则加载将失败。

另一个非常有用的工具是 XCode 自带的otool。它提供类似于 MacDependency 的信息和更多信息。这是一个带有路径和版本的 dylib 依赖项列表:

Mikes-iMac:Debug mike$ otool -L libcdbc.dylib
libcdbc.dylib:
    @executable_path/../Frameworks/libcdbc.dylib (compatibility version 1.0.0, current version 1.0.0)
    @executable_path/../Frameworks/libwbbase.dylib (compatibility version 1.0.0, current version 1.0.0)
    @executable_path/../Frameworks/libgrt.dylib (compatibility version 1.0.0, current version 1.0.0)
    @executable_path/../Frameworks/libgmodule-2.0.0.dylib (compatibility version 3401.0.0, current version 3401.2.0)
    @executable_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 3401.0.0, current version 3401.2.0)
    @executable_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 3401.0.0, current version 3401.2.0)
    @executable_path/../Frameworks/libmysqlcppconn.7.1.1.8.dylib (compatibility version 7.0.0, current version 7.1.1)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 307.4.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)

关于加载器路径的讨论可以在这里找到:如何在Xcode中设置dyld_library_path

如果您的应用程序或您创建的库期望从系统位置而不是相对位置获取dylibs,则可能意味着您链接了错误的lib。在“链接二进制”构建阶段,您可以选择要链接的库,并且选择对话框会列出所有项目目标。对于链接器列表,您可以从项目概述中的“产品”节点中拖动dylib到其中以进行设置。对于这样的库,设置正确路径的要求仍然成立。您可以使用在复制阶段后更改ID的脚本来执行此操作,或者将这些库的副本保存在其他地方,一旦更改它们的ID(例如,在下载它们之后),然后从那里将它们复制到最终应用程序中。


我尝试过了,但不幸的是,在启动应用程序时,它仍然会在原始的 /usr/local/lib/ 位置查找 dylibs,忽略嵌入式 dylibs 并无法正常启动。你知道如何解决这个路径问题吗? - Michel Rouzic
更新了我的回答。希望有所帮助。 - Mike Lischke
哦,我指的是我尝试添加复制阶段并更改路径,就像屏幕截图中显示的那样。 - Michel Rouzic
谢谢,编写一个脚本就能解决了(请查看我编辑后的问题,里面有一个适用于任何项目的脚本)。很难相信这是苹果想让它这样工作的方式,这相当令人失望,但我学到的越多,就越知道要从他们那里期待这些。 - Michel Rouzic
通常情况下,您需要自己编译第三方库并在其中设置ID。当我不是自己构建而是下载预构建的二进制文件时,我就会进行设置。这是一项简单的工作,更改ID后我再也不需要考虑它了。另外,您的情况有些特殊,因为您想嵌入应该安装在系统中的库。所有这些加在一起使得您的设置比平常要复杂一些。但我很高兴您现在已经解决了它。 - Mike Lischke
显示剩余7条评论

1
所有上述解决方案都不适用于较新版本的Xcode和iOS。此方法已更新为Xcode 13并在iOS 14上进行了测试。
以下是一步一步的指南:
1. 创建dylib时,请确保dylib的“安装目录”路径设置为: @executable_path/Frameworks
2. 将dylib复制到项目目录下的子目录中,例如:./libs/。
3. 在项目的Xcode构建设置选项卡中,搜索路径->库搜索路径中包含上述第2步的子目录路径。
4. 选择构建阶段,将dylib添加到“复制包资源”和“嵌入式框架”中,如下所示(以libgrt.dylib的条目为例):
[Xcode Project Build Phase1 请确保路径为Frameworks,并将子路径字段留空。
现在构建并测试您的应用程序!

1

你的意思是 "嵌入式库" 吗? - Raptor
非常好的消息,因为使用构建阶段的方法存在一个大问题:代码签名发生在所有阶段运行之后。因此,在引入新设置之前,可能会发生将未签名版本的库复制到应用程序包中的情况,这会导致应用程序构建失败,因为无法对应用程序进行签名。 - Mike Lischke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接