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

Compiling external C++ library for use with iOS project

我对使用C++库是全新的,所以欣赏这一点可能对我的情况有点具体(让我知道,我可以提供更多的细节)。

我有一个外部C++库,我想用IOS项目来使用它。库遵循配置、生成、生成生成模式以输出.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

基于这个问题,我尝试将build active architecture改为no,得到了相同的错误。这使我怀疑我为错误的体系结构编译了库。

在.a文件上运行lipo-info可以提供:

input file libpresage.a is not a fat file Non-fat file: libpresage.a

is architecture: x86_64

由于这不是ARMV7S、ARMV7或ARM64,所以我尝试用以下参数重新编译C++库:

1)尝试

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

编译时出错,我得到:

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

2)尝试

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

编译时出错,我得到:

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)

有什么明显的东西我不见了吗?

编辑:

感谢您的回复,所以我已经设法将库作为自定义构建目标导入Xcode,并将"make"命令指向库makefile。这个建筑很好。

我的步骤:

  • 从我的目标C iOS应用程序目标添加到自定义生成目标的依赖项。
  • 引用库并制作一个目标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)

  • 我的目标C++封装器(链接外部C++库头预置。h):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #import"PresageBridge.h"
    #include"presage.h"

    @implementation PresageBridge

    - (instancetype)init
    {
        if(self = [super init])
        {

           Presage hello(&callback);
        }

        return self;
    }
  • 基于上面的代码,我似乎没有丢失头,有趣的是我还尝试在外部库中创建了其他类的实例,它们似乎在工作,这表明Xcode不能正确地链接presage.h,因为某种原因。


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

您需要的通用方法是:

  • 找出用于构建每个切片的正确配置参数。通常,您只需要专注于获得一个手臂以及i386工作。剩下的很简单,你已经做到了。在某些情况下,您实际上需要修改配置文件以添加主机或进行其他调整。

  • 一旦你可以建立所有切片,你想运行lipo建立一个脂肪二元体。

最好的方法是创建一个构建脚本,它将为您完成所有工作。这样,就更容易重做。更重要的是,您可以重用该脚本或将其置换为构建其他外部libs。

有许多方法可以构建脚本。这里有一个。我碰巧有几种不同类型的脚本。这个脚本是用来构建curl的。它或多或少地适用于预赛与很少的mod(即改变卷曲为预赛)。注意,我没有在Xcode中测试它(即链接它并运行它)。我发现我必须禁用sqlite,否则它会生成不正确的工具项。如果你需要的话,你可以找出那部分。

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

脚本的关键点是:

  • 获取最新的SDK
  • 构建每个切片
  • 然后跑脂
  • 但是,请注意,它应该是开箱即用的,YMMV。如有必要,请做好调试准备。例如,我还没有确认主机类型,但通常这是我一直使用的类型。您希望将它放在用于预分页的目录中(与配置的目录相同)。完成后,所有架构都在输出目录中。通用库在预装配目录中。

    还请记住,在通用库中正确链接以及正确定义头文件搜索路径是您的责任。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    #!/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


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

    不过,我将尝试概述一些需要考虑的步骤:

    • 您需要确保为静态库(.a)和项目编译相同的体系结构。
    • 根据您的错误,您需要为i386编译静态库或将项目更改为x86_(这些体系结构之间的区别比较复杂,但现在假设i386表示桌面32位,而x86_表示桌面64位)。
    • ARM架构是针对iPhone的,而不是针对您的MacOS(这就是为什么它在MacOSX文件夹中找不到具有ARM架构的库的原因)!

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

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

    我猜你真的想做一个电话应用程序,所以对于第三个选项,你需要配置你的G++构建,以便在链接ARM目标(照顾iphoneos.platform)时从xcode查看iphoneskd。

    制造一个手臂只能在iPhone上工作。如果您希望它在模拟器上工作,您需要将静态库链接到iphoneSimulator.platform内的库。

    如果您希望静态库同时适用于iPhone和iPhone模拟器,则需要创建一个胖库(基本上是一个包含两个平台符号的库)。

    如果你缺少这些平台,你可以从xcode下载它们(但我相信它们确实存在)。

    正如您所看到的,一路上事情会变得越来越复杂,所以我强烈建议使用Xcode编译静态库(它仍然可以用g++you实现)。

    我相信您将有助于研究以下概念:

    • ARM,x86,x86_
    • 静态库
    • 静态连杆机构
    • fat lib(通用库)
    • 具有多个项目的Xcode工作区

    希望这有帮助:)。


    以下是Xcode9在iOS设备(iPhoneX)中的作用:1)使用以下设置的标志编译dylib:a)"安装目录":@可执行路径/框架b)"运行路径搜索路径":@可执行路径/框架见下图:xcode 9中的dylib设置

    2)在使用/链接dylib的xcode项目中:a)"运行路径搜索路径":@可执行路径/框架b)在"构建阶段->嵌入库"中,确保选择"目标"作为"可执行文件",子路径作为"框架",选中"代码登录副本":设置链接iOS应用程序

    此方法经过测试,并与Xcode9.2和iPhoneX一起使用。

    戴维


    移动本编写的脚本很棒,除了最后一行将库文件名硬编码到脚本中。下面是对最后一个lipo命令的替换,并动态地使用lipo合并编译后创建的每个库文件。

    更改手机本的线路:

    1
    $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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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的答案,并将其添加为注释丢失格式。


    将架构设置回默认值,然后尝试以下操作。1。在构建阶段->使用库链接二进制文件,将libz.dylib和libxml2.dylib库添加到项目中。2。在buildsettings->search path中,始终将search user path设置为yes,并在framework search path下添加框架的正确路径。使用终端获取框架的正确路径。三。尝试将你的"编译器源"设置为C++或使用命中和试用,并检查所有选项。编译源也在buildsettings下。使用cmd+f搜索它。

    尝试这些方法,让我知道,同时告诉我您在项目中尝试使用的框架或SDK。


    C++在iOS上工作,你可以把它添加到你的项目中。或者,如果您真的想拥有动态库,您可以使用Xcode编译它并指定目标体系结构。