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

2020-11-15  本文已影响0人  AndyGF

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

主要分为以下 5 大步骤 :

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

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

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

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

通过 Xcode 编译 LLVM

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

build_xcodellvm 同级.

-- 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
编译 Clang

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

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

add_llvm_library( GFPlugin MODULE BUILDTREE_ONLY
  GFPlugin.cpp
)
cmake -G Xcode ../llvm
编写插件代码位置

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

具体代码讲解, 请移步这里 : 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");

编译插件

由于文件太多, 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 设置

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

上一篇 下一篇

猜你喜欢

热点阅读