c++ 如何从Objective-C Xcode项目中链接和使用LLVM库?

3pmvbmvn  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(179)

我想在我自己的Objective-C应用程序中使用LLVM项目中的一些类。具体来说,我想使用BitstreamReader.hBitstreamWriter.h中声明的类。不幸的是,我没有太多链接C++库的经验,所以我真的不知道从哪里开始。我开始是通过Homebrew使用brew install llvm@14安装llvm。然后,在我的Xcode项目中,我尝试链接/opt/homebrew/opt/llvm@14/lib中的库,并将/opt/homebrew/opt/llvm@14/include/**添加到我的HEADER_SEARCH_PATHS中。
现在我完全卡住了。我在Xcode中有一堆构建错误,比如:

  • “引用未解析的using声明”
  • “全局命名空间中没有名为'ldiv'的成员”
  • “使用未声明的标识符'wcspbrk'”
  • ...

任何帮助都将不胜感激。谢谢!

jtoj6r0c

jtoj6r0c1#

在C++的世界里,有很多种方法可以将依赖关系合并到你的项目中,我将概述其中的一些,但会尝试详细描述我发现最适合你的场景的一种:

1.安装库系统/用户范围

这是最经典的方法,当你为你的特定平台预编译和预安装了库,这样它们就可以在默认的库搜索路径下使用。这类似于iOS中系统框架的使用-你只需要向项目添加链接器命令,而框架(库)独立于它而存在(或者不存在,这会导致链接器错误)。这种方法的问题是,您不能真正将库用于受限系统,其中“默认”库不可更改(iOS、tvOS、iPadOS)

2.嵌入静态库

另一种方法是将你需要的所有库预编译到所有平台的归档文件中,然后只将其中一个库版本嵌入到你最终的应用二进制文件中。这种方法有点麻烦,不便于移植,因为每次你需要修改库的某些部分或支持新平台时,你都需要手动编译库并重新嵌入。

3.嵌入一个xcframework

这种方法与前一种方法非常相似,但有一些好处,您可以将所有平台的二进制文件 Package 在一个包中,甚至可以在SPM中发布它。

4.使用CMake构建系统

C++中的许多项目(LLVM也不例外)都使用所谓的CMake工具。它是广泛采用的多平台构建系统,您可以使用它的好处之一是,您可以轻松地将任何其他项目作为自己项目的一部分。同时,CMake在移动的开发领域并不普遍,因此您可能很难找到这些平台的相关信息。

5.使用Xcode工作区

这就是我想要更详细描述的解决方案。简而言之,这个方法包括6个步骤:

  • 下载LLVM项目报告;
  • 使用CMake为所需的LLVM库生成Xcode项目;
  • 创建一个Xcode工作区,并向其中添加新生成的项目;
  • 将您自己的项目添加到同一工作区;
  • 将LLVM项目中的必需依赖项添加到您自己的项目中;
  • 调整项目设置以使其与依赖项兼容。

这种方法的好处在于它提供了最一致的体验(生成项目后,所有部分都可以从Xcode配置)、管理源代码的自由(LLVM项目文件可以随意更改)以及内置依赖关系图的奢华(构建目标所需的所有库都在Xcode设置下提供)。

正在下载LLVM项目

您可以从here克隆项目。请注意,这必须位于您将来的工作区所在的同一个文件夹中,因此您最好提前做好准备:

% mkdir MyWorkspace && cd MyWorkspace
% git clone git@github.com:llvm/llvm-project.git
正在生成LLVM Xcode项目

首先,确保你已经安装了CMake(它在macOS中不是开箱即用的)。你可以使用brew或者直接从the official site下载应用。然后,在llvm-project repo文件夹旁边为将来的Xcode项目创建一个新文件夹,并在LLVM工具包项目上运行cmake

% mkdir LLVM && cd LLVM
% cmake -GXCode ../llvm-project/llvm
创建Xcode工作区

这里没有什么花哨的东西,只要打开Xcode并导航到File/New/Workspace菜单(Ctrl+Cmd+N 快捷方式)。确保您的工作区是在MyWorkspace文件夹中创建的。您可以直接从Xcode中执行此操作:

然后添加上一步创建的LLVM项目,打开File/Add Files to "MyWorkspace"...菜单(Option+Cmd+A),选择Xcode项目文件:

如果Xcode建议自动创建方案,我建议接受这个建议,这样你就不必在以后自己处理它。

添加您自己的项目

这一步与前一步类似,只是你需要将自己的项目添加到工作区中。我没有一个现成的项目,所以我只是在工作区目录中创建了一个新项目(在我的例子中是macOS命令行应用程序)。如果你做了同样的操作,请确保该项目已添加到“MyWorkspace”中,并且文件夹是正确的:

最终,您的工作空间“项目导航器”应如下所示:

下面是目录树的简要外观:

% tree -L 2
.
|-- LLVM
|   |-- $(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|   |-- CMakeCache.txt
|   |-- CMakeFiles
|   |-- CMakeScripts
|   |-- CPackConfig.cmake
|   |-- CPackSourceConfig.cmake
|   |-- Debug
|   |-- LLVM.xcodeproj
|   |-- MinSizeRel
|   |-- RelWithDebInfo
|   |-- Release
|   |-- benchmarks
|   |-- build
|   |-- cmake
|   |-- cmake_install.cmake
|   |-- docs
|   |-- examples
|   |-- include
|   |-- lib
|   |-- llvm.spec
|   |-- projects
|   |-- runtimes
|   |-- test
|   |-- third-party
|   |-- tools
|   |-- unittests
|   `-- utils
|-- MyProject
|   |-- MyProject
|   `-- MyProject.xcodeproj
|-- MyWorkspace.xcworkspace
|   |-- contents.xcworkspacedata
|   |-- xcshareddata
|   `-- xcuserdata
`-- llvm-project
    |-- CONTRIBUTING.md
    |-- LICENSE.TXT
    |-- README.md
    |-- SECURITY.md
    |-- bolt
    |-- clang
    |-- clang-tools-extra
    |-- cmake
    |-- compiler-rt
    |-- cross-project-tests
    |-- flang
    |-- libc
    |-- libclc
    |-- libcxx
    |-- libcxxabi
    |-- libunwind
    |-- lld
    |-- lldb
    |-- llvm
    |-- llvm-libgcc
    |-- mlir
    |-- openmp
    |-- polly
    |-- pstl
    |-- runtimes
    |-- third-party
    `-- utils
向项目添加依赖项

从现在开始,您只需要Xcode就可以完成这项工作。让我们将所需的库链接到项目,并从Bitstream存档开始。打开项目的目标“常规”选项卡设置,然后在框架和库下单击**+**符号,该符号应将您带到显示当前所有可用本地依赖项的屏幕。它'LLVM项目+原生Apple库中的工具列表相当长,因此您可能希望根据需要对其进行过滤,以找到所需的位置:

现在是棘手的部分。实际上你可能不需要这样做,但是如果LLVMBitstreamReader目标本身依赖于其他库的符号(并且通过显式使用库符号来暴露这样的依赖关系),项目链接将失败,所以为了安全起见,请转到LLVMBitstreamReader构建设置,并在Target Dependencies部分检查它依赖于什么:

现在将这些依赖项也添加到您的项目中。最终您的Frameworks and Libraries部分应该如下所示:

调整项目设置
  • CMake生成的Xcode项目有一些特殊之处,在我们的例子中,LLVM项目目标的构建文件夹位于项目目录下,其名称与所选配置匹配(Debug/Release/MinSizeRel/RelWithDebInfo).为了让您自己的项目的目标找到使用给定配置构建的库,您必须调整LLVM项目/目标设置,我不推荐这样做,因为这需要大量的手工工作,或者只是添加自定义库搜索路径到您的项目。对于后者,请转到您在上一步添加依赖项的目标的Build Settings,找到Library Search Path项并添加$(SRCROOT)/../LLVM/$(CONFIGURATION)/lib作为新项:

  • 另一个棘手的部分是,如果依赖的目标有 * 不兼容的构建设置 ,依赖的目标将不会构建依赖。 不兼容 * 在这里是一个模糊的术语,但通常它意味着匹配架构 * 部分。幸运的是,在我们的例子中,它仅仅意味着使发布**配置只构建活动架构(调试不需要任何更改):

  • 最后但并非最不重要的是库的头文件,在生成LLVM Xcode项目之后,它提供的头文件不(完全)随项目一起提供,实际上位于存储库文件夹中。您可以直接将标题复制到您自己的项目中,但我建议在系统头搜索路径中添加repo目录,以与LLVMXcode项目设置保持一致(你可以自己在LLVMBitstreamReaderBuild Settings下查看header搜索路径在哪里),类似于lib搜索路径,我建议借助设置变量的帮助,灵活添加此路径:
$(SRCROOT)/../llvm-project/llvm/include
$(SRCROOT)/../LLVM/include

大结局

现在你应该可以使用LLVMBitstreamReader库和其中定义的类了,我把我的main.m重命名为main.mm,这样clang就知道我将在代码中使用C++,下面是我的示例代码:

//
//  main.mm
//  MyProject
//
//  Created by Aleksandr Medvedev on 14.12.2022.
//

#import <Foundation/Foundation.h>
#import <llvm/Bitstream/BitstreamReader.h>
#import <iostream>

int main(int argc, const char * argv[]) {
    llvm::BitstreamBlockInfo::BlockInfo info;
    info.Name = "Hello, LLVM!";
    std::cout << info.Name << std::endl;
    
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

如果一切都做对了,现在编译应该没有任何错误。

**P.S.**我多次关注特定的目录层次结构是有原因的--当XCode项目通过CMake生成时,它通常使用绝对路径,而不是相对路径,如果您将项目移动到另一个目录,您将不得不在所需目标/项目的构建设置中调整路径,或者再次重复生成LLVM Xcode项目的步骤。

相关问题