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

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

我对使用 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

基于 this question ,我尝试将 Build Active Architecture Only 设置为 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)试试

./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: 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。这个构建很好。

我的步骤:

  • 将我的 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)

  • 我的 Objective-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++ 库,我更喜欢命令行。您只需要构建一次,如果您需要更新版本或添加新的架构切片,则只需重新访问它。

您想要的通用方法是:

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

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

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

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

有很多方法可以让它更流畅。例如使用数组来存储所有架构。这只是蛮力。

脚本的重点是:

  1. 获取最新的 SDK
  2. 构建每个切片
  3. 然后运行 ​​lipo

请注意,它应该是开箱即用的,但是,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++ - 编译外部 C++ 库以用于 iOS 项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27016612/

相关文章:

iphone - iPhone:如何使UITextField不可见但仍可单击?

ios - NSData 在 9.0 版中给我一个错误

ios - 如何在状态之间正确设置 UIButton 的动画?

c++ - 事件数据复制

c++ - glfwSetCursorPosCallback 在另一个类中运行

ios - Yozio 等服务如何在没有 UDID 的情况下跟踪单个用户的应用安装?

objective-c - 在第二个 View 中加载 Nib

objective-c - 检查 UIPickerView 是显示还是隐藏? *如果*条件?

c++ - 在 QToolBar 中移动 QMenuBar

c++ - QCanBusDevice 缓冲区刷新