编译外部C ++库以用于iOS项目

时间:2014-11-19 12:16:04

标签: c++ ios objective-c xcode objective-c++

我对使用C ++库完全陌生,所以请注意这可能对我的情况有点具体(让我知道,我可以提供更多细节)。

我有一个外部C ++库,我试图在iOS项目中使用它。该库遵循configure,make,make构建模式以输出.a库文件。当我尝试将此库文件添加到Xcode时,我收到以下错误:

  

忽略文件   /Users/Developer/iOS/TestProj/libpresage.a,文件是   为存档而构建,而不是被链接的架构(i386):

     

/Users/Developer/iOS/TestProj/libpresage.a

基于this question,我尝试将Build Active Architecture Only转为NO,我得到了同样的错误。这让我怀疑我为不正确的架构编译了库。

在.a文件上运行lipo -info给出:

  

输入文件libpresage.a不是胖文件非胖文件:libpresage.a

     

是架构:x86_64

鉴于这不是armv7s,armv7或arm64,我尝试使用以下参数再次编译C ++库:

1)尝试

./configure CC="gcc -arch armv7s" \
                 CXX="g++ -arch armv7s" \
                 CPP="gcc -E" CXXCPP="g++ -E"

编译时出错,我得到:

ld: library not found for -lcrt1.3.1.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

2)尝试

./configure CC="gcc -arch arm64" \
                 CXX="g++ -arch arm64" \
                 CPP="gcc -E" CXXCPP="g++ -E"

编译时出错,我得到:

  

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片)忽略文件   /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib,   缺少文件中所需的架构arm64   /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib   (2片)

     

ld:动态主要可执行文件必须与libSystem.dylib链接   架构arm64 clang:错误:链接器命令失败,退出代码   1(使用-v查看调用)

我有什么明显的遗失吗?

修改

感谢您的回复,因此我设法将图书馆作为自定义构建目标放入Xcode,并指出了“#' make'命令库MakeFile。这个构建很好。

我的步骤:

  • 从Objective C iOS应用目标添加依赖项到自定义构建目标。
  • 引用库并制作Objective C ++包装器。
  • 这似乎没问题,直到我需要调用外部C ++库,然后在编译时遇到错误:
  

架构armv7的未定义符号:    " Presage :: Presage(PresageCallback *)",引自:         - PresageBridge.o中的[PresageBridge init]    " Presage :: ~Presage()",引自:         - PresageBridge.o中的[PresageBridge init]   ld:找不到架构armv7的符号   clang:错误:链接器命令失败,退出代码为1(使用-v查看调用)

  • 我的目标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。

6 个答案:

答案 0 :(得分:30)

所以我在iOS项目中使用了很多第三方C ++库。人们使用不同的策略。正如一些人已经提到的,您可以直接在项目中包含代码,使用Xcode构建静态库,或者构建命令行。对于使用GNU配置和构建系统的跨平台C ++库,我更喜欢命令行。您只需要构建一次,如果需要更新版本或添加新的体系结构切片,则只需重新访问它。

您想要的通用方法是:

  • 找出用于构建每个切片的正确配置参数。通常情况下,您只需要专注于让其中一个手臂以及i386正常工作。其余的很容易就可以完成。在某些情况下,您实际上需要修改配置文件以添加主机或进行其他一些调整。

  • 一旦你可以构建所有切片,你想运行lipo来构建一个胖二进制文件。

然后处理此问题的最佳方法是创建一个构建脚本,它将为您完成所有工作。这样,重做更容易。更重要的是,您可以重用脚本或置换脚本来构建其他外部库。

您可以通过多种方式构建脚本。这是一个。我碰巧有这种类型的脚本的几种变体。该脚本用于构建cURL。它或多或少地用于预设,具有非常少的mod(即改变卷曲到预期)。注意我没有在Xcode中测试它(即链接它并运行它)。我确实发现我必须禁用sqlite,否则它会构建不正确构建的工具项。如果你需要它,你可以找出那部分。

有很多方法可以让它更光滑。例如,使用数组来存储所有体系结构。这只是蛮力。

脚本的关键点是:

  1. 获取最新的SDK
  2. 构建每个切片
  3. 然后运行lipo
  4. 请注意,它应该开箱即用,但YMMV。如有必要,请准备好进行调试。例如,我还没有确认主机类型,但通常这是我一直使用的。你想把它放在预告的目录(配置相同的目录)。完成后,所有体系结构都在输出目录中。通用lib位于presage目录中。

    另外请记住,您有责任正确链接通用库,并正确定义头文件搜索路径。

    #!/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
    

答案 1 :(得分:6)

考虑到你是C ++库的新手,我想你需要做更多的研究。

但是,我将尝试概述您需要考虑的一些步骤:

  • 您需要确保为静态库(.a)和项目
  • 编译相同的体系结构
  • 从您的错误中,您需要为i386编译静态库或将项目更改为x86_64(这些体系结构之间的差异有点复杂,但现在让我们说i386意味着桌面32位而x86_64意味着桌面64位)
  • arm架构适用于iPhone,不适用于MacOS(这就是为什么它无法在MacOSX文件夹中找到带有arm架构的库)!

有多种方法可以解决这些问题。

对于第一个我建议将静态库包含到您的工作区中,并将其作为依赖项添加到您的构建目标。为此,您需要了解XCode构建。

我猜你实际上是在尝试创建一个手机应用程序,所以对于第三个选项,你需要配置你的g ++构建,以便在链接arm目标(看看iPhoneOS.platform)时从XCode查看iPhoneSDK

进行手臂构建仅适用于iPhone。如果您希望它在模拟器上运行,则需要将静态库链接到iPhoneSimulator.platform中的库。

如果你想让你的静态库适用于iPhone和iPhone模拟器,你需要制作一个胖lib(基本上是一个包含两个平台符号的库)

如果您缺少这些平台,可以从XCode下载它们(但我相信它们在那里)

正如您所看到的,在此过程中事情将变得越来越复杂,所以我强烈建议使用XCode来编译静态库(它仍然可以用g ++进行编译)。

我相信您可以使用以下概念进行研究:

  • arm,x86,x86_64
  • 静态库
  • 静态链接
  • fat lib(通用库)
  • 具有多个项目的XCode工作区

希望这会有所帮助:)。

答案 2 :(得分:2)

以下是Xcode 9 for iOS设备(iPhone X)的用途:
1)使用以下设置的标志编译dylib:
  a)"安装目录":      @ executable_path /框架   b)"运行路径搜索路径":      @ executable_path /框架
  见下图:
dylib settings in Xcode 9

2)在使用/链接dylib的Xcode项目中:
  a)" Runpath搜索路径":
     @ executable_path /框架
  b)在"构建阶段 - >嵌入库"确保选择"目的地" as"可执行文件"和子路径为"框架","代码签名复制"检查:
Setting the the linking iOS app

此方法已经过测试,可与Xcode 9.2和iPhone X一起使用。

大卫

答案 3 :(得分:0)

C ++适用于iOS,您只需将其添加到项目中即可。或者,如果您真的想拥有动态库,可以使用Xcode编译它并指定目标体系结构。

答案 4 :(得分:0)

将您的架构恢复为默认值,然后尝试以下操作。 1.在Build Phases-> Link Binary With Libraries中将libz.dylib和libxml2.dylib库添加到您的项目中。 2.在BuildSettings-> Search Paths中将Always Search User Paths设置为Yes,并在Framework Search Paths下添加框架的正确路径。使用终端获取框架的正确路径。 3.尝试将"编译器源设置为"到C ++或使用点击和试用,并检查所有选项。 Complier Source As也在BuildSettings下。使用cmd + f进行搜索。

尝试这些并让我知道,也告诉我您正在尝试在项目中使用的框架或sdk。

答案 5 :(得分:0)

Mobile Ben的脚本很棒,除了最后一行硬编码脚本中的库文件名。以下是最后一个lipo命令的替换,并动态使用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

为:

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

注意:我不想使用新代码功能编辑Mobile Ben的答案,并将其添加为评论丢失格式。