OCLint工具介绍
OCLint是一个静态代码扫描分析工具,可用于提高代码质量和减少潜在的缺陷,目前支持C,C++,Objective-C,它可以扫描出代码中存在的问题,比如:
- 可能存在的错误 - 比如空的 if/ else / try / catch / finally语句
- 未使用的代码段 - 比如未使用的局部变量和参数等
- 过于复杂的代码 - 比如多路径判断等
- 冗余的代码 - 比如冗余的if语句和无用的括号等
- 不合理的代码 - 比如超长的方法和超长的参数列表
- 错误的做法 - 比如反转逻辑,参数的重新分配
- …
该工具配合持续集成,自动打包编译,可以很大程度的提高编码的乐趣以及尽早的修复潜在的问题以降低维护成本
安装OCLint
OCLint是运行于Linux和MacOX平台上的开源工具,可以直接下载源码进行编译安装,也可以使用作者发布的release版本进行安装
针对于MacOS系统,可以直接通过Homebrew进行安装
1. 设置brew的第三方仓库
brew tap oclint/formulae
2. 安装
brew install oclint
3. OCLint升级
brew update
brew upgrade oclint
使用OCLint
命令列表
oclint -help
1、生成格式化的编译日志文件compile_commands.json
-
使用 xcodebuild 对工程进行编译
OCLint需要根据编译日志里的文件信息对工程进行扫描
xcodebuild -list 可以显示项目相关的信息
以quidemo为例
通过以下命令对工程进行编译并将编译的日志信息输出到 xcodebuild.log 文件中
xcodebuild -target qmuidemo -configuration Debug -scheme qmuidemo| tee xcodebuild.log
- xcpretty 格式化编译日志
直接使用OCLint对日志信息进行分析
oclint-xcodebuild xcodebuild.log
得到输出信息为:
This binary is no longer under maintenance by OCLint team.
Please consider using xcpretty (https://github.com/supermarin/xcpretty) instead!
提示需要使用xcpretty来分析日志信息,需要安装xcpretty, xcpretty可对xcodebuild的输出进行格式化。并且可以输出多种格式的日志信息,这里需要安装xcpretty
gem install xcpretty
注:如果没有权限,则在前面添加sudo,如果提示文件夹操作被禁止Operation not permitted - /usr/bin/rougify,则更改$GEM_HOME的路径到/usr/local/bin使用sudo gem install xcpretty -n /usr/local/bin来安装
xcpretty安装之后,使用--report json-compilation-database或者-r json-compilation-database可以生成指定格式的数据,当前指定为json格式。
运行命令:
xcodebuild | xcpretty -r json-compilation-database -o compile_commands.json
如果没有设置json输出路径,则默认的路径在build/reports中,需要移动并改名为compile_commands.json到根目录
如果你想获取编译日志,又想获取格式化后的编译日志可以这样:
xcodebuild [flags] | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json
2、OCLint根据json格式化后的编译日志信息对代码进行扫描分析
使用oclint-json-compilation-database命令对上一步生成的json数据进行分析
将build/reports目录中的compile_commands.json移动到项目根目录,然后运行命令
oclint-json-compilation-database — -report-type=html -o=report.html
对项目代码进行分析,最终生成report.html文件
OCLint目前支持输出html,json,xml,pmd,xcode格式文件
3、OCLint分析脚本
上面的步骤可以写成脚本:
#! /bin/sh
xcodebuild clean
xcodebuild | xcpretty -r json-compilation-database
cp build/reports/compilation_db.json compile_commands.json
oclint-json-compilation-database —-report-type=html -o=report.html
4、OCLint的规则
- 可以通过 -e 参数忽略指定的文件,比如忽略Pods文件夹:
oclint-json-compilation-database -e Pods -- -o=report.html
- 通过-rc改变检查规则的默认值,比如有一条默认规则:long line [size|P3] Line with 137 characters exceeds limit of 100 ,这表示一个方法里的代码行数不能超过100,可以通过-rc改变默认100行的限制比如改成200行:
oclint-json-compilation-database -- -rc=LONG_LINE=200 -o=report.html
具体可以操作哪些规则,可以去官网查询
- 通过 -disable-rule可以禁止某一规则,比如禁止LongLine长方法检查:
oclint-json-compilation-database -disable-rule=LongLine
- 这些命令是可以组合使用,比如:
oclint-json-compilation-database -e Pods -rc=LONG_LINE=200-- -o=report.html
-
如果需要更改的规则比较多,可以通过.oclint 文件配置规则
具体编写规则如下:
规则默认值:
Name | Description | Default |
---|---|---|
CYCLOMATIC_COMPLEXITY | Cyclomatic complexity of a method | 10 |
LONG_CLASS | Number of lines for a C class or Objective-C interface, category, protocol, and implementation | 1000 |
LONG_LINE | Number of characters for one line of code | 100 |
LONG_METHOD | Number of lines for a method or function | 50 |
LONG_VARIABLE_NAME | Number of characters for a variable name | 20 |
MAXIMUM_IF_LENGTH | Number of lines for the if block that would prefer an early exists | 15 |
MINIMUM_CASES_IN_SWITCH | Count of case statements in a switch statement | 3 |
NPATH_COMPLEXITY | NPath complexity of a method | 200 |
NCSS_METHOD | Number of non-commenting source statements of a method | 30 |
NESTED_BLOCK_DEPTH | Depth of a block or compound statement | 5 |
SHORT_VARIABLE_NAME | Number of characters for a variable name | 3 |
TOO_MANY_FIELDS | Number of fields of a class | 20 |
TOO_MANY_METHODS | Number of methods of a class | 30 |
TOO_MANY_PARAMETERS | Number of parameters of a method | 10 |
例如:
disable-rules:
- LongLine
rulePaths:
- /etc/rules
rule-configurations:
- key: CYCLOMATIC_COMPLEXITY
value: 15
- key: NPATH_COMPLEXITY
value: 300
output: oclint.html
report-type: html
enable-clang-static-analyzer: false
在Xcode里使用OCLint
OCLint是支持在Xcode中直接显示代码分析结果的
-
给工程添加一个Aggregate的target
Aggregate
选中刚刚创建的Aggregate target,然后点Build Phases,然后点左上角的加号,添加一个Add Run Script
如果你使用的是xctool ,输入这个脚本:
source~/.bash_profile
cd ${SRCROOT}
/path/to/xctool.sh -reporter json-compilation-database:compile_commands.json clean
/path/to/xctool.sh -reporter json-compilation-database:compile_commands.json build
oclint-json-compilation-database|sed's/\(.*\.\m\{1,2\}:[0-9]*:[0-9]*:\)/\1 warning:/'
如果你使用的是自带的xcodebuild,使用这个脚本
source~/.bash_profile
cd ${SRCROOT}
xcodebuild clean
xcodebuild|xcpretty -r json-compilation-database
oclint-json-compilation-database -- -report-type xcode
- 选中该target,点击编译
如果提示不存在.bash_profile文件,则手动创建该文件,由于该文件是隐藏文件,所有得先执行命令
defaults write com.apple.finder AppleShowAllFiles TRUE
让Finder显示隐藏文件,然后强制重新启动Finder,然后Command+Shift+G前往文件夹,填写~查看,若无此文件,命令行进入次目录创建一个即可:
cd ~
touch .bash_profile
恢复隐藏文件的隐藏
defaults write com.apple.finder AppleShowAllFiles FALSE
后重新启动Finder
4、结果
结果
控制OCLint的检查
有的时候,我们在已知一段代码会产生OCLint的警告,但是因为某些原因,我们没法去修改该段代码,或者没有更好的修改方法,这时候,我们可以在代码中控制OCLint忽略对这段代码的检查
1. 注解
可以使用注解的方法禁止OCLint的检查,语法是:
__attribute__((annotate("oclint:suppress[unused method parameter]")))
比如我们知道一个参数没有使用,而又不想产生警告信息就可以这样写,下面这段代码可以忽略对sender的警告但是没法忽略对参数i的警告:
- (IBAction)turnoverValueChanged: (id) __attribute__((annotate("oclint:suppress[unused method parameter]"))) sender
{
int i;// 这个参数不会被忽略,会产生OCLint警告
[self calculateTurnover];
}
对于方法的注解可以这样写,下面方法中产生的警告都会被忽略掉:
bool __attribute__((annotate("oclint:suppress"))) aMethod(int aParameter)
{
// 这个方法里所有的警告都会被忽略
// 比如下面这个空的if警告会被忽略
if (1) {}
return true;
}
2.!OCLint
也可以通过//!OCLint
注释的方式,不让OCLint检查。比如,禁止对未使用的参数unusedLocalVariable进行检查:
void a() {
int unusedLocalVariable; //!OCLINT
}
注释要写在对应的行上面才能禁止对应的检查,比如对于空的if/else
禁止检查的注释为:
if (true) //!OCLint
{
// it is empty
}