OCLint+Xcode实现Code Review
Code Review是开发过程中保证代码质量不可或缺的一部分,但是呢,要么是懒,要么是真没时间,在我们公司code review已从原来的流于形式到彻底废弃了。最近看了看项目代码,咋写的都有,看着确实难受,于是就动了"code review自动化"的念头。xcode里有内置的Analyser,但由于默认的规则太少导致功能实在很有限。找了半天,就盯上OCLint了。OCLint是啥我就不多说啦,具体可以查看OCLint官网。本篇博客记录了我如何使用oclint进行静态代码分析,只针对Mac。
一、环境配置
需要安装oclint和xcpretty。
1、安装oclint
方法一:brew安装
命令行执行:
$brew tap oclint/formulae $brew install oclint
方法二:安装包安装
(1)进入到github上,下载最新(当前为oclint-0.13-x86_64-darwin-16.7.0.tar.gz)安装包,解压出来为oclint-0.13
,放到如下目录:/Users/layne/OCLint
,即路径为/Users/layne/OCLint/oclint-0.13
。
(2)将oclint添加到环境变量。vim打开~/.bash_profile(若没有则创建),添加如下代码:
OCLINT_HOME=/Users/layne/OCLint/oclint-0.13 export PATH=$OCLINT_HOME/bin:$PATH
终端执行:
$oclint
出现如下内容证明没问题:
oclint: Not enough positional command line arguments specified! Must specify at least 1 positional argument: See: oclint -help
方法三:源码安装 (若要自定义规则,则必须使用源码方式安装,后边会说到具体方法。)
1、安装CMake和Ninja
brew install cmake ninja
CMake和Ninja是代码编译工具,因此必须要先安装。
2、从github上下载oclint源码,解压之后重命名为oclint-0.13,然后放到如下目录(随意):/Users/layne/OCLint
,最终为/Users/layne/OCLint/oclint-0.13
3、打开终端进入到/Users/layne/OCLint/oclint-0.13/oclint-scripts
cd /Users/layne/OCLint/oclint-0.13/oclint-scripts
然后执行:
./make
之后就开始下载和编译,不过时间会比较长(40min左右),且还必须能够爬出去才可以。成功之后会有如下路径:/Users/layne/OCLint/oclint-0.13/build/oclint-release
,这个就是oclint的路径。
4、添加oclint到环境变量。执行:
vim ~/.bash_profile
将如下内容写入:
OCLINT_HOME=/Users/layne/OCLint/oclint-0.13/build/oclint-releaseexport PATH=$OCLINT_HOME/bin:$PATH
保存退出。重启终端之后在终端执行:oclint --version
,出现如下内容:
LLVM (http://llvm.org/):LLVM version 5.0.0svn-r.320669Optimized build.Default target: x86_64-apple-darwin17.3.0Host CPU: broadwellOCLint (http://oclint.org/):OCLint version 0.13.Built Dec 14 2017 (16:03:48).
至此oclint安装成功。
2、安装xcpretty
gem install xcpretty
说明:这里安装的xcpretty是最新版,github上的oclint源码应该是针对最新版的xcpretty进行了兼容。为什么这么说呢?因为我一开始是采用的方法二安装的oclint,运行oclint现成的规则没有问题。之后想要自定义规则,但是方法三又太麻烦了,于是我就偷懒从网上下载了别人事先编译好的oclint-0.12(这里说的"编译好的oclint"保留了当初编译的"现场",可以进行自定义规则,而方法二中的是"干净"的oclint),然后进行自定义规则,可是跑起来一直报错(形如"/usr/sh fail with exit code 1")。于是乎我不得不用oclint源码重新编译一遍,再运行的时候就没有错误了。
二、xcode配置
以项目LayneStudy为例。
1、创建Aggregate类型target
打开LayneStudy项目,new一个新的target,类型选择Aggregate,命名为OCLint,确定。说明:在xcode9中,Aggregate类型在Cross-platform等目录下(而非iOS、watchOS、macOS等目录下)。
2、编写shell脚本
(1)选择target OCLint,在build phases里添加New Run Script Phase。在框里输入如下脚本代码:
chmod -R 777 $SRCROOT/oclint $SRCROOT/oclint/oclint.sh
(2)编写脚本oclint.sh,内容如下:
source ~/.bash_profile #获取项目路径 PROJECT_DIR=$(cd `dirname $0`;cd ..;pwd) cd ${PROJECT_DIR} buildPath="${PROJECT_DIR}/oclint/build" compilecommandsJsonFolderPath="${PROJECT_DIR}/oclint" compilecommandsJsonFilePath="${PROJECT_DIR}/oclint/compile_commands.json" rm -rf "$compilecommandsJsonFolderPath/build" xcodebuild SYMROOT=$buildPath | xcpretty -r json-compilation-database -o $compilecommandsJsonFilePath cd $compilecommandsJsonFolderPath oclint-json-compilation-database -- -report-type xcode \ -rc CYCLOMATIC_COMPLEXITY=10 \ -rc LONG_CLASS=1000 \ -rc LONG_METHOD=50 \ -rc LONG_LINE=140 \ -rc LONG_VARIABLE_NAME=30 \ -rc SHORT_VARIABLE_NAME=1 \ -rc MAXIMUM_IF_LENGTH=5 \ -rc MINIMUM_CASES_IN_SWITCH=2 \ -rc NCSS_METHOD=30 \ -rc NESTED_BLOCK_DEPTH=5 \ -rc TOO_MANY_METHOD=30 \ -rc TOO_MANY_PARAMETERS=5 \ -max-priority-1 0 \ -max-priority-2 5 \ -max-priority-3 10
将oclint.sh放到项目根目录下的oclint文件夹中(要先创建oclint文件夹)。最终目录结构如下:
../LayneStudy.xcodeproj ../LayneSutdy ../oclint ../oclint/oclint.sh
3、执行
回到xcode,scheme选择OCLint,command+B,编译完成之后xcode则出现各种警告,证明你成功了。
补充:
①若出现python错误,则通过设置环境变量使alias python=python3,即使用最新的python。
②若出现/Library/Ruby/Gems/2.3.0/gems/xcpretty-0.3.0/lib/xcpretty/parser.rb:434:in `===': invalid byte sequence in US-ASCII (ArgumentError) "这种错误,则是编码问题:在~/.bash_profile中设置编码:
export LANGUAGE=en_US.UTF-8export LANG=en_US.UTF-8export LC_ALL=en_US.UTF-8
自定义规则
1、自定义阈值
像脚本oclint.sh中写的,使用-rc设置各种阈值,详见threshold command line,也可以通过写配置文件的形式配置阈值,参照threshold configuration file。有一点需要注意,配置文件要放到oclint-json-compilation-database
命令执行的目录下,例如oclint.sh中有一段:
...... cd $compilecommandsJsonFolderPath oclint-json-compilation-database -- -report-type xcode \ ......
即oclint-json-compilation-database
是在$compilecommandsJsonFolderPath
中执行的,那么配置文件也要放到$compilecommandsJsonFolderPath
下,且命名为.oclint
(会被隐藏)。如果配置文件无法命名为.oclint
,则需要vim创建一个名为.oclint
的文件,并将配置的内容写入,最后移动它到$compilecommandsJsonFolderPath
目录下。
2、自定义规则
通过自定义规则类,才是真正的自定义 。
默认规则
在下载的oclint包已经有默认的一些规则,路径如下:/Users/layne/OCLint/oclint-0.13/lib/oclint/rules
。你没看错,规则都是些以dylib结尾的动态库文件。若在oclint-json-compilation-database
执行时没有指定规则加载的路径,那么将会加载这些默认的规则。若要自定义规则路径,则要改写oclint.sh 如下:
...... oclint-json-compilation-database -- -report-type xcode \ ......
改为
...... oclint-json-compilation-database -- -R $compilecommandsJsonFolderPath/oclint_rules -report-type xcode \ ......
(要事先在$compilecommandsJsonFolderPath目录下创建oclint_rules文件夹)这样oclint就会加载$compilecommandsJsonFolderPath/oclint_rules中的规则。你可以将一些默认规则文件放到这个目录下,也可以将自定义的规则文件放到这个目录下,进行统一管理。
自定义规则
再次强调: 若要自定义规则,则必须要使用oclint源码
OCLint提供了scaffoldRule工具进行自定义规则。
1、创建规则cpp文件。在如下路径处Users/layne/OCLint/oclint-0.13
打开终端窗口,输入如下命令(可用的选项为:SourceCodeReader、ASTVisitor和ASTMatcher):
oclint-scripts/scaffoldRule ObjCNoSynthesize -t SourceCodeReader
这样就在以下路径生成了对应的cpp文件和MakeFile:
oclint-rules/rules/customoclint-rules/test/custom
2、 为了方便开发,生成xcodeproj文件,使用xcode开发规则。
(1)在oclint-0.13目录下创建文件夹oclint-xcoderules
(2)进入到oclint-xcoderules目录(Users/layne/OCLint/oclint-0.13/oclint-xcoderules
),创建shell文件create-xcode-rules.sh
,内容如下:
#! /bin/sh -ecmake -G Xcode \ -D CMAKE_CXX_COMPILER=../build/llvm-install/bin/clang++ \ -D CMAKE_C_COMPILER=../build/llvm-install/bin/clang \ -D OCLINT_BUILD_DIR=../build/oclint-core \ -D OCLINT_SOURCE_DIR=../oclint-core \ -D OCLINT_METRICS_SOURCE_DIR=../oclint-metrics \ -D OCLINT_METRICS_BUILD_DIR=../build/oclint-metrics \ -D LLVM_ROOT=../build/llvm-install/ ../oclint-rules
保存退出之后,更改create-xcode-rules.sh的访问权限:
chmod 777 create-xcode-rules.sh
然后执行:
./create-xcode-rules.sh
最后如果出现如下内容证明xcodeproj生成成功了:
...... -- Configuration done -- Generating done -- Build files have been written to:/Users/layne/OCLint/oclint-0.13/oclint-xcoderules
并且在oclint-xcoderules文件夹下生成了工程目录。如下图:
3、双击打开OCLINT_RULES.xcodeproj,左侧显示所有的规则源文件,
拉到最底下就可以看到自己创建的规则ObjCNoSynthesize了。在ObjCNoSynthesizeRule.cpp中编写规则逻辑,之后在Scheme中选择对应的scheme,然后Command+B进行build,成功之后会在目录/Users/layne/OCLint/oclint-0.13/oclint-xcoderules/rules.dl/DEBUG
中找到libObjCNoSynthesizeRule.dylib
.至此我们就完成了规则的自定义。
4、创建新规则,即往OCLINT_RULES.xcodeproj项目中添加新规则。
(1)使用命令行创建新规则:
回到如下路径Users/layne/OCLint/oclint-0.13
打开终端窗口,输入如下命令:
oclint-scripts/scaffoldRule ObjCPointerStarShouldBeNearerToVariable -t ASTVisitor
(2)在OCLINT_RULES项目的scheme中随便选一个target,然后Commnand+B,这样新创建规则就会被加入到项目中来。
5、删除规则。
(1)从左边选择要删除的规则的cpp文件->delete->move to trash;
(2)打开对应的CMakeLists.txt,删除掉对应的配置。
(3)Command+B.
DONE!
补充说明:
1、规则的制定主要基于三个类:AbstractASTVisitorRule、AbstractASTMatcherRule和AbstractSourceCodeReaderRule,它们的关系如下:
RuleBase | |-AbstractASTRuleBase | |_ AbstractASTVisitorRule | |_AbstractASTMatcherRule | |-AbstractSourceCodeReaderRule
AbstractSourceCodeReaderRule:
提供eachLine方法,每行的读取源码。对对于想要从每行的内容编写规则的rule,可以使用继承自AbstractSourceCodeReaderRule。
AbstractASTVisitorRule:(一般继承这个类)
继承自AbstractASTVisitorRule的rule,可以实现访问AST上特定类型的所有节点,可以检查特定类型的所有节点是递归实现的,在AbstractASTVisitorRule的apply方法中可以看到代码实现,开发者只需要通过重写bool Visit*方法来访问特定类型的节点,在该函数中实现检查操作,其返回值往往是返回true,返回值表示是否继续递归检查。
AbstractASTMatcherRule: (目前没用过)
继承自AbstractASTMatcherRule的rule,实现setUpMatcher方法,在setUpMatcher()方法中实现添加matcher,当检查发现匹配的结果时,会调用callback()方法,故重新callback方法来对匹配的结果进行处理操作。
以上就是使用oclint的详细教程,至于自定义的规则如何去写,个人建议还是先看看现有规则的源码,找找感觉,然后再比着写。
下面是3个参考链接,我获益良多,建议深入阅读,尤其是里面的例子...
http://oriochan.com/codeReview01.html
https://juejin.im/post/595370986fb9a06bcb7f7d56
http://blog.csdn.net/hdwhappy/article/details/61924772