使 Xcode 嵌入必要的动态库
Making Xcode embed necessary dylibs
我正在尝试做一些非常简单和典型的事情,即在我的 Xcode 项目中使用动态链接库,然后部署所有嵌入的必要库。
但是我一定是做错了什么,因为 Xcode 8 不允许我嵌入 .dylib 文件,只能嵌入框架!下图是当我尝试向 Embedded Binaires 添加任何内容时发生的情况,dylibs 只是不显示,Add Other... 将它们添加到项目而不是 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=
echo change_path $bin
dyl_list=(`otool -L $bin | grep local | awk '{print }'`)
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
然后只需要在将 dylib 复制到 运行 之后添加一个 运行 脚本步骤,不带任何参数(环境变量包含所需的一切)。
我从未使用过 "Embedded Libraries",而是一个复制阶段,用于复制所有需要的第 3 方库以及由项目中的各种目标创建的库。您可以简单地将您想要从 "Frameworks" 和 "Products" 大纲节点复制的 dylib 拖到这个复制阶段(或通过“+”按钮添加它们)。 XCode 也将自动签署每个库,如果启用(默认情况下):
另请查看 "Installation Directory" 设置。您应该将此值设置为 @executable_path/../Frameworks(前提是这是您的实际框架文件夹,至少这是推荐的文件夹)。 XCode 使用该值来设置您的 dylib 的 ID,并且对于使它们正确加载是必不可少的
您还可以使用 MacDependency 之类的工具来检查 .app
、.dylib
和 .framework
软件包的详细信息。它还会向您展示一个库依赖于哪些其他 dylib 以及它期望它们在哪条路径下。该路径必须与 linked 库的 ID 相同,否则加载将失败。
另一个非常有用的工具是 otool,它与 XCode 一起提供。它提供类似的信息,如 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)
另一个关于加载器路径的讨论在这里:How to set dyld_library_path in Xcode。
如果您的应用程序或您创建的库需要来自系统位置而不是相关位置的动态库,那么这可能意味着您 link 将错误的库输入到它。在 "Link Binary" 构建阶段,您可以 select link 反对的库,并且 selection 对话框会在此处列出您的所有项目目标。同样对于 linker 列表,您可以简单地从项目大纲中的 "Products" 节点拖一个 dylib 到其中进行设置。同样对于此类库,设置正确路径的要求也适用。您可以使用在复制阶段后更改 ID 的脚本来执行此操作,或者您将这些库的副本保留在其他地方,更改它们的 ID 一次(例如,在您下载它们之后)并将它们从那里复制到您的最终应用程序。
嵌入 dylibs 现在可以工作(使用 Xcode 版本 10.2 检查)。
目标 -> 框架、库和嵌入式内容。
见https://help.apple.com/xcode/mac/11.0/index.html?localePath=en.lproj#/dev51a648b07
以上所有解决方案都不适用于更高版本的 Xcode 和 iOS。这是用 Xcode 13 更新并用 iOS 14.
测试的
这是一步一步的指南:
创建dylib后,确保dylib的Build Setting的“Install Directory”路径设置为:
@executable_path/Frameworks
复制你的dylib到你项目目录下的子目录如:./libs/
在您项目的 Xcode Build Settings 选项卡中,Search Path ->Library Search Path 中有一个指向上面 2) 中的子目录的路径。
Select 构建阶段,将您的 dylib 添加到 Copy Bundle Resource 以及 Embedded Frameworks 中,如下所示(参见 libgrt.dylib 的条目作为示例):
[
确保路径是 Frameworks 并将子路径字段留空。
现在构建并测试您的应用!
我正在尝试做一些非常简单和典型的事情,即在我的 Xcode 项目中使用动态链接库,然后部署所有嵌入的必要库。
但是我一定是做错了什么,因为 Xcode 8 不允许我嵌入 .dylib 文件,只能嵌入框架!下图是当我尝试向 Embedded Binaires 添加任何内容时发生的情况,dylibs 只是不显示,Add Other... 将它们添加到项目而不是 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=
echo change_path $bin
dyl_list=(`otool -L $bin | grep local | awk '{print }'`)
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
然后只需要在将 dylib 复制到 运行 之后添加一个 运行 脚本步骤,不带任何参数(环境变量包含所需的一切)。
我从未使用过 "Embedded Libraries",而是一个复制阶段,用于复制所有需要的第 3 方库以及由项目中的各种目标创建的库。您可以简单地将您想要从 "Frameworks" 和 "Products" 大纲节点复制的 dylib 拖到这个复制阶段(或通过“+”按钮添加它们)。 XCode 也将自动签署每个库,如果启用(默认情况下):
另请查看 "Installation Directory" 设置。您应该将此值设置为 @executable_path/../Frameworks(前提是这是您的实际框架文件夹,至少这是推荐的文件夹)。 XCode 使用该值来设置您的 dylib 的 ID,并且对于使它们正确加载是必不可少的
您还可以使用 MacDependency 之类的工具来检查 .app
、.dylib
和 .framework
软件包的详细信息。它还会向您展示一个库依赖于哪些其他 dylib 以及它期望它们在哪条路径下。该路径必须与 linked 库的 ID 相同,否则加载将失败。
另一个非常有用的工具是 otool,它与 XCode 一起提供。它提供类似的信息,如 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)
另一个关于加载器路径的讨论在这里:How to set dyld_library_path in Xcode。
如果您的应用程序或您创建的库需要来自系统位置而不是相关位置的动态库,那么这可能意味着您 link 将错误的库输入到它。在 "Link Binary" 构建阶段,您可以 select link 反对的库,并且 selection 对话框会在此处列出您的所有项目目标。同样对于 linker 列表,您可以简单地从项目大纲中的 "Products" 节点拖一个 dylib 到其中进行设置。同样对于此类库,设置正确路径的要求也适用。您可以使用在复制阶段后更改 ID 的脚本来执行此操作,或者您将这些库的副本保留在其他地方,更改它们的 ID 一次(例如,在您下载它们之后)并将它们从那里复制到您的最终应用程序。
嵌入 dylibs 现在可以工作(使用 Xcode 版本 10.2 检查)。 目标 -> 框架、库和嵌入式内容。
见https://help.apple.com/xcode/mac/11.0/index.html?localePath=en.lproj#/dev51a648b07
以上所有解决方案都不适用于更高版本的 Xcode 和 iOS。这是用 Xcode 13 更新并用 iOS 14.
测试的这是一步一步的指南:
创建dylib后,确保dylib的Build Setting的“Install Directory”路径设置为:
@executable_path/Frameworks
复制你的dylib到你项目目录下的子目录如:./libs/
在您项目的 Xcode Build Settings 选项卡中,Search Path ->Library Search Path 中有一个指向上面 2) 中的子目录的路径。
Select 构建阶段,将您的 dylib 添加到 Copy Bundle Resource 以及 Embedded Frameworks 中,如下所示(参见 libgrt.dylib 的条目作为示例):
[
确保路径是 Frameworks 并将子路径字段留空。
现在构建并测试您的应用!