美文网首页
iOS 开发 Clang 插件的配置及流程

iOS 开发 Clang 插件的配置及流程

作者: AndyGF | 来源:发表于2020-11-15 02:24 被阅读0次

今天初步学习了 Clang 插件的开发, 特此小结一下. 如果哪里不当, 还请指出, 感激不尽, 😄...

  • 场景 :
    如果在 OC 项目中, 使用 @property 声明 NSString, NSArray, NSDictionary 三种对象时没有使用 copy 关键字修饰, 就给出警告⚠️信息, 如: --------- NSString * 没用 copy 修饰 ---------

    警告
  • 分析 :
    创建一个插件名称为 GFPlugin 的插件来实现此警告功能.

  • 由于是第一次练习插件编写, 所以需要配置插件开发环境, 整个配置过程和实现过程如下 :

主要分为以下 5 大步骤 :

  1. LLVM 下载
  2. LLVM 编译
  3. 创建插件
  4. 编写插件代码并编译
  5. 插件的测试和集成

LLVM下载 --------------------

由于国内的网络限制, 我们需要借助镜像下载 LLVM 的源码.
https://mirror.tuna.tsinghua.edu.cn/help/llvm/
首先要 cd 到存放 llvm 项目的位置, 然后再开始下载
以下命令需要一个执行完成之后再执行下一个, 有的下载可能会慢.

  • 下载 llvm 项目
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git

  • llvmtools 目录下下载 Clang
    cd llvm/tools
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git

  • llvmprojects目录下依次下载 compiler-rt, libcxx, libcxxabi
    cd ../projects
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.git
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git

  • Clangtools 目录下安装 extra 工具
    cd ../tools/clang/tools
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-extra.git

LLVM编译 --------------------

通过 Xcode 编译 LLVM

  1. 使用 cmake 编译成 Xcode 项目
# 创建 build_xcode 文件夹
mkdir build_xcode
cd build_xcode
# 编译 llvm
cmake -G Xcode ../llvm

build_xcodellvm 同级.

  • 如果使用 cmake -G Xcode ../llvm 时遇到如下报错信息 , 请点击这里 : 查看解决方案
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
-- The ASM compiler identification is Clang
-- Found assembler: /Library/Developer/CommandLineTools/usr/bin/cc
CMake Error at CMakeLists.txt:49 (project):
  No CMAKE_C_COMPILER could be found.

CMake Error at CMakeLists.txt:49 (project):
  No CMAKE_CXX_COMPILER could be found.
错误信息
  1. 使用 Xcode 编译 Clang
  • 创建 Schemes, 手动或者自动,


    选择自动或者手动
  • 手动要添加以下两项, 这个是后来截图, 就当没看见 GFPlugin , 😄😄😄

    手动添加
  • 编译, 选择 clang 进行编译, 编译时间比较长, 关注电脑的温度, 随时准备用水降温, 😄 开个玩笑,

编译 Clang

本应该选择 ALL_BUILD Secheme 进行编译, 但是其中包含 i386 架构, 编译不会通过, 此处我选择 clang 也是可以完成案例的, 还没有寻找解决办法, 哪位兄弟知道怎么解决就麻烦告诉我 , 我后续也会补上.

创建插件 --------------------

  • /llvm/tools/clang/tools 目录下创建插件 GFPlugin

    创建插件
  • 修改 /llvm/tools/clang/tools 目录下的 CMakeLists.txt 文件,
    新增 add_clang_subdirectory(GFPlugin)

    add_clang_subdirectory(GFPlugin)
  • GFPlugin 目录下创建 CMakeLists.txtGFPlugin.cpp 文件, 并在 CMakeLists.txt 文件中写入

add_llvm_library( GFPlugin MODULE BUILDTREE_ONLY
  GFPlugin.cpp
)
  • 接下来用 cmake 重新生成一下 xcode 项目, 命令行切换到 build_xcode 目录
cmake -G Xcode ../llvm
  • 最后在 build_xcodellvm 的 xcode 项目中看到 Loadable modules 目录中有自己的 Plugin 目录, 我们可以在这里编写插件代码,
  • 文件 GFPlugin.cpp 就是编写插件代码的位置, 插件是用 c++ 编写.
编写插件代码位置

插件代码编写和编译 --------------------

具体代码讲解, 请移步这里 : iOS Clang 插件开发代码流程分析

// create by guofei
// 2020/11/14

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;

namespace GFPlugin {

class GFMatchHandler: public MatchFinder::MatchCallback {
    
private:
    CompilerInstance &CI;
    
    bool isUserSourceCode(const string filename) {
        if (filename.empty()) return  false;
        if (filename.find("/Applications/Xcode.app/") == 0) return false;
        return  true;
    }

    bool isShouldUseCopy(const string typeStr) {
        if (typeStr.find("NSString") != string::npos ||
            typeStr.find("NSArray") != string::npos ||
            typeStr.find("NSDictionary") != string::npos/*...*/)
        {
            return true;
        }
        
        return false;
    }
    
public:
    GFMatchHandler(CompilerInstance &CI) :CI(CI) {}
    
    void run(const MatchFinder::MatchResult &Result) {
        const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
        if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) {
            ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes();
            string typeStr = propertyDecl->getType().getAsString();
            
            if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {
                DiagnosticsEngine &diag = CI.getDiagnostics();
                diag.Report(propertyDecl->getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "--------- %0 没用 copy 修饰 ---------")) << typeStr;
            }
        }
    }
};

//-------------------------------------------------------------

class GFASTConsumer: public ASTConsumer {
private:
    
    MatchFinder matcher;
    GFMatchHandler handler;
    
public:
    
    GFASTConsumer(CompilerInstance &CI) : handler(CI) {
        matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler);
    }
    
    void HandleTranslationUnit(ASTContext &context) {
        matcher.matchAST(context);
    }
};

//-------------------------------------------------------------
class GFASTAction: public PluginASTAction {
    
public:
    unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef iFile) {
        return unique_ptr<GFASTConsumer> (new GFASTConsumer(CI));
    }
    
    bool ParseArgs(const CompilerInstance &ci, const std::vector<std::string> &args) {
        return true;
    }
};

}

static FrontendPluginRegistry::Add<GFPlugin::GFASTAction> X("GFPlugin", "This is the description of the plugin");

  • 编译插件, 将 GFPlugin 添加到 Schemes 中, 并选中编译一下, 在 products文件夹下会找到自己的插件
    将 GFPlugin 添加到 Schemes
编译插件

由于文件太多, products 滚动到上边去了.

GFPlugin.dylib

插件的测试和集成 --------------------

1. 命令行测试插件

测试命令格式如下 :
你自己编译好的clang -isysroot 模拟器sdk路径/ -Xclang -load -Xclang 编译好的GFPlugin.dylib的绝对路径 -Xclang -add-plugin -Xclang GFPlugin(插件名) -c viewController.m的绝对路径

  • 具体可以参照下图
终端测试命令和测试结果

2. Xcode 项目集成插件

在 Build Settings 栏目中搜索 other c, 添加如下图的命令和绝对路径,
-Xclang -load -Xclang 动态库绝对路径 -Xclang -add-plugin -Xclang 插件名称

动态库绝对路径插件名称 如下图所示

other c flags 设置
  • 由于 Clang 插件需要使用对应的版本去加载, 如果不一致则会导致 编译错误 会出现如下图的错误

    集成到项目编译报错
  • 在 Build Settings 栏目中新增两项用户自定义设置, 如下图所示
    分别是 CCCXX
    CC 是对应自己编译的 clang 的绝对路径
    CXX 是对应自己编译的 clang++ 的绝对路径

    设置编译器
  • 接下来在 Build Settings 栏目中搜索 index, 将 Enable Index-Wihle-Building FunctionalityDefault 改为 NO

    Enable Index-Wihle-Building Functionality设置

重新编译项目, 错误消失, 并在 ViewController.m 中出现如下警告⚠️信息,

  • 说明我的插件集成成功了. 开不开心 😄😄😄😄😄😄


    编译结果报警告

相关文章

网友评论

      本文标题:iOS 开发 Clang 插件的配置及流程

      本文链接:https://www.haomeiwen.com/subject/foribktx.html