為什么要使用 OCLint
做為一個(gè)靜態(tài)代碼分析工具,我們引入 OCLint 的目的主要是為了提高我們的代碼質(zhì)量信不。通常我們提高代碼質(zhì)量的方式是通過(guò) CodeReview嘲叔,但是這個(gè)過(guò)程耗費(fèi)的人工和時(shí)間往往較大,所以我們想通過(guò) OCLint 的一些規(guī)則抽活,讓機(jī)器幫我們完成一部分代碼質(zhì)量的檢測(cè)硫戈,從而提高我們的工作效率。
安裝 OCLint
OCLint 的安裝方式有很多中下硕,這里我們選擇最簡(jiǎn)單的方式:通過(guò) Homebrew 安裝丁逝。
安裝 Homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
安裝 OCLint
brew tap oclint/formulae
brew install oclint
命令行指令
OCLint 包含三個(gè)命令行指令:
oclint:基礎(chǔ)指令汁胆。通過(guò)這個(gè)指令可以指定加載驗(yàn)證規(guī)則、編譯代碼霜幼、分析代碼和生成報(bào)告嫩码。
oclint-json-compilation-database:高級(jí)指令。通過(guò)這個(gè)指令可以從
compile_commands.json
文件中讀取配置信息并執(zhí)行 oclint罪既。oclint-xcodebuild:通過(guò)這個(gè)指令可以從 Xcode 的
xcodebuild.log
文件導(dǎo)出編譯選項(xiàng)并保存成 JSON Compilation Database 格式铸题。然后把保存到compile_commands.json
文件中。
然后我們來(lái)看一下每個(gè)指令的選項(xiàng)和功能:
oclint
規(guī)則加載選項(xiàng)
-R <目錄>:指定規(guī)則加載的目錄萝衩』赝欤可以是多個(gè)目錄,用空格隔開猩谊,默認(rèn)情況下會(huì)搜索
$(oclint 可執(zhí)行文件目錄)/../lib/oclint/rules
千劈。-disable-rule <規(guī)則名> 通過(guò)規(guī)則名使某些驗(yàn)證規(guī)則失效。
-rc <參數(shù)>=<值> 修改某些閾值牌捷。
下面是一個(gè)簡(jiǎn)單的示例:
oclint -R /path/to/rules -disable-rule GotoStatement
表示從/path/to/rules
加載規(guī)則墙牌,然后使 GotoStatement
這個(gè)驗(yàn)證規(guī)則失效。
然后來(lái)看一下閾值暗甥,下面是 OCLint 里面一些常見的閾值:
名稱 | 描述 | 默認(rèn)值 |
---|---|---|
CYCLOMATIC_COMPLEXITY | 循環(huán)嵌套數(shù)限制 | 10 |
LONG_CLASS | 類行數(shù)限制 | 1000 |
LONG_LINE | 每行的字符限制 | 100 |
LONG_METHOD | 方法行數(shù)限制 | 50 |
LONG_VARIABLE_NAME | 參數(shù)名字符限制 | 20 |
MAXIMUM_IF_LENGTH | if 的行數(shù)限制 | 15 |
MINIMUM_CASES_IN_SWITCH | switch case 的最小數(shù)目 | 3 |
NPATH_COMPLEXITY | 通過(guò)該方法的非循環(huán)執(zhí)行路徑數(shù)量限制 | 200 |
NCSS_METHOD | 連續(xù)未注釋行數(shù)限制 | 30 |
NESTED_BLOCK_DEPTH | block 嵌套層數(shù)限制 | 5 |
SHORT_VARIABLE_NAME | 變量名的最小字符數(shù)限制 | 3 |
TOO_MANY_FIELDS | 類成員限制 | 20 |
TOO_MANY_METHODS | 類方法數(shù)限制 | 30 |
TOO_MANY_PARAMETERS | 參數(shù)個(gè)數(shù)限制 | 10 |
我們也可以把這些閾值配置到項(xiàng)目的 .oclint
文件下喜滨,比如:
rule-configurations:
- key: CYCLOMATIC_COMPLEXITY
value: 15
- key: LONG_LINE
value: 50
編譯選項(xiàng)
可以通過(guò)--
的方式在指令的最后添加編譯選項(xiàng)。
比如我們通過(guò)下面的clang
指令編譯一個(gè)文件:
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
編譯數(shù)據(jù)庫(kù)選項(xiàng)
-p <構(gòu)建目錄>:選擇一個(gè)包含compile_commands.json
文件的目錄作為構(gòu)建目錄撤防。如果沒(méi)有指定構(gòu)建目錄虽风,oclint 指令會(huì)查找第一個(gè)輸入文件的所有父目錄來(lái)找到compile_commands.json
文件。
生成報(bào)告選項(xiàng)
-o <目錄>:指定報(bào)告的輸出目標(biāo)寄月。
-report-type <類型名>:指定報(bào)告輸出的類型辜膝。默認(rèn)是普通文本。
報(bào)告輸出的類型有如下幾種:
Plain Text Report(text)
HTML Report(html)
XML Report(xml)
JSON Reporter (json)
PMD Reporter (pmd):這種類型主要提供給 CI 系統(tǒng)使用漾肮,在 CI 系統(tǒng)的展示會(huì)更友好厂抖。
Xcode Reporter (xcode):主要提供給 Xcode 內(nèi)查看。
同樣克懊,我們也可以把這個(gè)配置加入到 .oclint
文件中:
report-type: html
output: oclint.html
退出狀態(tài)選項(xiàng)
-max-priority-1 <閾值>
-max-priority-2 <閾值>
-max-priority-3 <閾值>
首先我們來(lái)看一下 OCLint 會(huì)返回的 5 種退出 Code:
0 - SUCCESS:成功忱辅。
1 - RULE_NOT_FOUND:沒(méi)有找到驗(yàn)證規(guī)則。
2 - REPORTER_NOT_FOUND:沒(méi)有指定報(bào)告輸出地址谭溉。
3 - ERROR_WHILE_PROCESSING:驗(yàn)證過(guò)程中出錯(cuò)墙懂。
4 - ERROR_WHILE_REPORTING:生成報(bào)告時(shí)出錯(cuò)。
5 - VIOLATIONS_EXCEED_THRESHOLD:違反規(guī)則的次數(shù)超出閾值扮念。
然后我們來(lái)看一下各個(gè)優(yōu)先級(jí)默認(rèn)的閾值:優(yōu)先級(jí) 3 的閾值為 20损搬;優(yōu)先級(jí) 2 的閾值為 10;優(yōu)先級(jí) 1 的閾值為 0。如果超過(guò)了其中任意一個(gè)閾值场躯,就表示你的代碼質(zhì)量是不達(dá)標(biāo)的,然后 OCLint 會(huì)返回 VIOLATIONS_EXCEED_THRESHOLD旅挤。
全局分析選項(xiàng)
-enable-global-analysis
開啟這個(gè)選項(xiàng)可以得到更準(zhǔn)確的分析結(jié)果踢关,但是相對(duì)耗時(shí)比較長(zhǎng),一般不采用粘茄。
Clang 靜態(tài)分析選項(xiàng)
-enable-clang-static-analyzer
如果開啟這個(gè)選項(xiàng)签舞,OCLint 會(huì) hook Clang 的編譯過(guò)程,然后收集編譯信息然后添加到報(bào)告中柒瓣。需要注意的是:這也會(huì)增加分析的時(shí)間儒搭。
Debug 選項(xiàng)
-debug
開啟這個(gè)選項(xiàng)會(huì)給出更詳細(xì)的信息。但是這個(gè)選項(xiàng)只有在 OCLint 的 debug 標(biāo)示開啟的時(shí)候才有效芙贫。
oclint-json-compilation-database
過(guò)濾選項(xiàng)
-i INCLUDES, -include INCLUDES, –include INCLUDES:
-e EXCLUDES, -exclude EXCLUDES, –exclude EXCLUDES:
這兩個(gè)選項(xiàng)是指在 compile_commands.json
文件中配置的基礎(chǔ)上添加一些文件/目錄來(lái)執(zhí)行 oclint搂鲫,或者讓一些文件/目錄不執(zhí)行 oclint。
這兩個(gè)選項(xiàng)支持正則匹配磺平,因?yàn)檫@個(gè)命令是用 Python 寫的魂仍,所以正則的格式以 Python 正則表達(dá)式語(yǔ)法 為準(zhǔn)。
比如拣挪,我們一般不對(duì)第三方庫(kù)執(zhí)行 oclint 擦酌,這個(gè)時(shí)候可以用下面的指令來(lái)把 Pods 目錄排除:
oclint-json-compilation-database -e Pods
oclint 的選項(xiàng)
可以通過(guò) --
的方式在指令的最后 oclint 選項(xiàng)。比如:
oclint-json-compilation-database -e Pods -- -o=report.html
在最后添加了一個(gè) oclint 的 -o 選項(xiàng)菠劝,表示將報(bào)告輸出到當(dāng)前目錄的 report.html 文件中赊舶。
調(diào)試選項(xiàng)
-v:通過(guò)這個(gè)選項(xiàng)可以輸出最終執(zhí)行的 oclint 指令。
-debug:開啟這個(gè)選項(xiàng)會(huì)給出更詳細(xì)的信息赶诊。但是同樣這個(gè)選項(xiàng)只有在 OCLint 的 debug 標(biāo)示開啟的時(shí)候才有效笼平。
oclint-xcodebuild
這個(gè)命令是給使用 Xcode 的用戶提供的。主要用于分析 xcodebuild.log
文件甫何,然后快速生成 compile_commands.json
文件出吹。
這貨已經(jīng)被 oclint 拋棄了,改用 xcpretty辙喂。
安裝 xcpretty
其實(shí)只需要執(zhí)行下面指令即可:
gem install xcpretty
使用 xcpretty 生成compile_commands.json
文件
通過(guò)下面的指令即可生成 compile_commands.json
文件:
xcodebuild [flags] | xcpretty -r json-compilation-database -o compile_commands.json
如果想保存 xcodebuild.log
捶牢,可以換成下面的指令:
xcodebuild [flags] | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json
然后就可以執(zhí)行 oclint-json-compilation-database
來(lái)進(jìn)行驗(yàn)證了。
與其他工具配合
xcodebuild
因?yàn)?oclint-xcodebuild 已經(jīng)被 oclint 拋棄了,所以跟 xcodebuild 的配合可以直接忽略亮元。
xctool
了解到 xctool 在 Xcode 8 以后已經(jīng)不再支持 build恼琼,而是變成了 xcbuild 指令,所以我們換成 xcbuild 來(lái)生成 compile_commands.json
文件灸蟆。
安裝 xcbuild
首先從 github 下載源碼:
git clone https://github.com/facebook/xcbuild
cd xcbuild
git submodule update --init
然后執(zhí)行如下指令:
make
這里需要用到 cmake 和 ninja,所以我們通過(guò)下面的指令安裝:
brew install cmake ninja
使用 xcbuild 生成 compile_commands.json
文件
使用如下指令即可生成 compile_commands.json
文件
xcbuild [flags] | xcpretty -r json-compilation-database -o compile_commands.json
事實(shí)上 xcbuild 和 xcodebuild 的指令是完全兼容的…
Xcode IDE
oclint 可以和 Xcode IDE 結(jié)合亲族,把錯(cuò)誤直接在 IDE 中顯示出來(lái)炒考。
首先可缚,我們?cè)陧?xiàng)目中創(chuàng)建一個(gè)新的 target,然后選擇 Aggregate
作為模板斋枢。
然后給這個(gè) target 命名帘靡,注意我們可以建立多個(gè) target,然后分別關(guān)注代碼分析的多個(gè)方面瓤帚。
然后在 Build Phases 選項(xiàng)卡中選擇 Add Run Script描姚。
關(guān)于腳本的編寫我們?nèi)匀贿x擇最簡(jiǎn)單的方式,即 xcodebuild + xcpretty + oclint-json-compilation-database 的方式來(lái)做 oclint戈次。腳本如下:
source ~/.bash_profilecd
${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
然后我們就可以開始執(zhí)行分析了轩勘,因?yàn)檫@里我們選擇的 report-type 是 xcode,這時(shí) oclint 發(fā)現(xiàn)的錯(cuò)誤會(huì)直接在 IDE 中標(biāo)示出來(lái)怯邪,方便我們對(duì)代碼進(jìn)行改進(jìn)绊寻。
Travis CI
Travis CI 大家應(yīng)該都很熟悉,它的配置也很簡(jiǎn)單擎颖,在工程目錄下添加 .travis.yml
文件即可榛斯。
所以我們?cè)?.travis.yml
中配置如下的內(nèi)容即可:
language: objective-c
osx_image: xcode8.0
before_install:
- brew cask uninstall oclint
- brew tap oclint/formulae
- brew install oclint
script:
- xcodebuild -workspace LPDLogger.xcworkspace -configuration Debug -scheme LPDLogger-Example build | xcpretty -r json-compilation-database -o compile_commands.json
- oclint-json-compilation-database
我們對(duì)這個(gè)文件做一下分析:
首先,
language
設(shè)置為objective-c
搂捧,osx_image
設(shè)置為xcode8.0
然后
before_install
所做的事情就是安裝 oclint驮俗。最后
script
所做的事情就是對(duì)代碼做 oclint。
Jenkins CI
相比 Travis CI允跑,Jenkins CI 提供了更友好的界面來(lái)進(jìn)行 oclint 的配置和報(bào)告展示王凑。
注:這個(gè)部分筆者還沒(méi)有親自實(shí)踐,所以主要還是把官方的英文文檔用中文的方式表述一遍聋丝。
建立持續(xù)集成的工程
首先索烹,創(chuàng)建一個(gè) free-style 的項(xiàng)目:
設(shè)置持續(xù)集成項(xiàng)目所需要的一些步驟:
配置 OCLint 和 PMD 插件
新建一個(gè)類型為 Execute shell 的 build step:
然后配置 oclint 的指令,這里有幾個(gè)注意點(diǎn):
先添加生成
compile_commands.json
指令弱睦。在某些情況下百姓,
oclint
指令比oclint-json-compilation-database
指令好用。設(shè)置 report-type 為 pmd况木。
設(shè)置輸出文件名垒拢,接下來(lái)我們還需要用到這個(gè)名字。
新建一個(gè)類型為 Publish PMD analysis results
的 post-build action火惊。
然后輸入剛才輸出的文件名求类。
執(zhí)行分析
然后我們就可以開始進(jìn)行分析了,分析結(jié)束之后我們就可以通過(guò) PMD Warnings
來(lái)查看各個(gè)規(guī)則相應(yīng)的報(bào)錯(cuò)數(shù)了屹耐。
結(jié)語(yǔ)
至此 OCLint 的介紹以及集成都已經(jīng)完成了尸疆,大家有興趣的話也可以在自己的開源項(xiàng)目中實(shí)踐一下,總的來(lái)說(shuō)還是比較簡(jiǎn)單的。下一篇文章應(yīng)該是 Cocoapods 庫(kù)發(fā)布相關(guān)的一些內(nèi)容寿弱,敬請(qǐng)期待……