iOS工程自动化之静态代码分析OCLint

写在前面

刚入职的时候,博哥交代给我一个任务,让我调研一款叫SonarQube的静态代码分析工具,我当时跪在了CentOS与Java环境配置的混合双打中。痛定思痛,我还是先从专精C语言家族静态代码分析的工具OCLint入手开始做一些工作吧。

OCLint是什么

OCLint是一款静态代码分析工具,它可以用来检查C,C++,Objective-C的代码、从而提高代码的质量、减少潜在的问题。它可以扫描出代码中存在的问题,比如:

可能存在的错误 - 比如空的 if/ else / try / catch / finally语句

未使用的代码段 - 比如未使用的局部变量和参数等

过于复杂的代码 - 比如多路径判断等

冗余的代码 - 比如冗余的if语句和无用的括号等

不合理的代码 - 比如超长的方法和超长的参数列表

错误的做法 - 比如反转逻辑,参数的重新分配

为什么要使用OCLint

做为一个静态代码分析工具,我们引入 OCLint 的目的主要是为了提高我们的代码质量。通常我们提高代码质量的方式是通过 CodeReview,但是这个过程耗费的人工和时间往往较大,所以我们想通过 OCLint 的一些规则,让机器帮我们完成一部分代码质量的检测,从而提高我们的工作效率。

安装OCLint

OCLint是运行于Linux和MacOX平台上的开源工具,可以直接下载源码进行编译安装,也可以使用作者发布的release版本进行安装

针对于MacOS系统,可以直接通过Homebrew进行安装

brew tap oclint/formulae

brew install oclint

当然,如果你所在网络环境不太友好,使用HomeBrew下载龟速的时候,有条件的同学可以尝试下载Github上的release版本压缩包,然后将下载好的压缩包放在

~/Library/Caches/Homebrew/

路径下,直接执行brew install oclint命令即可安装。

针对对OCLint升级的方法:

brew update

brew upgrade oclint

使用brew cleanup可以清理旧版本的安装数据。

xcodebuild

xcodebuild是xcode的编译命令。

可以查看xcodebuild的版本信息:

xcodebuild -version

Xcode 9.0

Build version 9M202q

也可以查看当前系统的sdk以及其版本:

xcodebuild -showsdks

iOS SDKs:

iOS 11.0 -sdk iphoneos11.0

iOS Simulator SDKs:

Simulator - iOS 11.0 -sdk iphonesimulator11.0

macOS SDKs:

macOS 10.13 -sdk macosx10.13

tvOS SDKs:

tvOS 11.0 -sdk appletvos11.0

tvOS Simulator SDKs:

Simulator - tvOS 11.0 -sdk appletvsimulator11.0

watchOS SDKs:

watchOS 4.0 -sdk watchos4.0

watchOS Simulator SDKs:

Simulator - watchOS 4.0 -sdk watchsimulator4.0

需要使用xcodebuild [flags]命令进行编译并把相关的日志信息输入到xcodebuild.log中,需要在.xcodeproj文件所在的目录里面运行。

xcodebuild | tee xocdebuild.log

在这个地方我遇到了个坑,因为我们的项目是使用CocoaPods来管理第三方库的,所以项目生成了.xcworkspace文件。这时我们想要编译整个项目则需要编译.xcworkspace并指定scheme。

所以WorkSpace版本的编译命令就变成了

xcodebuild -workspace XinRen.xcworkspace -scheme XinRen -sdk iphoneos11.0 -configuration Release | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

其中-workspace是用来声明workspace文件的,-scheme是用来指定workspace中的scheme的,-sdk用来选择sdk版本,-configuration用来声明使用哪种配置编译。还有-arch用来指定architecture,使用armV7,armV7S等。

细心的你一定注意到了有一个叫上面的命令中有一个叫xcpretty的参数,嗯,没错,为了使用它我们还得安装一个xcpretty

gem install xcpretty

接下来就是编译中的漫长等待了,喝杯咖啡,稍等一会,我们发现目录里多了两个文件和一个文件夹。分别是xcodebuild.log,compile_commands.json和一个叫build的文件夹。其中对我们有用的是那个叫compile_commands.json的文件。后面我们将使用OCLint工具来分析这个json文件。

OCLint命令行指令

OCLint 包含三个命令行指令:

oclint:基础指令。通过这个指令可以指定加载验证规则、编译代码、分析代码和生成报告。

oclint-json-compilation-database:高级指令。通过这个指令可以从compile_commands.json文件中读取配置信息并执行 oclint。

oclint-xcodebuild:通过这个指令可以从 Xcode 的xcodebuild.log文件导出编译选项并保存成 JSON Compilation Database 格式。然后把保存到compile_commands.json文件中。

然后我们来看一下每个指令的选项和功能:

oclint规则加载选项

-R <目录>:指定规则加载的目录。可以是多个目录,用空格隔开,默认情况下会搜索 $(oclint 可执行文件目录)/../lib/oclint/rules。

-disable-rule <规则名> 通过规则名使某些验证规则失效。

-rc <参数>=<值> 修改某些阈值。

下面是一个简单的示例:

oclint -R /path/to/rules -disable-rule GotoStatement

表示从 /path/to/rules 加载规则,然后使 GotoStatement 这个验证规则失效。

然后来看一下阈值,下面是 OCLint 里面一些常见的阈值:

CYCLOMATIC_COMPLEXITY循环嵌套数限制10

LONG_CLASS类行数限制1000

LONG_LINE每行的字符限制100

LONG_METHOD方法行数限制50

LONG_VARIABLE_NAME参数名字符限制20

MAXIMUM_IF_LENGTHif 的行数限制15

MINIMUM_CASES_IN_SWITCHswitch case 的最小数目3

NPATH_COMPLEXITY通过该方法的非循环执行路径数量限制200

NCSS_METHOD连续未注释行数限制30

NESTED_BLOCK_DEPTHblock 嵌套层数限制5

SHORT_VARIABLE_NAME变量名的最小字符数限制3

TOO_MANY_FIELDS类成员限制20

TOO_MANY_METHODS类方法数限制30

TOO_MANY_PARAMETERS参数个数限制10

名称

描述

默认值

我们也可以把这些阈值配置到项目的 .oclint 文件下,比如:

rule-configurations:

- key: CYCLOMATIC_COMPLEXITY

value: 15

- key: LONG_LINE

value: 50

编译选项

可以通过 -- 的方式在指令的最后添加编译选项。比如我们通过下面的 clang 指令编译一个文件:

clang -x objective-c -arch armv7 -std=gnu99 -fobjc-arc -O0 -isysroot /Developer/SDKs/iPhoneOS6.0.sdk -g -I./Pods/Headers -c RPActivityIndicatorManager.m

在 OCLint 里面就可以这么写:

oclint [oclint options] RPActivityIndicatorManager.m -- -x objective-c -arch armv7 -std=gnu99 -fobjc-arc -O0 -isysroot /Developer/SDKs/iPhoneOS6.0.sdk -g -I./Pods/Headers -c

编译数据库选项

-p <构建目录>:选择一个包含 compile_commands.json 文件的目录作为构建目录。如果没有指定构建目录,oclint 指令会查找第一个输入文件的所有父目录来找到 compile_commands.json 文件。

生成报告选项

-o <目录>:指定报告的输出目标。

-report-type <类型名>:指定报告输出的类型。默认是普通文本。

报告输出的类型有如下几种:

Plain Text Report(text)

HTML Report(html)

XML Report(xml)

JSON Reporter (json)

PMD Reporter (pmd):这种类型主要提供给 CI 系统使用,在 CI 系统的展示会更友好。

Xcode Reporter (xcode):主要提供给 Xcode 内查看。

同样,我们也可以把这个配置加入到 .oclint 文件中:

report-type: html

output: oclint.html

退出状态选项

-max-priority-1 <阈值>

-max-priority-2 <阈值>

-max-priority-3 <阈值>

首先我们来看一下 OCLint 会返回的 5 种退出 Code:

0 - SUCCESS:成功。

1 - RULE_NOT_FOUND:没有找到验证规则。

2 - REPORTER_NOT_FOUND:没有指定报告输出地址。

3 - ERROR_WHILE_PROCESSING:验证过程中出错。

4 - ERROR_WHILE_REPORTING:生成报告时出错。

5 - VIOLATIONS_EXCEED_THRESHOLD:违反规则的次数超出阈值。

然后我们来看一下各个优先级默认的阈值:优先级 3 的阈值为 20;优先级 2 的阈值为 10;优先级 1 的阈值为 0。如果超过了其中任意一个阈值,就表示你的代码质量是不达标的,然后 OCLint 会返回 VIOLATIONS_EXCEED_THRESHOLD。

全局分析项

-enable-global-analysis

开启这个选项可以得到更准确的分析结果,但是相对耗时比较长,一般不采用。

Clang静态分析选项

-enable-clang-static-analyzer

如果开启这个选项,OCLint 会 hook Clang 的编译过程,然后收集编译信息然后添加到报告中。需要注意的是:这也会增加分析的时间。

Debug选项

-debug

开启这个选项会给出更详细的信息。但是这个选项只有在 OCLint 的 debug 标示开启的时候才有效。

oclint-json-compilation-database过滤选项

-i INCLUDES, -include INCLUDES, –include INCLUDES:

-e EXCLUDES, -exclude EXCLUDES, –exclude EXCLUDES:

这两个选项是指在 compile_commands.json 文件中配置的基础上添加一些文件/目录来执行 oclint,或者让一些文件/目录不执行 oclint。

这两个选项支持正则匹配,因为这个命令是用 Python 写的,所以正则的格式以Python 正则表达式语法为准。

比如,我们一般不对第三方库执行 oclint ,这个时候可以用下面的指令来把 Pods 目录排除:

oclint-json-compilation-database -e Pods

oclint的选项

可以通过--的方式在指令的最后 oclint 选项。比如:

oclint-json-compilation-database -e Pods -- -o=report.html

在最后添加了一个 oclint 的 -o 选项,表示将报告输出到当前目录的 report.html 文件中。

调试选项

-v:通过这个选项可以输出最终执行的 oclint 指令。

-debug:开启这个选项会给出更详细的信息。但是同样这个选项只有在 OCLint 的 debug 标示开启的时候才有效。

与其他工具配合

嗯,其实它可以跟很多工具搭配使用,比如oclint 可以和 Xcode IDE 结合,把错误直接在 IDE 中显示出来。

首先,我们在项目中创建一个新的 target,然后选择Aggregate作为模板。

然后给这个 target 命名,注意我们可以建立多个 target,然后分别关注代码分析的多个方面。

然后在 Build Phases 选项卡中选择 Add Run Script。

关于脚本的编写我们仍然选择最简单的方式,即 xcodebuild + xcpretty + oclint-json-compilation-database 的方式来做 oclint。脚本如下:

source ~/.bash_profile

cd ${PROJECT_DIR}

xcodebuild clean

xcodebuild -workspace LPDLogger.xcworkspace -configuration Debug -scheme LPDLogger-Example build | xcpretty -r json-compilation-database -o compile_commands.json

oclint-json-compilation-database -- -report-type xcode

然后我们就可以开始执行分析了,因为这里我们选择的 report-type 是 xcode,这时 oclint 发现的错误会直接在 IDE 中标示出来,方便我们对代码进行改进。

OCLint还可以搭配Jenkins使用,限于时间。这部分我还没有实践。有空的话可以继续探索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容