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

Compiling external C++ library for use with iOS project

本文关键字:用于 iOS 项目 外部 C++ 编译      更新时间:2023-10-16

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

我有一个外部C++库,我正在尝试将其与 iOS 项目一起使用。该库遵循配置、制作、制作构建模式来输出 .a 库文件。当我尝试将此库文件添加到 Xcode 时,出现以下错误:

忽略文件 /Users/Developer/iOS/TestProj/libpresage.a, file was 为未链接的体系结构的存档而构建 (i386):

/

users/developer/iOS/TestProj/libpresage.a

基于这个问题,我尝试将"仅构建活动架构"转换为"否",但遇到了相同的错误。这让我怀疑我为不正确的架构编译了库。

在 .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/Content/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib, 文件中缺少所需的体系结构 arm64 /Applications/Xcode.app/Content/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib (2 个切片)忽略文件 /applications/Xcode.app/Content/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib, 文件中缺少所需的体系结构 arm64 /applications/Xcode.app/Content/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 应用目标添加到自定义生成目标。
  • 引用库并制作目标C++包装器。
  • 这似乎很好,直到我需要调用外部C++库,然后在编译时出现错误:

架构 armv7 的未定义符号: "Presage::P resage(PresageCallback*)",引用自: -[PresageBridge init] in PresageBridge.o "Presage::~Presage()",引用自: -[PresageBridge init] in PresageBridge.o 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。

所以我

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

所需的通用方法是:

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

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

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

您可以通过多种方式生成脚本。这是其一。我碰巧有这种类型的脚本的几种变体。此脚本用于构建 cURL。它或多或少适用于 presage 和很少的 mod(即将 curl 更改为 presage)。请注意,我没有在 Xcode 中测试它(即链接它并运行它)。我确实发现我必须禁用 sqlite,否则它构建的工具项无法正确构建。如果你需要它,你可以弄清楚那部分。

有很多方法可以使其更加流畅。例如,使用数组来存储所有体系结构。这只是蛮力。

脚本的关键点是:

  1. 获取最新的软件开发工具包
  2. 构建每个切片
  3. 然后运行脂肪

请注意,它应该开箱即用,但是,YMMV。如有必要,请准备好对其进行调试。例如,我还没有确认主机类型,但通常这就是我一直使用的主机类型。您想将其放在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

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

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

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

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

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

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

制作手臂构建仅适用于iPhone。如果你想让它在模拟器上工作,你需要将你的静态库链接到iPhoneSimulator.platform内的库。

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

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

如您所见,在此过程中,事情会变得越来越复杂,所以我强烈建议使用 XCode 来编译静态库(使用 g++ thou 仍然可以使用

)。

我相信以下概念对研究是有用的:

  • 手臂, x86 , x86_64
  • 静态库
  • 静态联动
  • 胖库(通用图书馆)
  • 具有多个项目的 XCode 工作区

希望这对:)有所帮助。

以下是适用于

iOS设备的Xcode 9(iPhone X)中对我有用的内容:
1) 使用这些标志编译 dylib,如下所示:
a) "安装目录": @executable_path/框架 b) "运行路径搜索路径": @executable_path/框架
请看下面的图片:
Xcode 9 中的 dylib 设置

2) 在使用/链接 dylib 的 Xcode 项目中:
a) "运行路径搜索路径":
@executable_path/框架
b) 在"构建阶段>嵌入库"中,确保选择"目标"作为"可执行文件",子路径作为"框架","复制代码签名"选中:
设置链接的 iOS 应用

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

大卫

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的答案,并将其添加为注释会丢失格式。

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

将体系结构设置回默认值,然后尝试以下操作。1. 在构建阶段 ->链接二进制文件与库,将 libz.dylib 和 libxml2.dylib 库添加到您的项目中。2. 在"生成设置->搜索路径"中,将"始终搜索用户路径"设置为"是",然后在"框架搜索路径"下添加框架的正确路径。使用终端来获取框架的正确路径。3. 尝试将"编译器源设置为"C++或使用点击和试用并检查所有选项。编译器源 As 也在"构建设置"下。使用 cmd+f 进行搜索。

试试这些,让我知道,还告诉我你尝试在项目中使用的框架或 sdk。