iOS开发端代码检查

iOS开发端代码检查

背景:一直以来,代码质量都是一个痛心疾首的问题,特别是有新人进来团队的时候,每个人都需要相当长的磨合时间。传统上,我们都是在提测集成阶段进行代码质量检查,但是经常会忽略单个bug的修复以及无法落地整个团队的代码规范。针对这一系列的情况,决定利用开发端的资源,把日常代码质量检查融入到每个开发环节中。

ps:收藏一下,受益终生 ^_^

一、git hook

基本概念:git作为代码管理工具,目前应该是大家使用最多的了,git的最大的特点是增量记录,除了这个特点,今天还要说一下ta的另外一个特性git hook,其实就是针对git操作过程中每一个步骤进行监控,既包括客户端的,也包括服务端的。

目标:今天我们只学习客户端的监控,以达到开发端监控代码质量的目的

1、git的管理知识

(1)首先要了解git管理文件的一个概念,什么是工作区、版本库中的暂存区、版本库,如下图:

iOS开发端代码检查_第1张图片

工作区:就是咱们平时文件存放的地方

版本库中的暂存区:就是文件的索引(index)文件,index会指向对应的objects

版本库:就是每次真正提交后存放的位置

所以,如果我们使用的是sourcetree,那么平时未暂存的文件,就是属于工作区,暂存的文件就是暂存区,提交动作之后的就是进入版本库。

(2)对应的命令

工作区,通过add命令会进入暂存区;暂存区,通过commit命令进度版本库,checkout 命令返回工作区;版本库,通过checkout命令进入工作区,通过reset命令进入暂存区;

2、开发端的git hook可用的有哪些?

平时我们直接git clone别人的库是看不到git hook的相关文件的,那么我们如何去创建这些hook程序呢?答案是在终端输入以下代码。

git init

没错,就是这么简单。

调完这个命令之后,就会创建出如下图的隐藏文件:


iOS开发端代码检查_第2张图片

可以看到,hook文件夹下面多了很多sample,没错,这些就是支持的hook了。

3、使用git hook的例子

这次我们只使用pre-commit这个例子,而我的项目里面也暂时只需要使用这个。要让这个hook生效很简单,只需要pre-commit.sample把后缀去掉。

然后打开pre-commit文件,码下以下代码看看是否生效了:

echo "测试"

exit 1

你会发现commit再也提不上去了,还报了红色的“测试”两个大字。恭喜你hook成功了!

其实我们要对比代码,检查变更代码里面的问题,只需要用到git diff --staged命令,这样就可以获取到变更信息的字符串,剩下的工作就是规则、算法、判断、提示的工作了,这里我就不细说了。

4、无痕落地到开发项目以及日常更新

关于这个,我这边只说以下大致方案,其实没什么难点。

方案如下:

1、xcode建立script的target,然后执行脚本,在脚本中注入hook相关资源,可以让其他开发都能直接从0-1安装hook。

2、脚本还需增加版本管理,这样,可以判断是否版本更新了,以执行新脚本替换。

只要实现上面两步方案,开发基本就是,从分支里拉代码,build就完成更新操作,方便又实用。

二、clang插件

先看看效果,如下:

iOS开发端代码检查_第3张图片

基本概念:LLVM全称 Low Level Virtual Machine,最开始设计是为虚拟机,现在已经是各种开发语言的编译解决方案,里面包含了多个工具:clang、clang-tools-extra、compiler-rt、libcxx、lld、lldb、llvm、llgo、openmp等等。

目标:这章节,我们主要是针对clang这个工具进行讲解,实战。

1、开源LLVM的选择

(1)LLVM源码

官方网站:https://www.llvm.org

官方github:https://github.com/llvm/llvm-project

工具拆分github:https://github.com/llvm-mirror

苹果专用:https://github.com/apple/llvm-project

(2)区别

【1】官方网站和官方github:下载的源码是一致的,无论是版本号,还是代码结构;

【2】工具拆分github:是可以根据使用者的需求单独下载对应工具模块,这样可以减少开发者下载等待的时间,毕竟一个完整的llvm包要差不多2g,里面有很多工具我们也不一定会用到;代码结构与其他源码不一致。

【3】苹果专用:就像字面一样,里面包含了很多苹果定制化的功能,比如-index-store-path的使用,还有modulemap等等。

(3)选择

因为我们要解决的是iOS的编译问题,当然是选择苹果专用的,不要问我为什么这么选择,我不会告诉你我踩过多少坑。

2、LLVM的编译

(1)了解苹果专用的LLVM目录结构,如下:


iOS开发端代码检查_第4张图片

(2)CMake编译准备

    【1】LLVM是用CMake来进行项目管理的,有图有真相,如下:

iOS开发端代码检查_第5张图片

    【2】CMake安装

CMake可以去https://cmake.org/下载,ta其实是一个应用程序,里面包含了cmake的工具命令。

CMake程序界面:

iOS开发端代码检查_第6张图片

CMake程序命令的位置:/Applications/CMake.app/Contents/bin/cmake,如果自己要在终端上使用,可以把这个命令的路径加到$PATH变量里面。


到这里你终于可以用CMake生成编译配置了。


    【3】CMake 编译配置生成,终端命令如下

cd 进入llvm源码根目录

mkdir build

cd build

cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;libcxx;libcxxabi;compiler-rt;" -DCLANG_DEFAULT_CXX_STDLIB=libc++ -DCMAKE_BUILD_TYPE=Release ../llvm

如果生成成功会这样显示:

iOS开发端代码检查_第7张图片

同样,你可以用CMake的app进行生成,这里我就不详细介绍了。

(3)终端编译

make -j`sysctl -n hw.logicalcpu`

如果没有安装make命令的只需要安装 xcode并且安装command Line Tools。

如果已经安装了, 只需要静等30-60分钟就可以编译完成。

(4)部分编译选项的作用

在这个过程里面,最重要的就是CMake的设置,上面代码的大体意思是,-G代表Generators,里面有多种选项,比如unix makefiles项目、Xcode项目、Ninja项目等,我们这里使用unix makefiles,因为用着舒服。

编译的项目“LLVM_ENABLE_PROJECTS”有“clang;clang-tools-extra;libcxx;libcxxabi;compiler-rt;”,这些库是经过我多次尝试选择后的最优开发模块,如果你们要用自己ld或者lldb,那你可以引入相关的项目进行编译。

“CLANG_DEFAULT_CXX_STDLIB”顾名思义是默认c++的标准库,支持c++11标准以后要改成用libc++库。

“CMAKE_BUILD_TYPE”编译模式为release模式,当然可以是debug模式,debug模式的程序要比release模式的程序大4倍,除非有特殊需求,不然建议直接release

源码路径是“../llvm”。

关于CMake的选项设置,更详细的可以通过命令:cmake --help查看,官方文档可以看:http://llvm.org/docs/CMake.html

3、项目接入自编译的clang

(1)clang接入的两步骤

    【1】xcode->build settings->User-Defined添加CC、CXX,如下图:


iOS开发端代码检查_第8张图片

    【2】xcode->build settings->other link flags添加-lstdc++,如下图:

iOS开发端代码检查_第9张图片

到这里恭喜你,你已经顺利接入自己编译的clang了^_^

题外话:为什么我会想自己编译clang呢?我估计有很多同学都会带着这样的疑问在看这边文章。其实最本质的原因就是,只有自己编译的clang才能放开插件功能,苹果默认的clang是不开放插件功能的。


(2)AST语法树的认识

    【1】知识介绍

抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使整个阶段变得混乱。因此,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。抽象语法树在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器。

引用外部一个对AST的理解的文章:https://blog.csdn.net/weixin_39408343/article/details/95984062,虽然里面不是OC相关语法树的例子,但是,抽象语法树是通用的,无论针对什么语言。

    【2】如何查看AST树

在终端下输入以下代码,当然,ViewController.m你要自己写,或者是用xcode新建一个项目,直接用里面的文件直接替换

clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -Xclang -ast-dump -fsyntax-only ViewController.m

例子

    源码:

iOS开发端代码检查_第10张图片

    AST树:

iOS开发端代码检查_第11张图片

    【3】树节点例子说明

从上图可以看出,这个AST树主体分为两大部分:

第一部分ObjCInterfaceDecl

    ObjCInterfaceDecl是OC类的声明,在这个例子是otherclass,同级的ObjCImplementationDecl是otherclass的实现部分。

        ObjCInterfaceDecl后面super那一行,意思是otherclass继承NSObject;

        ObjCImplementation表示实现的名字也是otherclass;

        ObjCProtocol表示类实现了NSCoding协议;

        ObjCPropertyDecl表示属性定义了aaa、bbb都是NSString*类型,拥有的特性readwrite nonatomic strong

        ObjCMethodDecl表示定义了方法aaa、bbb、setAaa入参是NSString、setBbb入参是NSString,其实就是对应属性的get、set方法

第二部分ObjCImplementationDecl

    ObjCImplementationDecl是otherclass的实现部分。

        ObjCMethodDecl表示方法dealloc,和第一部分不一样的是,这部分的方法声明还包含实现部分。

            ImplicitParamDecl 表示方法参数 ,像delloc这个有两个默认参数,self、_cmd,self是当前实例,_cmd是当前方法名

        CompoundStmt可以简单认为是{}里面包含各种数据结构

            CallExpr调用方法返回类型为void

                ImplicitCastExpr返回值指针转换

                    DeclRefExpr方法名以及类型

                    ObjCStringLiteral参数类型

                        StringLiteral参数值

            ObjCMessageExpr要调用的OC方法removeObserver

                ObjCMessageExpr要调用的OC方法[NSNotificationCenter  defaultCenter]   

                ImplicitCastExpr入参otherclass * self             

详细定义说明请查看《AST语法树关键字解析》

4、编写clang插件demo

通过上面章节,我们基本完成对开发环境的搭建,以及对基础知识的了解,下面我们来进行实战,创建一个clang插件。

(1)项目配置

    项目结构如下图:

iOS开发端代码检查_第12张图片

    【1】从图中可以看出,我们也是选择用CMake的方式构建项目,因此咱们先来看看CMakeLists.txt的文件结构,如下:

cmake_minimum_required (VERSION 2.6)#要求最低版本

project (XXClangPlugin)#项目名字

set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )#设置build后输出的文件路径

set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )#设置build后输出的文件路径

set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )#设置build后输出的文件路径

set( LLVM_HOME /opt/llvm-apple )#设置内部变量的值,根目录

set( LLVM_SRC_DIR ${LLVM_HOME}/llvm-project201911/llvm )#llvm源码目录

set( CLANG_SRC_DIR ${LLVM_HOME}/llvm-project201911/clang )#clang源码目录

set( LLVM_BUILD_DIR ${LLVM_HOME}/llvm-project201911/build )#llvm编译目录

set( CLANG_BUILD_DIR ${LLVM_HOME}/llvm-project201911/build/tools/clang)#clang编译目录

add_definitions (-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS)#项目增加宏

add_definitions (-D_GNU_SOURCE -DHAVE_CLANG_CONFIG_H)#项目增加宏

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -fPIC  -fno-common  -Woverloaded-virtual  -Wcast-qual  -fno-strict-aliasing  -pedantic  -Wno-long-long  -Wall  -Wno-unused-parameter  -Wwrite-strings  -fno-exceptions   -fno-rtti   -stdlib=libc++   -std=c++14   -fPIC   -w")#cmake项目的选项

set (CMAKE_MODULE_LINKER_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress")#cmake项目的选项

set (LLVM_LIBS  LLVMMCJIT  LLVMX86CodeGen  LLVMX86AsmParser  LLVMX86Disassembler  LLVMExecutionEngine  LLVMAsmPrinter  LLVMSelectionDAG  LLVMX86Info  LLVMMCParser  LLVMCodeGen  LLVMX86Utils  LLVMScalarOpts  LLVMInstCombine  LLVMTransformUtils  LLVMAnalysis  LLVMTarget  LLVMCore  LLVMMC  LLVMSupport  LLVMBitReader  LLVMOption  LLVMDemangle  LLVMBitstreamReader  LLVMProfileData  LLVMBinaryFormat  LLVMRemarks) #项目需要加载的库

macro(add_clang_plugin name) #内部方法定义

    set (srcs ${ARGN})#设置内部参数

    include_directories( "${LLVM_SRC_DIR}/include"    "${CLANG_SRC_DIR}/include"    "${LLVM_BUILD_DIR}/include"    "${CLANG_BUILD_DIR}/include" ) #设置包含的头文件目录

    link_directories( "${LLVM_BUILD_DIR}/lib" ) #链接的库位置

    add_library( ${name} SHARED ${srcs} ) #设置为动态库

    if (SYMBOL_FILE)   

        set_target_properties( ${name} PROPERTIES LINK_FlAGS      "-exported_symbols_list ${SYMBOL_FILE}") 

    endif() 

    foreach (clang_lib ${CLANG_LIBS})   

        target_link_libraries( ${name} ${clang_lib} )   #动态加入clang依赖的库

    endforeach() 

    foreach (llvm_lib ${LLVM_LIBS})   

        target_link_libraries( ${name} ${llvm_lib} ) #动态加入llvm依赖的库

    endforeach() 

    foreach (user_lib ${USER_LIBS})   

        target_link_libraries( ${name} ${user_lib} ) #动态加入用户自己想引入的库

    endforeach()

endmacro(add_clang_plugin)

set(SYMBOL_FILE XXClangPlugin.exports) #设置变量

set (CLANG_LIBS  clang  clangFrontend  clangAST  clangAnalysis  clangBasic  clangCodeGen  clangDriver  clangFrontendTool  clangLex  clangParse  clangSema  clangEdit  clangSerialization  clangStaticAnalyzerCheckers  clangStaticAnalyzerCore  clangStaticAnalyzerFrontend  clangAPINotes)#设置变量

set (USER_LIBS  pthread  curses  z)#设置变量

add_clang_plugin(XXClangPlugin   XXClangPlugin.cpp)#调用内部方法

set_target_properties(XXClangPlugin PROPERTIES  LINKER_LANGUAGE CXX  PREFIX "")


可以开始生成编译项目了,如下:

进入插件源码目录

mkdir build;

cd build

cmake -G Xcode ..

(2)编写代码

上面我们生成了xcode项目,可以直接xcode打开了,下面接着编写源码,如下:

static clang::FrontendPluginRegistry::AddX("XXClangPlugin", "XX Clang Plugin");//入口代码就一句,注册一个插件类

////最简单的插件类 

#include

#include "clang/Frontend/FrontendPluginRegistry.h"

#include "clang/AST/AST.h"

#include "clang/AST/ASTConsumer.h"

#include "clang/Frontend/CompilerInstance.h"

#include "clang/AST/RecursiveASTVisitor.h"

using namespace clang;

namespace XXClangPlugin{

     class XXClassVisitor : public RecursiveASTVisitor

    {   

         private:       

             CompilerInstance &Instance;        //当前检查变量       

             ASTContext *context;        ///是否需要检查的类       

         public:       

                XXClassVisitor (CompilerInstance &Instance)        :Instance(Instance)       

                {                   }       

                void setContext(ASTContext &context)        {            this->context = &context;        }

                ///类声明       

                bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *declaration)

                {

                    printf("进来了"); 

                    return true;

                }

    }

    class XXConsumer : public ASTConsumer 

     {       

            CompilerInstance &Instance;       

            std::set ParsedTemplates;   

        public:       

             XXConsumer(CompilerInstance &Instance,                     std::set ParsedTemplates)        : Instance(Instance), ParsedTemplates(ParsedTemplates), visitor(Instance)        {        }       

             void HandleTranslationUnit(ASTContext &context)

               {           

                        visitor.setContext(context);           

                        visitor.TraverseAST(context);       

                }   

           private:       

                XXClassVisitor visitor;   

    };

    class XXASTAction : public PluginASTAction

    {       

            std::set ParsedTemplates;   

        public:       

            virtual std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler,                                                      llvm::StringRef InFile)       

            {           

                    printf("编译文件:%s\n", InFile);           

                    return std::make_unique(Compiler, ParsedTemplates);        

            }       

             bool ParseArgs(const CompilerInstance &CI, const                       std::vector& args)

            {           

                    for (int i = 0; i < args.size(); i++) {               

                        printf("参数:%s\n", args[i].c_str());           

                    }           

                    return true;               

            }   

      };

}

到此,基础的插件源码文件已经写完,编译通过后会生成XXClangPlugin.lib,这个就是咱们要引进项目的插件了。ps:可以看到,上面的代码是用C++编写的,所以你要学习一下C++。

(3)调试

说起调试,这个真的是一个巨大的坑,翻遍百度和google都没找到相关资料。幸好,后来找到一个很有用的信息,就是clang的编译每次都是一个独立的进程。有了这个信息,终于可以愉快调试了。。。以下是相关调试方法:

    【1】注意点

       clang必须使用自己编译出来的,找找编译目录,能找到,不然编译报错提示加载插件失败。

       必须要加载对应的系统库,不然你连uikit都识别不了

       必须要开arc模式,不然等着各种报错。

    【2】终端代码

/xxx/llvm-project201911/build/bin/clang -Xclang -load -Xclang /Path/to/XXClangPlugin.dylib -Xclang -add-plugin -Xclang XXClangPlugin -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -fsyntax-only -fobjc-arc -v /Users/XXXX/Desktop/test/test/ViewController.m

这一步的目标虽然可以看到插件的返回内容,但是并不是真正的调试。目标是要找到以下这段代码:

"/xxx/llvm-project201911/build/bin/clang-10" -cc1 -triple x86_64-apple-ios13.2.0-simulator -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -fsyntax-only -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ViewController.m -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -masm-verbose -munwind-tables -target-sdk-version=13.2 -target-cpu core2 -dwarf-column-info -debugger-tuning=lldb -target-linker-version 530 -v -resource-dir /xxx/llvm-project201911/build/lib/clang/10.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/local/include -internal-isystem /xxx/llvm-project201911/build/lib/clang/10.0.0/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include -fdebug-compilation-dir /xxx/XXClangPlugin/build/lib/Debug -ferror-limit 19 -fmessage-length 111 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fobjc-runtime=ios-13.2.0 -fobjc-arc -fobjc-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -load XXClangPlugin.dylib -add-plugin XXClangPlugin -x objective-c /Users/XXXX/Desktop/test/test/ViewController.m

接着,调试肯定是LLDB

lldb "/xxx/llvm-project201911/build/bin/clang-10"

会进入如下状态:

lldb后,还是要继续码代码,如下:

process launch -- -cc1 -triple x86_64-apple-ios13.2.0-simulator -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -fsyntax-only -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ViewController.m -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -masm-verbose -munwind-tables -target-sdk-version=13.2 -target-cpu core2 -dwarf-column-info -debugger-tuning=lldb -target-linker-version 530 -v -resource-dir /xxx/llvm-project201911/build/lib/clang/10.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/local/include -internal-isystem /opt/llvm-apple/llvm-project201911/build/lib/clang/10.0.0/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include -fdebug-compilation-dir /xxx/XXClangPlugin/build/lib/Debug -ferror-limit 19 -fmessage-length 111 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fobjc-runtime=ios-13.2.0 -fobjc-arc -fobjc-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -load XXClangPlugin.dylib -add-plugin XXClangPlugin -x objective-c /Users/XXXX/Desktop/test/test/ViewController.m

恭喜你!!到这里是真的可以调试了,你会发现执行完上面的代码后,一样能达到之前直接执行终端命令的效果。为啥没有中断呢?那是因为没有设置断点。。

break set --name XXClangPlugin::XXClassVisitor::VisitObjCInterfaceDecl

好了,你再次执行process launch就可以完美进入断点了。

【3】调试技巧

 打印c++对象要用p命令,用po啥都看不到

c命令,就是跳到下个断点

n命令,是下一步

s 进去

exit 退出lldb

其他命令都可以网上查到,慢慢玩耍

5、项目引入插件

项目接入插件比较简单。

(1)把编译好的clang及相关头文件都要复制到想使用的项目的根目录,当然目录可以自己定,如下:


iOS开发端代码检查_第13张图片

里面的文件都是经过千辛万苦裁减过的,是独立编译的最小包了,大概是200M。

(2)buildsetting->other c flag和other c++ flag都要加上如下代码:

-Xclang -load -Xclang $(SRCROOT)/Path/to/XXClangPlugin.dylib -Xclang -add-plugin -Xclang XXClangPlugin

恭喜,到这里终于可以在项目里面使用自己的插件了!!!!!

6、pod如何使用插件

现在项目大部分都用pod来进行管理,所以咱们也要把pod环境兼容了。这里有很多坑,后面在Q&A环节一一解答。

pod的兼容其实基本和主工程才不多,只是要把相关工作放在podfile上去操作而已,代码如下:

config.build_settings['CC'] = ['$(SRCROOT)/../clang-apple/bin/clang']                      

config.build_settings['CXX'] = ['$(SRCROOT)/../clang-apple/bin/clang++']                       config.build_settings['OTHER_LDFLAGS'] = ['$(inherited)','-lstdc++'];                     

config.build_settings['OTHER_CFLAGS'] = ['-Xclang -load -Xclang $(SRCROOT)/../clang-apple/FXClangPlugin.dylib -Xclang -add-plugin -Xclang FXClangPlugin']                     

config.build_settings['OTHER_CPLUSPLUSFLAGS'] = ['$(OTHER_CFLAGS)']

只要根据需要,把需要特别处理的项目加入上面代码相关的配置,就可以加载自己编译的插件实现章节开始最上面的效果。

三、Q&A

1、Q:为什么不能用其他版本的LLVM?

A:因为苹果自己做了定制化的内容,改了一些,如果使用其他版本的LLVM新项目是可以编译通过的,没有那么多限制,但是老项目就可能使用了很多苹果的特性功能,所以会导致各种问题。比如:index-store-path问题,modulemap问题,import重复加载问题等等。

2、Q:llvm的clang版本和xcode自带clang的版本如何对上?

A:确实基本是对不上的,llvm官网上面最新版本才9.01,但是你用命令去查xcode的clang版本会发现是11。所以才会导致我最后找到苹果的开源代码,看上面的代码可以看出,我编译出来的版本是clang-10,但是其实已经是很新的clang版本了。目前发现的版本就是看日期,尽量使用最接近xcode发布时间的release版本。

3、Q:使用clang插件的优缺点?

A:

        优点:(1)依赖编译检查(2)可以中断编译(3)可以深度控制AST检查(4)可以实现代码补全

        缺点:(1)依赖整个clang编译环境 (2)不能完全控制clang,包括映射虚拟内存文件(3)不可以单独检查部分变更文件

4、等待同学提出及时补充更新

四、总结

知识的沉淀很重要,在最开始学习llvm时,没有前辈的系统知识和文章,只能不停的尝试,现在把这些尝试后的宝贵经验留下,为了让以后的人少走弯路。

你可能感兴趣的:(iOS开发端代码检查)