OCLint从安装到使用
OCLint是什么,引用官方对于OCLint的介绍:OCLint is a static code analysis tool for improving quality and reducing defects by inspecting C, C++ and Objective-C code and looking for potential problems. 通俗的讲,就是一个静态代码分析工具,通过该工具,我们可以检查出代码中的一些潜在问题,同时也就实现了自动化的code review。
接下里,我们直接进入OCLint的使用之旅:
一、 工具知识
在具体开始安装使用OCLint之前,我们先来了解一些在使用OCLint的过程中所涉及到的相关工具知识。
1. OCLint
OCLint是一套工具的总称,其中包含以下工具:
- oclint :是OCLint工具集最主要的指令,用于规则加载、编译分析选项以及生产分析报告。
- oclint-json-compilation-database :用于在
JSON Compilation Database format
类型的编译文件compile_commands.json
中提取必要信息。 - oclint-xcodebuild :用于将
xcodebuild
生产的log文件xcodebuild.log
转换为JSON Compilation Database format
类型的文件。
2. xcodebuild
使用OCLint需要搭配Xcode,准确的来说是使用Xcode套件中的xcodebuild
工具,xcodebuild可以对Xcode工程进行编译,生成对应的xcodebuild.log
文件。
3. xcpretty
xcpretty
是配合xcodebuild
来生成并输出对应格式的工具,在OCLint的使用里,我们可以用xcpretty
配合xcodebuild
来替代oclint-xcodebuild
来输出JSON Compilation Database format
格式文件。
4. 时序图
在使用OCLint对iOS工程进行代码分析中所涉及到的工具我们都已经介绍完了,下面是这些工具对应使用的时序图:
二、 工具安装
1. OCLint安装
有三种方式可以安装OCLint,分别为Homebrew安装、安装包安装以及编译源码安装。
如果你需要对OCLint进行规则的自定义开发,那么只能通过编译源码安装这种方式进行;如果你仅仅是使用OCLint规则来对源码进行分析,那么以上方式随你选择。
方式一. Homebrew安装
在安装前,请确保机器已经安装Homebrew。
该方式安装最为简单,只需执行以下命令即可:
brew tap oclint/formulae
brew install oclint
方式二. 安装包安装
(1). 进入oclint的github主页,进入Releases中,选择最新版本的安装包,例如oclint-0.13-x86_64-darwin-16.7.0.tar.gz
。
(2). 解压下载文件得到oclint-0.13文件,将文件放在某个目录下,如/Users/XXX/OCLint
。
(3). 终端输入vim ~/.bash_profile
,将oclint添加到环境变量中,如下:
OCLINT_HOME=/Users/XXX/OCLint/oclint-0.13
export PATH=$OCLINT_HOME/bin:$PATH
(4). 重启终端,之后执行oclint命令,得到以下内容则证明安装成功:
oclint: Not enough positional command line arguments specified!
Must specify at least 1 positional argument: See: oclint -help
方式三. 编译源码安装
(1). 使用Homebrew安装CMake
以及Ninja
,其中这两个工具为代码编译工具。
brew install cmake ninja
- 1
(2). 进入oclint的github主页,进入Releases中,选择最新版本的源码,例如0.13版本的Source code。
(3). 解压下载文件得到oclint-0.13文件,将文件放在某个目录下,如/Users/XXX/OCLint
。
(4). 使用终端进入到/Users/XXX/OCLint/oclint-0.13/oclint-scripts目录下。
cd /Users/XXX/OCLint/oclint-0.13/oclint-scripts
- 1
(5). 执行make脚本
./make
- 1
之后就是下载oclint-json-compilation-database、oclint-xcodebuild、llvm源码以及clang源码,并进行相关的编译得到oclint,该阶段所使用的时间较长,且必须自备梯子。
(6). 编辑结束,会生成/Users/mademao/OCLint/oclint-0.13/build/oclint-release文件夹,该文件夹下文件即为OCLint。且方式二下载的安装包就是该文件夹下内容。
(6). 终端输入vim ~/.bash_profile
,将oclint添加到环境变量中,如下:
OCLINT_HOME=/Users/XXX/OCLint/oclint-0.13/build/oclint-release
export PATH=$OCLINT_HOME/bin:$PATH
- 1
- 2
(7). 重启终端,之后执行oclint --version命令,得到以下内容则证明安装成功:
LLVM (http://llvm.org/):
LLVM version 5.0.0svn-r344880
Optimized build.
Default target: x86_64-apple-darwin17.7.0
Host CPU: skylake
OCLint (http://oclint.org/):
OCLint version 0.13.
Built Oct 22 2018 (11:34:55).
可能遇到的问题
在编译源码的过程中,可能会遇到如下错误:
该问题的原因是,make脚本下载的llvm、clang以及compiler-rt都是从svn上进行下载,而svn上tsan_libdispatch_mac.cc
该文件有错误,所以编译会报错,我们可以采用以下步骤去解决:
- 前往的github上下载对应的文件,该文件所在的位置为
/lib/tsan/rtl/tsan_libdispatch_mac.cc
。 - 将tsan_libdispatch_mac.cc第94行
user_alloc_internal
方法替换为user_alloc
方法。 - 重新执行
./make
,在输出A compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
之后,编译llvm之前将/Users/XXX/OCLint/oclint-0.13/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
文件替换。当然,你也可以重写make脚本执行的ci脚本源码,将下载源码与编译源码进行拆分,然后在这两步之间替换文件。
2. xcodebuild安装
这个工具,安装Xcode之后就顺带安装了。。。
3. xcpretty安装
在安装前,请确保机器已经安装Ruby gem。
使用如下命令安装xcpretty:
gem install xcpretty
- 1
三、 OCLint使用
这里我推荐结合Xcode来使用OCLint,具体步骤如下:
(1). 打开需要分析的Xcode程序,创建一个Aggregate
类型的target。
(2). 为新添加的target添加如下执行脚本
chmod -R 777 $SRCROOT/oclint
$SRCROOT/oclint/oclint.sh
- 1
- 2
(3). 在Xcode工程的.xcodeproj
文件同级目录下创建oclint文件夹,并在该文件夹下创建oclint.sh脚本文件,写入如下内容:
source ~/.bash_profile
#获取项目路径
PROJECT_DIR=$(cd `dirname $0`;cd ..;pwd)
cd ${PROJECT_DIR}
buildPath="
P
R
O
J
E
C
T
D
I
R
/
o
c
l
i
n
t
/
b
u
i
l
d
"
c
o
m
p
i
l
e
c
o
m
m
a
n
d
s
J
s
o
n
F
o
l
d
e
r
P
a
t
h
=
"
{PROJECT_DIR}/oclint/build" compilecommandsJsonFolderPath="
PROJECTDIR/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
(4). 打开项目,scheme选择OCLint,command+B,之后就会将分析后的警告提示在Xcode上。
可能遇到的问题
此处可能会遇到编译时提示xcpretty invalid byte sequence in US-ASCII (ArgumentError)
的错误,解决该错误的方法是在oclint.sh
的最前端增加export LC_ALL=en_US.UTF-8
设置。
四、 规则的使用
对于上一步出现的警告,是依据什么出现的呢?这就涉及到了OCLint的规则文件,OCLint在0.13版本中提供了71种默认规则,默认请见:OCLint默认规则。
1. 自定义阈值
OCLint对于默认规则,提供了几个可以修改的阈值,列表如下:
至于如何配置这些阈值,可修改oclint.sh脚本中最后一条命令,即oclint-json-compilation-database
工具在提取必要编译信息时设置可选项,例如:
oclint-json-compilation-database -- -report-type xcode -rc CYCLOMATIC_COMPLEXITY=10
- 1
2. 允许违反规则次数
在OCLint中,所有规则分为3个等级,oclint可以设置最多允许违反对应等级规则次数,当有一级别规则违反次数超过限制,那么OCLint的检测会以VIOLATIONS_EXCEED_THRESHOLD
的错误码返回。
设置违反次数方式如下:
oclint-json-compilation-database -- -report-type xcode -max-priority-1 0 -max-priority-2 10 -max-priority-3 20
- 1
3. 忽略需要分析的文件
OCLint提供了忽略需要分析文件的功能,例如我们想要忽略工程主目录下名为ThirdParts
文件夹下的所有文件,设置如下:
oclint-json-compilation-database -e ThirdParts -- -report-type xcode
- 1
4. 忽略某些规则
OCLint提供了忽略默认规则的功能,设置如下:
oclint-json-compilation-database -report-type xcode -disable-rule=AssignIvarOutsideAccessors
- 1
对应的规则名,同样可以在OCLint默认规则中找到命名。
5. 自定义规则
OCLint同样允许我们自定义规则,在我们自定义规则之前,我们先来看看OCLint中的规则是什么。
源码编译安装在/Users/XXX/OCLint/oclint-0.13/build/oclint-release/lib/oclint/rules
,其他两种安装方式在/Users/XXX/OCLint/oclint-0.13/lib/oclint/rules
下找到默认规则文件,这些文件均为dylib
动态库文件。
在执行oclint-json-compilation-base
时没有指定规则文件所在目录,那么就会默认使用上述目录。我们可以通过-R
选项来指定所需要使用的规则所在文件。
oclint-json-compilation-database \
-- \
-R /Users/XXX/Desktop/ProjectName/oclint/oclint_rules \
-report-type xcode \
同样的,如果我们既想使用默认规则,又想使用自定义规则,可以指定两个-R
,对于默认规则的路径,可以根据oclint环境变量的路径得到。
OCLint提供了三种类型的规则,开发者只要对应在具体的方法中实现自己的需求即可完成规则的自定义,这三种类型规则分别为:AbstractASTVisitorRule
、AbstractASTMatcherRule
、AbstractSourceCodeReaderRule
,三种规则之间的关系如下:
AbstractSourceCodeReaderRule
提供eachLine方法,每行的读取源码。对对于想要从每行的内容编写规则的rule,可以使用继承自AbstractSourceCodeReaderRule。
AbstractASTVisitorRule
继承自AbstractASTVisitorRule的rule,可以实现访问AST上特定类型的所有节点,可以检查特定类型的所有节点是递归实现的,在AbstractASTVisitorRule的apply方法中可以看到代码实现,开发者只需要通过重写bool Visit*方法来访问特定类型的节点,在该函数中实现检查操作,其返回值往往是返回true,返回值表示是否继续递归检查。
AbstractASTMatcherRule
继承自AbstractASTMatcherRule的rule,实现setUpMatcher方法,在setUpMatcher()方法中实现添加matcher,当检查发现匹配的结果时,会调用callback()方法,故重新callback方法来对匹配的结果进行处理操作。
接下里,我们就开始自定义规则:
(1). 创建规则cpp文件。
在如下路径/Users/XXX/OCLint/oclint-0.13
处执行以下命令:
oclint-scripts/scaffoldRule CustomRuleName -t SourceCodeReader
- 1
其中,规则类型可选值即为上述三种类型:AbstractASTVisitorRule
、AbstractASTMatcherRule
、AbstractSourceCodeReaderRule
。
此时在路径/Users/XXX/OCLint/oclint-0.13/oclint-rules/custom
下生成了管理自定义规则的CMakeLists.txt文件和自定义规则的逻辑文件CustomRuleNameRule.cpp文件。同时我们可以在/Users/XXX/OCLint/oclint-0.13/oclint-rules
下看到系统默认规则的源文件。
(2). 至此,我们再次执行./make
就可在默认规则路径下找到.dylib
文件了。但是每次执行该命令并不友好,我们可以将规则的开发整合为一个Xcode工程,做法如下:
① 在/Users/XXX/OCLint/oclint-0.13
目录下新建oclint-xcoderules
文件夹。
② 进入oclint-xcoderules
文件夹,创建create-xcode-rules.sh
脚本文件,内容如下:
#! /bin/sh -e
cmake -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
③ 修改权限并执行
chmod 777 create-xcode-rules.sh
./create-xcode-rules.sh
- 1
- 2
④ 有如下输出表明成功,同时生成Xcode工程
-- Configuration done
-- Generating done
-- Build files have been written to:/Users/layne/OCLint/oclint-0.13/oclint-xcoderules
⑤ 打开Xcode工程,编辑相应规则的cpp文件,同时选择相应规则的scheme进行编译,关于如何调试相应规则,可以借鉴这篇文章:OCLint自定义规则的调试方法。同时在编译之后,会在/Users/XXX/OCLint/oclint-0.13/oclint-xcoderules/rules.dl
下找到对应的.dylib
文件。(如果此包最终为要使用的包,最好在release环境下编译并生成,release包比debug包大小要小很多。)
⑥ 若要继续添加新规则,继续执行以下类似命令:
oclint-scripts/scaffoldRule CustomRuleName -t SourceCodeReader
然后在OCLINT_RULES
工程中随便选择一个scheme进行编译,新规则就会被加入进来。
⑦ 若要删除已有的自定义规则,则在/Users/XXX/OCLint/oclint-0.13/oclint-rules/custom
文件夹下删除对应的cpp文件,并在CMakeLists.txt中删除相关配置即可。