寫在前面
剛?cè)肼毜臅r候剥扣,博哥交代給我一個任務(wù)宜鸯,讓我調(diào)研一款叫SonarQube的靜態(tài)代碼分析工具坎怪,我當時跪在了CentOS與Java環(huán)境配置的混合雙打中岖沛。痛定思痛暑始,我還是先從專精C語言家族靜態(tài)代碼分析的工具OCLint入手開始做一些工作吧。
OCLint是什么
OCLint是一款靜態(tài)代碼分析工具,它可以用來檢查C婴削,C++廊镜,Objective-C的代碼、從而提高代碼的質(zhì)量馆蠕、減少潛在的問題期升。它可以掃描出代碼中存在的問題惊奇,比如:
可能存在的錯誤 - 比如空的 if/ else / try / catch / finally語句
未使用的代碼段 - 比如未使用的局部變量和參數(shù)等
過于復(fù)雜的代碼 - 比如多路徑判斷等
冗余的代碼 - 比如冗余的if語句和無用的括號等
不合理的代碼 - 比如超長的方法和超長的參數(shù)列表
錯誤的做法 - 比如反轉(zhuǎn)邏輯,參數(shù)的重新分配
為什么要使用OCLint
做為一個靜態(tài)代碼分析工具播赁,我們引入 OCLint 的目的主要是為了提高我們的代碼質(zhì)量颂郎。通常我們提高代碼質(zhì)量的方式是通過 CodeReview,但是這個過程耗費的人工和時間往往較大容为,所以我們想通過 OCLint 的一些規(guī)則乓序,讓機器幫我們完成一部分代碼質(zhì)量的檢測,從而提高我們的工作效率坎背。
安裝OCLint
OCLint是運行于Linux和MacOX平臺上的開源工具替劈,可以直接下載源碼進行編譯安裝,也可以使用作者發(fā)布的release版本進行安裝
針對于MacOS系統(tǒng)得滤,可以直接通過Homebrew進行安裝
brew tap oclint/formulae
brew install oclint
當然陨献,如果你所在網(wǎng)絡(luò)環(huán)境不太友好,使用HomeBrew下載龜速的時候懂更,有條件的同學(xué)可以嘗試下載Github上的release版本壓縮包眨业,然后將下載好的壓縮包放在
~/Library/Caches/Homebrew/
路徑下,直接執(zhí)行brew install oclint命令即可安裝沮协。
針對對OCLint升級的方法:
brew update
brew upgrade oclint
使用brew cleanup可以清理舊版本的安裝數(shù)據(jù)龄捡。
xcodebuild
xcodebuild是xcode的編譯命令。
可以查看xcodebuild的版本信息:
xcodebuild -version
Xcode 9.0
Build version 9M202q
也可以查看當前系統(tǒng)的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]命令進行編譯并把相關(guān)的日志信息輸入到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的參數(shù),嗯庄岖,沒錯豁翎,為了使用它我們還得安裝一個xcpretty
gem install xcpretty
接下來就是編譯中的漫長等待了,喝杯咖啡隅忿,稍等一會心剥,我們發(fā)現(xiàn)目錄里多了兩個文件和一個文件夾邦尊。分別是xcodebuild.log,compile_commands.json和一個叫build的文件夾优烧。其中對我們有用的是那個叫compile_commands.json的文件蝉揍。后面我們將使用OCLint工具來分析這個json文件。
OCLint命令行指令
OCLint 包含三個命令行指令:
oclint:基礎(chǔ)指令畦娄。通過這個指令可以指定加載驗證規(guī)則又沾、編譯代碼、分析代碼和生成報告熙卡。
oclint-json-compilation-database:高級指令杖刷。通過這個指令可以從compile_commands.json文件中讀取配置信息并執(zhí)行 oclint。
oclint-xcodebuild:通過這個指令可以從 Xcode 的xcodebuild.log文件導(dǎo)出編譯選項并保存成 JSON Compilation Database 格式驳癌。然后把保存到compile_commands.json文件中滑燃。
然后我們來看一下每個指令的選項和功能:
oclint規(guī)則加載選項
-R <目錄>:指定規(guī)則加載的目錄⊥窍剩可以是多個目錄不瓶,用空格隔開,默認情況下會搜索 $(oclint 可執(zhí)行文件目錄)/../lib/oclint/rules灾杰。
-disable-rule <規(guī)則名> 通過規(guī)則名使某些驗證規(guī)則失效蚊丐。
-rc <參數(shù)>=<值> 修改某些閾值。
下面是一個簡單的示例:
oclint -R /path/to/rules -disable-rule GotoStatement
表示從?/path/to/rules?加載規(guī)則艳吠,然后使?GotoStatement?這個驗證規(guī)則失效麦备。
然后來看一下閾值,下面是 OCLint 里面一些常見的閾值:
CYCLOMATIC_COMPLEXITY循環(huán)嵌套數(shù)限制10
LONG_CLASS類行數(shù)限制1000
LONG_LINE每行的字符限制100
LONG_METHOD方法行數(shù)限制50
LONG_VARIABLE_NAME參數(shù)名字符限制20
MAXIMUM_IF_LENGTHif 的行數(shù)限制15
MINIMUM_CASES_IN_SWITCHswitch case 的最小數(shù)目3
NPATH_COMPLEXITY通過該方法的非循環(huán)執(zhí)行路徑數(shù)量限制200
NCSS_METHOD連續(xù)未注釋行數(shù)限制30
NESTED_BLOCK_DEPTHblock 嵌套層數(shù)限制5
SHORT_VARIABLE_NAME變量名的最小字符數(shù)限制3
TOO_MANY_FIELDS類成員限制20
TOO_MANY_METHODS類方法數(shù)限制30
TOO_MANY_PARAMETERS參數(shù)個數(shù)限制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
編譯數(shù)據(jù)庫選項
-p <構(gòu)建目錄>:選擇一個包含?compile_commands.json?文件的目錄作為構(gòu)建目錄。如果沒有指定構(gòu)建目錄栏渺,oclint 指令會查找第一個輸入文件的所有父目錄來找到?compile_commands.json?文件呛梆。
生成報告選項
-o <目錄>:指定報告的輸出目標。
-report-type <類型名>:指定報告輸出的類型磕诊。默認是普通文本填物。
報告輸出的類型有如下幾種:
Plain Text Report(text)
HTML Report(html)
XML Report(xml)
JSON Reporter (json)
PMD Reporter (pmd):這種類型主要提供給 CI 系統(tǒng)使用,在 CI 系統(tǒng)的展示會更友好霎终。
Xcode Reporter (xcode):主要提供給 Xcode 內(nèi)查看滞磺。
同樣,我們也可以把這個配置加入到 .oclint 文件中:
report-type: html
output: oclint.html
退出狀態(tài)選項
-max-priority-1 <閾值>
-max-priority-2 <閾值>
-max-priority-3 <閾值>
首先我們來看一下 OCLint 會返回的 5 種退出 Code:
0 - SUCCESS:成功莱褒。
1 - RULE_NOT_FOUND:沒有找到驗證規(guī)則击困。
2 - REPORTER_NOT_FOUND:沒有指定報告輸出地址。
3 - ERROR_WHILE_PROCESSING:驗證過程中出錯广凸。
4 - ERROR_WHILE_REPORTING:生成報告時出錯阅茶。
5 - VIOLATIONS_EXCEED_THRESHOLD:違反規(guī)則的次數(shù)超出閾值蛛枚。
然后我們來看一下各個優(yōu)先級默認的閾值:優(yōu)先級 3 的閾值為 20;優(yōu)先級 2 的閾值為 10脸哀;優(yōu)先級 1 的閾值為 0坤候。如果超過了其中任意一個閾值,就表示你的代碼質(zhì)量是不達標的企蹭,然后 OCLint 會返回 VIOLATIONS_EXCEED_THRESHOLD白筹。
全局分析項
-enable-global-analysis
開啟這個選項可以得到更準確的分析結(jié)果,但是相對耗時比較長谅摄,一般不采用徒河。
Clang靜態(tài)分析選項
-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 文件中配置的基礎(chǔ)上添加一些文件/目錄來執(zhí)行 oclint,或者讓一些文件/目錄不執(zhí)行 oclint爷狈。
這兩個選項支持正則匹配植影,因為這個命令是用 Python 寫的,所以正則的格式以Python 正則表達式語法為準涎永。
比如思币,我們一般不對第三方庫執(zhí)行 oclint ,這個時候可以用下面的指令來把 Pods 目錄排除:
oclint-json-compilation-database -e Pods
oclint的選項
可以通過--的方式在指令的最后 oclint 選項羡微。比如:
oclint-json-compilation-database -e Pods -- -o=report.html
在最后添加了一個 oclint 的 -o 選項谷饿,表示將報告輸出到當前目錄的 report.html 文件中。
調(diào)試選項
-v:通過這個選項可以輸出最終執(zhí)行的 oclint 指令妈倔。
-debug:開啟這個選項會給出更詳細的信息博投。但是同樣這個選項只有在 OCLint 的 debug 標示開啟的時候才有效。
與其他工具配合
嗯盯蝴,其實它可以跟很多工具搭配使用毅哗,比如oclint 可以和 Xcode IDE 結(jié)合,把錯誤直接在 IDE 中顯示出來结洼。
首先黎做,我們在項目中創(chuàng)建一個新的 target叉跛,然后選擇Aggregate作為模板松忍。
然后給這個 target 命名,注意我們可以建立多個 target筷厘,然后分別關(guān)注代碼分析的多個方面鸣峭。
然后在 Build Phases 選項卡中選擇 Add Run Script宏所。
關(guān)于腳本的編寫我們?nèi)匀贿x擇最簡單的方式,即 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
然后我們就可以開始執(zhí)行分析了爬骤,因為這里我們選擇的 report-type 是 xcode,這時 oclint 發(fā)現(xiàn)的錯誤會直接在 IDE 中標示出來莫换,方便我們對代碼進行改進霞玄。
OCLint還可以搭配Jenkins使用,限于時間拉岁。這部分我還沒有實踐坷剧。有空的話可以繼續(xù)探索。