编译用于 iOS 项目的外部 C++ 库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27016612/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compiling external C++ library for use with iOS project
提问by HHHH
I'm completely new to using C++ libraries, so appreciate this might be a bit specific for my case (let me know and I can provide more details).
我对使用 C++ 库完全陌生,所以请注意这可能对我的情况有点特殊(让我知道,我可以提供更多详细信息)。
I have an external C++ library that I'm trying to use with an iOS project. The library follows a configure, make, make build pattern to output a .a library file. When I try and add this library file to Xcode, I get the following error:
我有一个外部 C++ 库,我正尝试将它用于 iOS 项目。该库遵循 configure、make、make 构建模式以输出 .a 库文件。当我尝试将此库文件添加到 Xcode 时,出现以下错误:
ignoring file /Users/Developer/iOS/TestProj/libpresage.a, file was built for archive which is not the architecture being linked (i386):
/Users/Developer/iOS/TestProj/libpresage.a
忽略文件/Users/Developer/iOS/TestProj/libpresage.a,该文件是为归档而构建的,这不是所链接的架构(i386):
/用户/开发人员/iOS/TestProj/libpresage.a
Based on this question, I've tried turning Build Active Architecture Only to NO, and I get the same error. This makes me suspect that I've compiled the library for the incorrect architecture.
基于这个问题,我尝试将仅构建活动架构变为否,但我得到了同样的错误。这让我怀疑我已经为不正确的架构编译了库。
Running lipo -info on the .a file gives:
在 .a 文件上运行 lipo -info 给出:
input file libpresage.a is not a fat file Non-fat file: libpresage.a
is architecture: x86_64
输入文件 libpresage.a 不是胖文件 非胖文件:libpresage.a
是架构:x86_64
Given that this isn't armv7s, armv7, or arm64, I try and compile the C++ library again with the following parameters:
鉴于这不是 armv7s、armv7 或 arm64,我尝试使用以下参数再次编译 C++ 库:
1)Try
1)尝试
./configure CC="gcc -arch armv7s" \
CXX="g++ -arch armv7s" \
CPP="gcc -E" CXXCPP="g++ -E"
Error in compiling, I get:
编译出错,我得到:
ld: library not found for -lcrt1.3.1.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
2)Try
2)尝试
./configure CC="gcc -arch arm64" \
CXX="g++ -arch arm64" \
CPP="gcc -E" CXXCPP="g++ -E"
Error in compiling, I get:
编译出错,我得到:
ld: warning: ld: warning: ignoring file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib, missing required architecture arm64 in file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib (2 slices)ignoring file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib, missing required architecture arm64 in file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib (2 slices)
ld: dynamic main executables must link with libSystem.dylib for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
ld: 警告: ld: 警告: 忽略文件 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib,缺少必需的架构 arm64文件/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib (2 slices)忽略文件/Applications/Xcode.app/Contents/ Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib,文件 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer 中缺少所需的架构 arm64 /SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib(2个切片)
ld:动态主可执行文件必须与 libSystem.dylib 链接,用于架构 arm64 clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)
Is there something obvious that I'm missing?
有什么明显的东西我失踪了吗?
EDIT:
编辑:
Thanks for the replies, so I've managed to get the library into Xcode as a custom build target, pointing the 'make' command to the libraries MakeFile. This build fine.
感谢您的回复,所以我设法将库作为自定义构建目标放入 Xcode,将“make”命令指向库 MakeFile。这个构建不错。
My steps from here:
我从这里开始的步骤:
- Add a dependency from my Objective C iOS app target to the custom build target.
- Reference the library and make an Objective C++ wrapper.
- This seems fine until I need to call the external C++ library, then I get the error when compiling:
- 将我的 Objective C iOS 应用程序目标的依赖项添加到自定义构建目标。
- 引用该库并制作一个 Objective C++ 包装器。
- 这似乎很好,直到我需要调用外部 C++ 库,然后在编译时出现错误:
Undefined symbols for architecture armv7: "Presage::Presage(PresageCallback*)", referenced from: -[PresageBridge init] in PresageBridge.o "Presage::~Presage()", referenced from: -[PresageBridge init] in PresageBridge.o ld: symbol(s) not found for architecture armv7 clang: error: linker command failed with exit code 1 (use -v to see invocation)
架构 armv7 的未定义符号:“Presage::Presage(PresageCallback*)”,引用自:-[PresageBridge init] in PresageBridge.o “Presage::~Presage()”,引用自:-[PresageBridge init] in PresageBridge。 ld:找不到架构 armv7 clang 的符号:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)
My objective C++ wrapper (linking the external C++ library header presage.h):
#import "PresageBridge.h" #include "presage.h" @implementation PresageBridge - (instancetype)init { if(self = [super init]) { Presage hello(&callback); } return self; }
Based on the code above it doesn't seem like I'm missing the header, and what's interesting is that I've also tried creating an instance of other classes in the external library and they seem to be working, which suggests that Xcode can't link presage.h properly for some reason.
我的目标 C++ 包装器(链接外部 C++ 库头文件 presage.h):
#import "PresageBridge.h" #include "presage.h" @implementation PresageBridge - (instancetype)init { if(self = [super init]) { Presage hello(&callback); } return self; }
根据上面的代码,我似乎没有遗漏标题,有趣的是,我还尝试在外部库中创建其他类的实例,它们似乎正在工作,这表明 Xcode 可以由于某种原因,不能正确链接 presage.h。
回答by Mobile Ben
So I've used many a 3rd party C++ library in my iOS projects. There are different strategies people use for this. As some have already cited, you can include the code within the project directly, build the static lib with Xcode, or build it command line. In the case of cross platform C++ libs which use the GNU configure and build system, I prefer command line. You only need to build it once and you only have to revisit it if you need to update the version or add a new architecture slice.
所以我在我的 iOS 项目中使用了许多 3rd 方 C++ 库。人们为此使用不同的策略。正如一些人已经提到的,您可以直接将代码包含在项目中,使用 Xcode 构建静态库,或构建它的命令行。对于使用 GNU 配置和构建系统的跨平台 C++ 库,我更喜欢命令行。您只需要构建一次,如果您需要更新版本或添加新的架构切片,您只需重新访问它。
The generalized approach you want is:
您想要的通用方法是:
Figure out the right configure arguments to use to build each slice. Typically, you only need to focus on getting one of the arm as well as i386 working. The rest are easy one you have this done. In some cases, you actually need to modify the configure file to add the host or make some other adjustments.
Once you can build all slices, you want to run lipo to build a fat binary.
找出用于构建每个切片的正确配置参数。通常,您只需要专注于让一只手臂和 i386 工作。其余的很容易,你已经完成了。在某些情况下,您实际上需要修改配置文件以添加主机或进行一些其他调整。
一旦您可以构建所有切片,您就希望运行 lipo 来构建一个胖二进制文件。
The best way then to deal with this is create a build script which will do all the work for you. This way, it's easier to redo. More importantly, you can reuse the script or permute it to build other external libs.
解决这个问题的最好方法是创建一个构建脚本,它将为您完成所有工作。这样,更容易重做。更重要的是,您可以重用该脚本或对其进行置换以构建其他外部库。
There are many ways you can build the script. Here is one. I happen to have several variations of this type of script. This script was used to build cURL. It more or less worked for presage with very little mod (ie. change curl to presage). Note I didn't test it in Xcode (ie. linking it and running it). I did find that I had to disable sqlite, else it built tool items which don't build right. If you need it, you can figure that part out.
您可以通过多种方式构建脚本。这是一个。我碰巧有这种脚本的几种变体。此脚本用于构建 cURL。它或多或少适用于很少 mod 的 presage(即将 curl 更改为 presage)。注意我没有在 Xcode 中测试它(即链接它并运行它)。我确实发现我必须禁用 sqlite,否则它构建了无法正确构建的工具项。如果你需要它,你可以弄清楚那部分。
There are many ways you could make it more slick. For example using an array to store all the architectures. This is just brute force.
有很多方法可以使它更光滑。例如,使用数组来存储所有架构。这只是蛮力。
The key points of the script are:
脚本的要点是:
- Getting the latest SDK
- Building each slice
- Then running lipo
- 获取最新的 SDK
- 构建每个切片
- 然后跑脂
Note that it should work out of the box, however, YMMV. Be prepared to have to debug it if necessary. For example, I haven't confirmed the host type, but generally that is what I've always used. You want to put this at the directory for presage (same directory where configure). When it is done, all architectures are in the output directory. The universal lib is in the presage directory.
请注意,它应该是开箱即用的,但是,YMMV。准备好在必要时调试它。例如,我没有确认主机类型,但通常这是我一直使用的。你想把它放在 presage 目录(配置的同一目录)。完成后,所有架构都在输出目录中。通用库位于 presage 目录中。
Also remember it is your responsibility to properly link in the universal lib as well as have the header files search path defined properly.
还要记住,您有责任在通用库中正确链接以及正确定义头文件搜索路径。
#!/bin/bash
PLATFORMPATH="/Applications/Xcode.app/Contents/Developer/Platforms"
TOOLSPATH="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin"
export IPHONEOS_DEPLOYMENT_TARGET="8.0"
pwd=`pwd`
findLatestSDKVersion()
{
sdks=`ls $PLATFORMPATH/.platform/Developer/SDKs`
arr=()
for sdk in $sdks
do
arr[${#arr[@]}]=$sdk
done
# Last item will be the current SDK, since it is alpha ordered
count=${#arr[@]}
if [ $count -gt 0 ]; then
sdk=${arr[$count-1]:${#1}}
num=`expr ${#sdk}-4`
SDKVERSION=${sdk:0:$num}
else
SDKVERSION="8.0"
fi
}
buildit()
{
target=
hosttarget=
platform=
if [[ $hosttarget == "x86_64" ]]; then
hostarget="i386"
elif [[ $hosttarget == "arm64" ]]; then
hosttarget="arm"
fi
export CC="$(xcrun -sdk iphoneos -find clang)"
export CPP="$CC -E"
export CFLAGS="-arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
export AR=$(xcrun -sdk iphoneos -find ar)
export RANLIB=$(xcrun -sdk iphoneos -find ranlib)
export CPPFLAGS="-arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
export LDFLAGS="-arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk"
mkdir -p $pwd/output/$target
./configure --prefix="$pwd/output/$target" --disable-shared --disable-sqlite --host=$hosttarget-apple-darwin
make clean
make
make install
}
findLatestSDKVersion iPhoneOS
buildit armv7 iPhoneOS
buildit armv7s iPhoneOS
buildit arm64 iPhoneOS
buildit i386 iPhoneSimulator
buildit x86_64 iPhoneSimulator
LIPO=$(xcrun -sdk iphoneos -find lipo)
$LIPO -create $pwd/output/armv7/lib/libpresage.a $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a
回答by MichaelCMS
Considering that you are new with C++ libraries, I guess you will need to do a bit more research.
考虑到您是 C++ 库的新手,我想您需要做更多的研究。
However, I will try to outline some steps things you need to take into consideration :
但是,我将尝试概述您需要考虑的一些步骤:
- you need to make sure you compile for the same architecture both the static library (.a) and the project
- from your error , you need to compile your static library for i386 OR change your project to x86_64 ( the difference between these architectures is a bit more complex, but for now let's say that i386 means desktop 32 bit while x86_64 means desktop 64 bit)
- arm architectures are for iPhone , not for your MacOS (that's why it fails to find libraries with arm architecture inside the MacOSX folder) !
- 您需要确保为相同的架构编译静态库 (.a) 和项目
- 根据您的错误,您需要为 i386 编译静态库或将您的项目更改为 x86_64(这些架构之间的差异有点复杂,但现在假设 i386 表示桌面 32 位,而 x86_64 表示桌面 64 位)
- arm 架构适用于 iPhone,而不适用于您的 MacOS(这就是为什么它无法在 MacOSX 文件夹中找到具有 arm 架构的库的原因)!
There are multiple ways to approach these issues .
有多种方法可以解决这些问题。
For the first one I would suggest to include into your workspace the static library, and add it as dependency to your build target . For this you need to understand XCode builds.
对于第一个,我建议将静态库包含到您的工作区中,并将其作为依赖项添加到您的构建目标中。为此,您需要了解 XCode 构建。
I'm guessing that you actually are trying to make a phone application, so for the 3rd option you need to configure your g++ build to look into the iPhoneSDK from XCode when linking arm targets (look after iPhoneOS.platform) for this.
我猜您实际上是在尝试制作手机应用程序,因此对于第三个选项,您需要配置 g++ 构建,以便在为此链接 arm 目标(查看 iPhoneOS.platform)时从 XCode 中查看 iPhoneSDK。
Making an arm build will only work on iPhones . If you want it to work on simulator , you will need to link your static library to libraries inside the iPhoneSimulator.platform.
制作 arm 版本仅适用于 iPhone。如果你想让它在模拟器上工作,你需要将你的静态库链接到 iPhoneSimulator.platform 中的库。
If you want your static library to work for both iPhones and iPhone simulator, you will need to make a fat lib (basically a library containing symbols for both platforms)
如果你想让你的静态库同时适用于 iPhone 和 iPhone 模拟器,你需要制作一个胖库(基本上是一个包含两个平台符号的库)
If you are missing these platforms, you can download them from XCode (but I believe they are there)
如果您缺少这些平台,可以从 XCode 下载它们(但我相信它们在那里)
As you can see, things are going to get more and more complex along the way, so I strongly recommend to use XCode for compiling the static library (it is still doable with g++ thou).
正如你所看到的,事情会变得越来越复杂,所以我强烈建议使用 XCode 来编译静态库(它仍然可以使用 g++ thou)。
I believe the following concepts you would be useful to research upon :
我相信以下概念对您的研究很有用:
- arm, x86 , x86_64
- static library
- static linkage
- fat lib (universal library)
- XCode workspace with multiple projects
- 手臂,x86,x86_64
- 静态库
- 静态链接
- fat lib(通用库)
- 具有多个项目的 XCode 工作区
Hope this helps :).
希望这可以帮助 :)。
回答by us_david
Here is what worked for me in Xcode 9 for iOS devices (iPhone X):
1) Compile dylib with these flags set as the following:
a) "Installation Directory":
@executable_path/Frameworks
b) "Runpath Search Path":
@executable_path/Frameworks
See the picture bellow:
dylib settings in Xcode 9
以下是适用于 iOS 设备(iPhone X)的 Xcode 9 中对我
有用的内容:1)编译 dylib,这些标志设置如下:
a)“安装目录”:@executable_path/Frameworks b)“运行路径搜索路径”:@executable_path /Frameworks
见下图:
Xcode 9 中的 dylib 设置
2) In the Xcode project where the dylib is used/linked:
a) "Runpath Search Path":
@executable_path/Frameworks
b) In "Build Phase->Embed Libraries" make sure you select "Destination" as "Executables" and Subpath as "Frameworks", "Code sign on copy" checked:
Setting the the linking iOS app
2)在使用/链接dylib的Xcode项目中:
a)“运行路径搜索路径”:
@executable_path/Frameworks
b)在“构建阶段->嵌入库”中,确保选择“目标”作为“可执行文件”和子路径作为“框架”,“复制代码签名”已选中:
设置链接的 iOS 应用程序
This method is tested and used with Xcode 9.2 and iPhone X.
此方法已在 Xcode 9.2 和 iPhone X 上测试和使用。
David
大卫
回答by A.Badger
The script by Mobile Ben is great, apart from the final line hardcodes a library filename into the script. The following is a replacement for the last lipo command and dynamically uses lipo merges each library file created after compilation.
Mobile Ben 的脚本很棒,除了最后一行将库文件名硬编码到脚本中。下面是对上一个 lipo 命令的替换,并动态使用 lipo 合并编译后创建的每个库文件。
Change Mobile Ben's line:
更改移动本的线路:
$LIPO -create $pwd/output/armv7/lib/libpresage.a $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a
to:
到:
for t in `ls $pwd/output/armv7/lib/*.a` ;
do
export LIB=`basename $t`
export ARCHSTRING=""
for a in `ls $pwd/output`
do
export ARCSTRING="$ARCSTRING $pwd/output/$a/lib/$LIB "
done
$LIPO -create $ARCSTRING -output $LIB
done
Note: I didn't want to edit Mobile Ben's answer with new code functionality, and adding this as a comment lost formatting.
注意:我不想使用新的代码功能编辑 Mobile Ben 的答案,并将其添加为注释会丢失格式。
回答by debug
C++ Works on iOS, you can just add it in your project. Or if you really want to have your dynamic library, you can compile it with Xcode and specify your target architecture.
C++ 适用于 iOS,您只需将其添加到您的项目中即可。或者如果你真的想要你的动态库,你可以用 Xcode 编译它并指定你的目标架构。
回答by Nauman
Set your architecture back to default then try the following. 1. In Build Phases->Link Binary With Libraries add libz.dylib and libxml2.dylib libraries to your project. 2. In BuildSettings->Search Paths set Always Search User Paths to Yes, and under Framework Search Paths add the correct path of your framework. Use terminal to get the right path of your framework. 3. Try setting your "Compiler Source As" to C++ or use hit and trial and check with all options. Complier Source As is also under BuildSettings. Use cmd+f to search it.
将您的架构设置回默认值,然后尝试以下操作。1. 在 Build Phases->Link Binary With Libraries 中,将 libz.dylib 和 libxml2.dylib 库添加到您的项目中。2. 在 BuildSettings->Search Paths 中将 Always Search User Paths 设置为 Yes,然后在 Framework Search Paths 下添加您的框架的正确路径。使用终端获取框架的正确路径。3. 尝试将“Compiler Source As”设置为 C++ 或使用 hit and trial 并检查所有选项。Complier Source As 也在 BuildSettings 下。使用 cmd+f 搜索它。
Try these and let me know, also tell me about the framework or sdk that you are trying to use in your project.
尝试这些并让我知道,还告诉我您尝试在项目中使用的框架或 sdk。