iOS开发端代码检查

作者: zerryzarax | 来源:发表于2020-03-20 10:19 被阅读0次

    iOS开发端代码检查

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

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

    一、git hook

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

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

    1、git的管理知识

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

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

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

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

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

    (2)对应的命令

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

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

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

    git init

    没错,就是这么简单。

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

    可以看到,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插件

    先看看效果,如下:

    基本概念: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目录结构,如下:

    (2)CMake编译准备

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

        【2】CMake安装

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

    CMake程序界面:

    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

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

    同样,你可以用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,如下图:

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


    到这里恭喜你,你已经顺利接入自己编译的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

    例子

        源码:

        AST树:

        【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)项目配置

        项目结构如下图:

        【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::Add<XXClangPlugin::XXASTAction>X("XXClangPlugin", "XX Clang Plugin");//入口代码就一句,注册一个插件类

    ////最简单的插件类 

    #include <zlib.h>

    #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<XXClassVisitor>

        {   

             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<std::string> ParsedTemplates;   

            public:       

                 XXConsumer(CompilerInstance &Instance,                     std::set<std::string> 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<std::string> ParsedTemplates;   

            public:       

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

                {           

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

                        return std::make_unique<XXConsumer>(Compiler, ParsedTemplates);        

                }       

                 bool ParseArgs(const CompilerInstance &CI, const                       std::vector<std::string>& 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及相关头文件都要复制到想使用的项目的根目录,当然目录可以自己定,如下:

    里面的文件都是经过千辛万苦裁减过的,是独立编译的最小包了,大概是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开发端代码检查

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