Clang插件开发 - ShenYj/ShenYj.github.io GitHub Wiki

Clang插件开发

编译LLVM

准备

  • clone llvm项目

    llvm-project, 仓库地址中已经提供了编译步骤

  • CMake

clang、compiler-rt、libcxx 、libcxxabi 原始仓库以及早期版本的llvm仓库都已经归档,被整合到了llvm-project一个仓库下

编译llvm

进入到llvm-project 目录使用cmake进行编译,生成一个xcode项目

cmake -S llvm -B build -G Xcode -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"

直到编译成功结束

...
...
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/shenyj/Documents/Code/GitHubSources/llvm-project/build

这样一个llvm的xcode项目就生成了, 用xode打开llvm项目,分别编译clang和clangTooling两个target

CMake部分参数说明

参数 描述
-S Explicitly specify a source directory.
-B Explicitly specify a build directory.
-C Pre-load a script to populate the cache.
-D Create or update a cmake cache entry.
-U Remove matching entries from CMake cache.
-G Specify a build system generator.
-T Specify toolset name if supported by generator.
-A Specify platform name if supported by generator.
--DLLVM_ENABLE_PROJECTS=<...> semicolon-separated list of the LLVM subprojects you’d like to additionally build. Can include any of: clang, clang-tools-extra, lldb, compiler-rt, lld, polly, or cross-project-tests. For example, to build LLVM, Clang, libcxx, and libcxxabi, use -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi".

创建插件

clang和clangTooling两个target编译成功后就开始开始clang插件开发了

  • 进入到llvm-project/clang/tools路径下,这里存放的都是clang的插件,新建对应插件目录clang-plugin-demo

  • llvm-project/clang/tools路径下的CMakeLists.txt中最后面添加刚新建的目录,告诉clang需要加载的插件名称
    add_clang_subdirectory(clang-plugin-demo)

  • 准备插件资源文件

    <project dir>/
        |
        CMakeLists.txt
        <pass name>/
            |
            CMakeLists.txt
            Pass.cpp
            ...
    • 代码源文件 TestPlugin.cpp

    • CMake文件 CMakeLists.txt

      CMake文件中添加如下:
      add_llvm_library(clang-plugin-demo MODULE BUILDTREE_ONLY TestPlugin.cpp)

      • clang-test 插件的名称
      • TestPlugin.cpp 插件的代码文件

    LLVM项目中包含示例代码,在Loadable modules/LLVMHello下,另外很多已有clang插件都是学习和参照的资源

  • CMake重新生成一下Xcode项目

    回到llvm-project重新编译一遍

    增量编译,由于第一次我新建的clang-test对应target已存在,所以增量编译失败,所以给插件起名字的时候注意不要重名了

    编译失败log
      CMake Error at /Users/shenyj/Documents/Code/GitHubSources/llvm-project/clang/test/CMakeLists.txt:180 (add_custom_target):
      add_custom_target cannot create target "clang-test" because another target
      with the same name already exists.  The existing target is a module library
      created in source directory
      "/Users/shenyj/Documents/Code/GitHubSources/llvm-project/clang/tools/clang-test".
      See documentation for policy CMP0002 for more details.
    

    编译成功后重新打开LLVM项目,就可以看到自己插件的target了

    .

    这里面我命名的文件夹名称和cpp文件名不同,为的是测试验证,一个是路径名,一个是Module的名称,需要注意,另外经过测试发现CMamke编译后llvm项目中生成了两个同名scheme 在 Building LLVM with CMake - Developing LLVM passes out of source中有提到,前面是资源的路径,后面是Pass的名称,我理解的是对应的scheme

  • 在自定义插件target下开始编写代码

  • 编写完代码编译下这个target,在llvm-project/build 的LLVM项目统计目录下的Debug/lib中可以看到我们编译出的插件:clang-plugin-demo.dylib,一个动态库

测试插件

根据自己的需求通过C++编写完代码并生成clang插件后,在使用这个插件前我们需要注意一点,我们的插件是基于当前环境:llvm 和 clang的,使我们从github下载的一个指定版本,与我们本机xcode中内置的版本可能相同,也可能不同,这个很好理解,比如在使用OCLint做代码审查时,由于我们更新了Xcode版本,会同时升级内置的llvm以及clang版本,而在OCLint还未做更新时,这时就会出错,详情可以参考这篇笔记 OCLint 13.0 在Xcode 11下报错

在真正使用前,可以通过终端来测试一下制作的clang插件

命令格式如下:

当前环境llvm编译后clang文件所在路径 \
-isysroot sdk路径 \
-Xclang -load \
-Xclang clang插件(.dylib)路径 \
-Xclang -add-plugin \
-Xclang 插件名 \
-c 源码路径

当前demo实际命令

/Users/shenyj/Documents/Code/GitHubSources/llvm-project/build/Debug/bin/clang \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk \
-Xclang -load \
-Xclang /Users/shenyj/Documents/Code/GitHubSources/llvm-project/build/Debug/lib/clang-plugin-demo.dylib \
-Xclang -add-plugin -Xclang TestPlugin \
-c /Users/shenyj/Desktop/OCDemo/Test/Test/ViewController.m

使用插件

经过测试满足了我们需要达到的效果,就可以集成Xcode来使用了,毕竟日常开发是在Xcode下进行的

打开自己的项目,在Build Settings -> Other C Flags下添加如下内容:

-Xclang -load -Xclang clang(.dylib)动态库路径 -Xclang -add-plugin -Xclang H clang插件名称

这时我们直接运行项目会build失败, 关键字flat namespace

编译失败log
error: unable to load plugin '/Users/shenyj/Documents/Code/GitHubSources/llvm-project/build/Debug/lib/clang-plugin-demo.dylib': 'dlopen(/Users/shenyj/Documents/Code/GitHubSources/llvm-project/build/Debug/lib/clang-plugin-demo.dylib, 0x0009): symbol not found in flat namespace '__ZN4llvm15SmallVectorBaseIjE13mallocForGrowEmmRm''
/Users/shenyj/Library/Developer/Xcode/DerivedData/WorkSpace-epmwormffbdoiaermqldmxamnvsq/Build/Intermediates.noindex/Test.build/Debug-iphonesimulator/Test.build/Objects-normal/x86_64/main.dia:1:1: warning: Could not read serialized diagnostics file: error("Failed to open diagnostics

这就是前面我们提到的环境问题,插件版本不同步导致

  • 同步clang版本

    在Build Settings 栏目中新增两项用户定义设置(Add User-Defined Setting):

    分别是CC和CXX

    • CC对应的是自己编译的clang的绝对路径
    • CXX对应的是自己编译的clang++的绝对路径

    .

  • 接下来在 Build Settings栏目中搜索 index,将 Enable Index-Wihle-Building Functionality的Default改为NO

    .

接下来就可以在Xcode中使用这个clang插件了

优化

目前仅仅是实现了本地环境下集成并使用了自己开发的clang插件, Build Setting设置是零散的,通过xcconfig来配置会更加醒目和方便

学习

  1. C++ 编写clang 插件的能力
  2. 本地编译后的clang插件如何能多端使用
⚠️ **GitHub.com Fallback** ⚠️