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).
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:
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
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:
input file libpresage.a is not a fat file Non-fat file: libpresage.a
is architecture: x86_64
Given that this isn't armv7s, armv7, or arm64, I try and compile the C++ library again with the following parameters:
1) Try
./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
./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)
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.
My steps from here:
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)
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.
Go to the Project menu. Go to Build Options... In the options dialog, select the Linker Settings tab. Use the Add button to select a library and add it to your project.
If the C++ compiler provides its own versions of the C headers, the versions of those headers used by the C compiler must be compatible. Oracle Developer Studio C and C++ compilers use compatible headers, and use the same C runtime library. They are fully compatible.
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.
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.
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.
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:
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.
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/$1.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=$1 hosttarget=$1 platform=$2 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With