文章結(jié)構(gòu):
1. 什么是 OCLint
OCLint 是針對(duì)于 C,C++,Objective-C 代碼的靜態(tài)分析工具,目的是提高軟件質(zhì)量并且減少代碼中存在的潛在問(wèn)題,OCLint 旨于分析以下潛在問(wèn)題:
- 可能出現(xiàn)的 bug:if/else/try/catch 等條件語(yǔ)句空的聲明
- 未使用的代碼: 未使用的局部變量以及參數(shù)
- 復(fù)雜的代碼邏輯:高循環(huán)復(fù)雜度、NP 復(fù)雜度(懵)、 高 NCSS(懵)
- 冗余代碼:冗余的條件表達(dá)式以及無(wú)效的括號(hào)
- 代碼嗅覺(jué):方法代碼行過(guò)長(zhǎng)或者參數(shù)過(guò)多
- 不好的代碼習(xí)慣:顛倒的邏輯和參數(shù)的錯(cuò)誤分配
- ...
靜態(tài)代碼分析工具是偵測(cè)編譯器不可見的潛在缺陷的關(guān)鍵技術(shù)。OCLint 具有以下先進(jìn)的代碼檢驗(yàn)特性:
- 依靠源碼的抽象語(yǔ)法樹來(lái)提高分析的精確度以及效率,誤報(bào)率低
- 動(dòng)態(tài)規(guī)則
- 靈活可擴(kuò)展的配置,確保用戶可以自定義分析行為
- 命令行式的調(diào)用使持續(xù)集成成為可能
2. OCLint 安裝
2.1. Homebrew 安裝
確保你已經(jīng)安裝了 Homebrew
$ brew install oclint
近些天使用 Homebrew 安裝 OCLint 經(jīng)常失敗,遂放棄了這種安裝方式。
2.2. 下載并設(shè)置下載目錄為環(huán)境變量
添加以下代碼到命令行啟動(dòng)文件中,默認(rèn)啟動(dòng)文件是 .bashrc 或 .bash_profile ,安裝 oh-my-zsh 后是 .zshrc 文件。
$ OCLINT_HOME=path-to-oclint-release
$ export PATH=$OCLINT_HOME/bin:$PATH
解釋:path-to-oclint-release是 OCLint 安裝包的路徑
使用以下命令判斷是否添加環(huán)境變量成功
echo $PATH
2.3. 下載并拷貝至系統(tǒng)環(huán)境變量中
也可以直接將 OCLint 的可執(zhí)行文件拷貝至系統(tǒng)文件夾 /usr/local/bin。可以注意到 /usr/local/bin 也在 PATH 中。
cd oclint
cp bin/oclint* /usr/local/bin/
cp -rp lib/* /usr/local/lib/
3. OCLint 工具的組成
OCLint 工具集由一下三部分組成
- oclint
- oclint-json-compilation-database
- oclint-xcodebuild
具體的使用手冊(cè)還是建議大家去閱讀他們的官方文檔,其功能可以簡(jiǎn)單概括為:
-
oclint是 OCLint 工具集最主要的指令,主要作用是規(guī)則加載、編譯分析選項(xiàng)以及生成分析報(bào)告 -
oclint-json-compilation-database的作用是在 JSON Compilation Database format 類型的編譯文件compile_commands.json中提取必要的信息。 -
oclint-xcodebuild用于將xcodebuild生成的 log 文件xcodebuild.log轉(zhuǎn)換為JSON Compilation Database format類型
可以使用時(shí)序圖來(lái)概括我們使用這幾個(gè)指令的場(chǎng)景:

4. OCLint 的使用
4.1. OCLint 結(jié)合 xcodebuild 的使用
xcodebuild 命令用來(lái)編譯 Xcode 工程,xcodebuild 可以編譯 Xcode 工程里面的一個(gè)或多個(gè) target,也可以用來(lái)編譯 Xcode workspace 或者 Xcode project 中的任意一個(gè) scheme。
OCLint 結(jié)合 xcodebuild 的使用主要分為一下幾個(gè)步驟
- 使用 xcodebuild clean 清理工程(可選)
- 使用 xcodebuild build 編譯工程 并且使用 xcpretty 輸出編譯文件
- 使用 oclint-json-compilation-database 輸出分析結(jié)果
4.1.1. 清理工程(可選)
假設(shè)一個(gè)源文件已經(jīng)使用 xcodebuild 編譯過(guò)了,并且沒(méi)有被修改,那么下次編譯的時(shí)候改源文件不參與編譯,也就是說(shuō)生成的 compile_commands.json 文件里面是不含有該源文件的內(nèi)容的。如果你希望該源文件每次都編譯,可以使用 xcodebuild clean 指令來(lái)清理編譯緩存。
然而 clean 過(guò)后,編譯過(guò)程會(huì)變得相當(dāng)漫長(zhǎng),這種現(xiàn)象在大項(xiàng)目中表現(xiàn)最為明顯。所以說(shuō)如果你的項(xiàng)目文件沒(méi)改動(dòng),工程的編譯選項(xiàng)也未修改。使用上次的 xcodebuild 和 compile_commands.json 進(jìn)行分析也是可取的。這里是否可以得出增量編譯的條件下,項(xiàng)目中未變動(dòng)代碼的部分是不會(huì)出現(xiàn)在新的編譯報(bào)告中的。
4.1.2. 編譯工程,輸出編譯報(bào)告
使用 xcodebuild 命令編譯工程,例如使用一下命令編譯 OCLintDemo.xcworkspace 下的 OCLintDemo scheme。
xcodebuild -workspace OCLintDemo.xcworkspace \
-scheme OCLintDemo \
-configuration Debug \
-sdks iphonesimulator10.3 \
build
此時(shí)就有兩種方式生成 compile_commands.json。
方式一:使用 tee 指令獲取輸出結(jié)果為 xcodebuild.log ,使用 oclint-xcodebuild 將 xcodebuild.log 輸出為 compile_commands.json 文件
xcodebuild -workspace OCLintDemo.xcworkspace \
-scheme OCLintDemo \
-configuration Debug \
-sdks iphonesimulator10.3 \
build | tee xcodebuild.log
oclint-xcodebuild
方式二:使用 xcpretty 直接將編譯結(jié)果輸出為 compile_commands.json
xcodebuild -workspace OCLintDemo.xcworkspace \
-scheme OCLintDemo \
-configuration Debug \
-sdks iphonesimulator10.3 \
build | xcpretty -r json-compilation-database -o compile_commands.json
推薦使用 xcpretty 的方式生成 json-compilation-database 類型的編譯報(bào)告。
4.1.3. 輸出分析結(jié)果
使用 oclint-json-compilation-database 指令來(lái)解析 json-compilation-database 類型的編譯報(bào)告。
$ oclint-json-compilation-database --help
usage: oclint-json-compilation-database [-h] [-v] [-debug] [-i INCLUDES]
[-e EXCLUDES]
[oclint_args [oclint_args ...]]
OCLint for JSON Compilation Database (compile_commands.json)
positional arguments:
oclint_args arguments that are passed to OCLint invocation
optional arguments:
-h, --help show this help message and exit
-v show invocation command with arguments
-debug, --debug invoke OCLint in debug mode
-i INCLUDES, -include INCLUDES, --include INCLUDES
extract files matching pattern
-e EXCLUDES, -exclude EXCLUDES, --exclude EXCLUDES
remove files matching pattern
從引導(dǎo)上來(lái)看,oclint-json-compilation-database 可以通過(guò) -e 選項(xiàng)來(lái)忽略對(duì)制定路徑文件的分析,對(duì)于使用 Cocoapods 來(lái)管理依賴的工程,我們往往會(huì)忽略 Pods 文件夾。
oclint-json-compilation-database -e Pods -- xxxx
通常使用 -- 來(lái)分割 oclint-json-compilation-database 的參數(shù)與 oclint_args。oclint_args 就是 oclint 命令的參數(shù),接下來(lái)我們來(lái)看看 oclint 指令支持的參數(shù)。
$ oclint --help
USAGE: oclint [options] <source0> [... <sourceN>]
OPTIONS:
OCLint options:
-R=<directory> - Add directory to rule loading path
-allow-duplicated-violations - Allow duplicated violations in the OCLint report
-disable-rule=<rule name> - Disable rules
-enable-clang-static-analyzer - Enable Clang Static Analyzer, and integrate results into OCLint report
-enable-global-analysis - Compile every source, and analyze across global contexts (depends on number of source files, could results in high memory load)
-extra-arg=<string> - Additional argument to append to the compiler command line
-extra-arg-before=<string> - Additional argument to prepend to the compiler command line
-list-enabled-rules - List enabled rules
-max-priority-1=<threshold> - The max allowed number of priority 1 violations
-max-priority-2=<threshold> - The max allowed number of priority 2 violations
-max-priority-3=<threshold> - The max allowed number of priority 3 violations
-no-analytics - Disable the anonymous analytics
-o=<path> - Write output to <path>
-p=<string> - Build path
-rc=<parameter>=<value> - Override the default behavior of rules
-report-type=<name> - Change output report type
-rule=<rule name> - Explicitly pick rules
Generic Options:
-help - Display available options (-help-hidden for more)
-help-list - Display list of available options (-help-list-hidden for more)
-version - Display the version of this program
-p <build-path> is used to read a compile command database.
For example, it can be a CMake build directory in which a file named
compile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
CMake option to get this output). When no build path is specified,
a search for compile_commands.json will be attempted through all
parent paths of the first input file . See:
http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an
example of setting up Clang Tooling on a source tree.
<source0> ... specify the paths of source files. These paths are
looked up in the compile command database. If the path of a file is
absolute, it needs to point into CMake's source tree. If the path is
relative, the current working directory needs to be in the CMake
source tree and the file must be in a subdirectory of the current
working directory. "./" prefixes in the relative files will be
automatically removed, but the rest of a relative path must be a
suffix of a path in the compile command database.
For more information, please visit http://oclint.org
如果想最終輸出一個(gè) HTML 類型的分析報(bào)告,一個(gè)完整的 oclint-json-compile-database 指令應(yīng)該這么寫
$ oclint-json-compilation-database -e Pods -- -o=report.html
4.2. 使用 OCLint 分析 Xcode 工程
5. OCLint 與持續(xù)集成
6. 遇到的問(wèn)題
6.1. PCH 錯(cuò)誤
Compiler Errors: (please be aware that these errors will prevent OCLint from analyzing this source code) :0:0: input is not a PCH file: '/xxx/PrefixHeader.pch.pch' :0:0: file '/xxx/PrefixHeader.pch.pch' is not a valid precompiled PCH file :0:0: input is not a PCH file: '/xxx/PrefixHeader.pch.pch' :0:0: file '/xxx/PrefixHeader.pch.pch' is not a valid precompiled PCH file OCLint Report Summary: TotalFiles=0 FilesWithViolations=0 P1=0 P2=0 P3=0 [OCLint (http://oclint.org) v0.12]
沒(méi)找到問(wèn)題原因,目前只知道將編譯選項(xiàng) Precompile prefix header 設(shè)置為 NO 可以解決問(wèn)題。
6.2. oclint: error: violations exceed threshold
運(yùn)行 oclint-json-compilation-databse 以下錯(cuò)誤
$ oclint-json-compilation-database -e Pods -- -o=report.html
oclint: error: violations exceed threshold
P1=0[0] P2=1637[10] P3=53904[20]
出現(xiàn)這個(gè)的原因是項(xiàng)目中的 issue 超過(guò)了限制,使用以下 option 來(lái)約定最大的 issue 閾值。
-max-priority-1=9999 -max-priority-2=9999 -max-priority-3=9999