IOS静态代码扫描--分析与总结
为了进一步加强代码质量,规范并减少代码缺陷,静态代码扫描是上过环节中必不可少的一部分。大多数都希望通过不同的途径提前发现日常测试中难发现的问题。
然而iOS静态代码扫描工具有不少,它们都有什么不同?我应该选哪一个?因此,本文主要针对主流的几个工具,对同步助手的代码进行扫描,并分析对比它们的扫描结果,再敲定后续的接入计划。
该文章从以下几部分进行阐述,可按需阅读:
一、工具介绍 二、扫描能力对比 三、使用问题总结
01
工具介绍
主流的扫描工具:coverity、infer、clang、oclint
- 1、coverity
Coverity是检测和解决C、C 、Java和C#源代码中最严重的缺陷的领先的自动化方法。它将基于布尔可满足性验证技术应用于源代码分析引擎,分析引擎利用其专利的软件DNA图谱技术和meta-compilation技术,综合分析源代码、编译构建系统和操作系统等可能使软件产生的缺陷。但这里要注意的是Coverity检测是收费的。
- 2、clang
Clang作为LLVM编译器框架的前端,最主要的任务是词法分析、语法分析,中间代码生成。源代码通过clang语法分析后,生成了语法分析树(AST)后,可作为静态分析工具对AST进行分析。
Clang命令行调用方法:
(1)下载clang:
代码语言:javascript复制http://clang-analyzer.llvm.org/
(2)命令行cd到项目代码所在目录:
代码语言:javascript复制$ cd /path
(3)使用clang扫描,命令开头为clang的scan-build所在目录:
代码语言:javascript复制$/Users/admin/tools/analyzer/bin/scan-build -vxcodebuild -target QQPimPro -configuration Developer
(4)可以看到生成报告在指定目录下
- 3、infer
Infer是Facebook开源的用来执行增量分析的一款静态分析工具,由OCaml语言编写的infer目前能检测出空指针访问、资源泄露以及内存泄露,可对C、Java和Objective-C代码进行检测。
Infer命令行调用方法:
安装python 2.7:MAC自带;
安装infer:brew install infer
- 4、oclint
Oclint是针对C、C 和Objective C代码的静态扫描分析工具,可以和xcode、xcodebuild、xctool等集成,使用命令行方式生成分析报告。这里主要使用oclint对xcodebuild产生的log进行分析,获取相关数据以后生成html文件。
Oclint命令行调用方法:
下载oclint:https://github.com/oclint/oclint/releases(这里注意下,oclint release目前最高0.13 下载releases版本或者使用brew install oclint则不能安装最新版本,在mac 10.14 上无法执行,需要手动进行编译,编译很简单,见下图) http://docs.oclint.org/en/stable/intro/build.html
一、环境配置
需要安装oclint和xcpretty。
1、安装oclint
方法一:brew安装
命令行执行:
代码语言:javascript复制$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
终端执行:
代码语言:javascript复制 $oclint
代码语言:javascript复制出现如下内容证明没问题:
代码语言:javascript复制oclint: Not enough positional command line arguments specified!
Must specify at least 1 positional argument: See: oclint -help
方法三:源码安装 (若要自定义规则,则必须使用源码方式安装,后边会说到具体方法。)
1、安装CMake和Ninja
代码语言:javascript复制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
然后执行:
代码语言:javascript复制./make
之后就开始下载和编译,不过时间会比较长(40min左右),且还必须能够爬出去才可以。成功之后会有如下路径:/Users/layne/OCLint/oclint-0.13/build/oclint-release
,这个就是oclint的路径。
4、添加oclint到环境变量。执行:
vim ~/.bash_profile
将如下内容写入:
代码语言:javascript复制OCLINT_HOME=/Users/layne/OCLint/oclint-0.13/build/oclint-release
export PATH=$OCLINT_HOME/bin:$PATH
保存退出。重启终端之后在终端执行:oclint --version
,出现如下内容:
LLVM (http://llvm.org/):
LLVM version 5.0.0svn-r.320669
Optimized build.
Default target: x86_64-apple-darwin17.3.0
Host CPU: broadwell
OCLint (http://oclint.org/):
OCLint version 0.13.
Built Dec 14 2017 (16:03:48).
至此oclint安装成功。
2、安装xcpretty
代码语言:javascript复制 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。在框里输入如下脚本代码:
代码语言:javascript复制 chmod -R 777 $SRCROOT/oclint
$SRCROOT/oclint/oclint.sh
(2)编写脚本oclint.sh,内容如下:
代码语言:javascript复制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文件夹)。最终目录结构如下:
代码语言:javascript复制 ../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中设置编码:
代码语言:javascript复制export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
02
扫描能力对比
在未加任何过滤规则的情况下,四个工具对同一份代码进行扫描,并与开发童鞋一起对扫描结果进行了初步筛选和整理:
- (1)准确率:coverity > infer >clang > oclint;
- (2)coverity扫描维度更多、发现问题更精准;infer、clang能发现部分coverity未发现的问题,但误报率较高,可作为补充扫描;但这里要说的是coverity是收费的,并且价格还不算便宜
- (3)infer发现的大部分较为准确(可进行缺陷扫描)
- (4)oclint扫描出的问题数量最多,但大多是开发不关注的问题,可过滤特定结果类型关注,更适合作为扫描代码复杂度的工具。(但对于代码规范这种定制化较高的需求,使用oclint还是不错的选择)
ps:这里提一下 Xcode提供了一个Analyze功能其实就是集成的clang编译扫描
03
使用问题总结
01
1、缺少证书问题
Build代码的时候可能会遇到缺少了部分证书的问题,因此命令行调用时使用了developer模式,可忽略部分证书问题;
具体命令如下:
代码语言:javascript复制xcodebuild build -workspace "${project_name}.xcworkspace" -scheme ${scheme} -configuration Release -sdk iphonesimulator COMPILER_INDEX_STORE_ENABLE=NO
02
2、oclint源码编译
由于oclint是基于LLVM编译器框架,基于python进行的编译,在下载内容的过程中需要科学上网,修改编译代码不是一件可取的事情,如果没能力 还是老老实实用别人编译好的吧
03
xcpretty安装
oclint和infer都需要xcpretty,所以要安装xcpretty