Clang Plugin 之 Debug

前面一篇文章 LLVM & Clang 入门 讲了如何编写一个 Clang 插件,而后将插件编译成一个.dylib的动态连接库。集成到 Xcode 中就能够看到效果(正确的结果)。html

在获得正确结果的过程当中,必不可少的一步就是Debug,没有任何程序是一蹴而就的,除非你printf一个"Hello, World!",说不定你的world还写成了word前端

在使用Plugin的模式下咱们是不能打断点进行 Debug 的,可是咱们能够在代码中加日志,而后在终端中执行命令看日志进行 Debug。这种低效率(Low)的方式是你想要的吗?显然不是。c++

咱们只须要把.dylib动态库变成可执行文件就能打断点 debug。LibTooling 或许是一个不错的选择。使用 LibTooling 的话,咱们只须要改动不多部分的代码就能够。git

LibTooling 简介github

LibTooling 是一个独立的库,它容许使用者很方便地搭建属于你本身的编译器前端工具,它基于 C++ 接口,提供给使用者强大全面的 AST 解析和控制能力,同时因为它与 Clang 的内核过于接近致使它的版本兼容能力比 libclang 差得多,Clang 的变更很容易影响到 LibTooling。libTooling 还提供了完整的参数解析方案,能够很方便的构建一个独立的命令行工具。xcode

建立 LibTooling 项目及代码调整

为了方便,咱们直接建立一个可执行的LibTooling项目,咱们能够建立一个名为QTPluginTooling的项目。bash

  1. 建立过程跟 建立插件 步骤差很少,前面 3 步都是同样的,只须要把QTPlugin替换为QTPluginTooling就能够。app

  2. 只是在第 4 步略有不一样,QTPluginTooling目录下的CMakeLists.txt的文件内容为函数

    set(LLVM_LINK_COMPONENTS
        Support
    )
    
    add_clang_executable(QTPluginTooling
        QTPluginTooling.cpp
    )
    
    target_link_libraries(QTPluginTooling
        PRIVATE
        clangAST
        clangBasic
        clangDriver
        clangFormat
        clangLex
        clangParse
        clangSema
        clangFrontend
        clangTooling
        clangToolingCore
        clangRewrite
        clangRewriteFrontend
    )
    
    if (UNIX)
        set(CLANGXX__LING_OR_COPY create_symlink)
    else()
        set(CLANGXX_LINK_OR_COPY copy)
    endif()
    复制代码

    tooling_cmakeLists

  3. llvm_xcode目录下执行$ cmake -G Xcode ../llvm,从新生成一下Xcode项目。Tooling项目在 Xcode 的Clang executables目录下能够找到。工具

  4. 将以前 Plugin 的代码复制过来,新增三个头文件

    #include "clang/Tooling/CommonOptionsParser.h"
    #include "clang/Frontend/FrontendActions.h"
    #include "clang/Tooling/Tooling.h"
    复制代码
  5. 新增一个命名空间

    using namespace clang::tooling;
    复制代码
  6. QTASTAction的继承改成继承至ASTFrontendAction

  7. FrontendPluginRegistry注册插件的方式注释。更改成main()函数方式

    static llvm::cl::OptionCategory OptsCategory("QTPlugin");
    int main(int argc, const char **argv) {
        CommonOptionsParser op(argc, argv, OptsCategory);
        ClangTool Tool(op.getCompilations(), op.getSourcePathList());
        return Tool.run(newFrontendActionFactory<QTPlugin::QTASTAction>().get());
    }
    复制代码

最后整个文件的内容能够在 QTPluginTooling.cpp 看到。

输入源

若是这时候就run的话则会直接退出。这是由于没有“输入源”。咱们能够在QTPluginToolingScheme加入。

/Users/laiyoung_/Desktop/Plugin/ViewController.m
--
-isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
-isystem
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks
复制代码

参数解释

上面在--后面的参数,是传递给CICompilation DataBase的,而不是这个命令行工具自己的。好比咱们的ViewController.m,由于有#import <UIKit/UIKit.h>这么一条语句,以及继承了UIViewController,那么语法分析器(Sema)读到这里的时候就须要知道UIViewController的定义是从哪里来的,换句话说就是它须要找到定义UIViewController的地方。怎么找呢?经过指定的-I-F这些参数指定的目录来寻找。--后面的参数,能够理解为若是你要编译ViewController.m须要什么参数,那么这个后面就要传递什么参数给咱们的 QTPlugin,不然就会看到Console里打出找不到xxx定义或者xxx.h文件的错误。固然由于通常的编译指令,会有-c参数指定源文件,可是--后面并不须要,由于咱们在--前面就指定了。--这种传参的方式还有另一种方法,使用-extra-arg="xxxx"的方式指定编译参数,这样就不须要--了。

-extra-arg="-Ixxxxxx"
-extra-arg="-Fxxxxxx"
-extra-arg="-isysroot xxxxxx"
xxxxxx表示的路径
复制代码

最终效果:

参考文章

若有内容错误,欢迎 issue 指正。

Example

转载请注明出处!

相关文章
相关标签/搜索