iOS工程自動(dòng)化 - OCLint

為什么要使用 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)期待……

參考資料

官方文檔:oclint-docs.readthedocs.io/en/stable/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容