OCLint+Xcode實現Code Review
Code Review是開發過程中保證程式碼質量不可或缺的一部分,但是呢,要麼是懶,要麼是真沒時間,在我們公司code review已從原來的流於形式到徹底廢棄了。最近看了看專案程式碼,咋寫的都有,看著確實難受,於是就動了“code review自動化”的念頭。xcode裡有內建的Analyser,但由於預設的規則太少導致功能實在很有限。找了半天,就盯上OCLint了。OCLint是啥我就不多說啦,具體可以檢視OCLint官網。本篇部落格記錄了我如何使用oclint進行靜態程式碼分析,只針對Mac。
一、環境配置
需要安裝oclint和xcpretty。
1、安裝oclint
方法一:brew安裝
命令列執行:
$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
終端執行:
$oclint
出現如下內容證明沒問題:
oclint: Not enough positional command line arguments specified!
Must specify at least 1 positional argument: See: oclint -help
方法三:原始碼安裝 (若要自定義規則,則必須使用原始碼方式安裝,後邊會說到具體方法。)
1、安裝CMake和Ninja
brew install cmake ninja
CMake和Ninja是程式碼編譯工具,因此必須要先安裝。
2、從github上下載/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
然後執行:
./make
之後就開始下載和編譯,不過時間會比較長(40min左右),且還必須可以科學上網才可以。成功之後會有如下路徑:/Users/layne/OCLint/oclint-0.13/build/oclint-release
,這個就是oclint的路徑。
4、新增oclint到環境變數。執行:
vim ~/.bash_profile
將如下內容寫入:
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-r320669
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
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。在框裡輸入如下指令碼程式碼:
chmod -R 777 $SRCROOT/oclint
$SRCROOT/oclint/oclint.sh
(2)編寫指令碼oclint.sh,內容如下:
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資料夾)。最終目錄結構如下:
../LayneStudy.xcodeproj
../LayneSutdy
../oclint
../oclint/oclint.sh
3、執行
回到xcode,scheme選擇OCLint,command+B,編譯完成之後xcode則出現各種警告,證明你成功了。
自定義規則
1、自定義閾值
像指令碼oclint.sh中寫的,使用-rc設定各種閾值,詳見threshold command line,也可以通過寫配置檔案的形式配置閾值,參照threshold configuration file。有一點需要注意,配置檔案要放到oclint-json-compilation-database
命令執行的目錄下,例如oclint.sh中有一段:
......
cd $compilecommandsJsonFolderPath
oclint-json-compilation-database -- -report-type xcode \
......
即oclint-json-compilation-database
是在$compilecommandsJsonFolderPath
中執行的,那麼配置檔案也要放到$compilecommandsJsonFolderPath
下,且命名為.oclint
(會被隱藏)。如果配置檔案無法命名為.oclint
,則需要vim建立一個名為.oclint
的檔案,並將配置的內容寫入,最後移動它到$compilecommandsJsonFolderPath
目錄下。
2、自定義規則
通過自定義規則類,才是真正的自定義 。
預設規則
在下載的oclint包已經有預設的一些規則,路徑如下:
/Users/layne/OCLint/oclint-0.13/lib/oclint/rules
。你沒看錯,規則都是些以dylib結尾的動態庫檔案。若在oclint-json-compilation-database
執行時沒有指定規則載入的路徑,那麼將會載入這些預設的規則。若要自定義規則路徑,則要改寫oclint.sh 如下:
......
oclint-json-compilation-database -- -report-type xcode \
......
改為
......
oclint-json-compilation-database -- -R $compilecommandsJsonFolderPath/oclint_rules -report-type xcode \
......
(要事先在 compilecommandsJsonFolderPath/oclint_rules中的規則。你可以將一些預設規則檔案放到這個目錄下,也可以將自定義的規則檔案放到這個目錄下,進行統一管理。
自定義規則
再次強調: 若要自定義規則,則必須要使用oclint原始碼
OCLint提供了scaffoldRule工具進行自定義規則。
1、建立規則cpp檔案。在如下路徑處Users/layne/OCLint/oclint-0.13
開啟終端視窗,輸入如下命令(可用的選項為:SourceCodeReader、ASTVisitor和ASTMatcher):
oclint-scripts/scaffoldRule ObjCNoSynthesize -t SourceCodeReader
這樣就在以下路徑生成了對應的cpp檔案和MakeFile:
oclint-rules/rules/custom
oclint-rules/test/custom
2、 為了方便開發,生成xcodeproj檔案,使用xcode開發規則。
(1)在oclint-0.13目錄下建立資料夾oclint-xcoderules
(2)進入到oclint-xcoderules目錄(Users/layne/OCLint/oclint-0.13/oclint-xcoderules
),建立shell檔案create-xcode-rules.sh
,內容如下:
#! /bin/sh -e
cmake -G Xcode \
-D CMAKE_CXX_COMPILER=../build/llvm-install/bin/clang++ \
-D CMAKE_C_COMPILER=../build/llvm-install/bin/clang \
-D OCLINT_BUILD_DIR=../build/oclint-core \
-D OCLINT_SOURCE_DIR=../oclint-core \
-D OCLINT_METRICS_SOURCE_DIR=../oclint-metrics \
-D OCLINT_METRICS_BUILD_DIR=../build/oclint-metrics \
-D LLVM_ROOT=../build/llvm-install/ ../oclint-rules
儲存退出之後,更改create-xcode-rules.sh的訪問許可權:
chmod 777 create-xcode-rules.sh
然後執行:
./create-xcode-rules.sh
最後如果出現如下內容證明xcodeproj生成成功了:
......
-- Configuration done
-- Generating done
-- Build files have been written to:/Users/layne/OCLint/oclint-0.13/oclint-xcoderules
並且在oclint-xcoderules資料夾下生成了工程目錄。如下圖:
3、雙擊開啟OCLINT_RULES.xcodeproj,左側顯示所有的規則原始檔,
拉到最底下就可以看到自己建立的規則ObjCNoSynthesize了。在ObjCNoSynthesizeRule.cpp中編寫規則邏輯,之後在Scheme中選擇對應的scheme,然後Command+B進行build,成功之後會在目錄/Users/layne/OCLint/oclint-0.13/oclint-xcoderules/rules.dl/DEBUG
中找到libObjCNoSynthesizeRule.dylib
.至此我們就完成了規則的自定義。
4、建立新規則,即往OCLINT_RULES.xcodeproj專案中新增新規則。
(1)使用命令列建立新規則:
回到如下路徑Users/layne/OCLint/oclint-0.13
開啟終端視窗,輸入如下命令:
oclint-scripts/scaffoldRule ObjCPointerStarShouldBeNearerToVariable -t ASTVisitor
(2)在OCLINT_RULES專案的scheme中隨便選一個target,然後Commnand+B,這樣新建立規則就會被加入到專案中來。
5、刪除規則。
(1)從左邊選擇要刪除的規則的cpp檔案->delete->move to trash;
(2)開啟對應的CMakeLists.txt,刪除掉對應的配置。
(3)Command+B.
DONE!
補充說明:
1、規則的制定主要基於三個類:AbstractASTVisitorRule、AbstractASTMatcherRule和AbstractSourceCodeReaderRule,它們的關係如下:
RuleBase
|
|-AbstractASTRuleBase
| |_ AbstractASTVisitorRule
| |_AbstractASTMatcherRule
|
|-AbstractSourceCodeReaderRule
AbstractSourceCodeReaderRule:
提供eachLine方法,每行的讀取原始碼。對對於想要從每行的內容編寫規則的rule,可以使用繼承自AbstractSourceCodeReaderRule。
AbstractASTVisitorRule:(一般繼承這個類)
繼承自AbstractASTVisitorRule的rule,可以實現訪問AST上特定型別的所有節點,可以檢查特定型別的所有節點是遞迴實現的,在AbstractASTVisitorRule的apply方法中可以看到程式碼實現,開發者只需要通過重寫bool Visit*方法來訪問特定型別的節點,在該函式中實現檢查操作,其返回值往往是返回true,返回值表示是否繼續遞迴檢查。
AbstractASTMatcherRule: (目前沒用過)
繼承自AbstractASTMatcherRule的rule,實現setUpMatcher方法,在setUpMatcher()方法中實現新增matcher,當檢查發現匹配的結果時,會呼叫callback()方法,故重新callback方法來對匹配的結果進行處理操作。
以上就是使用oclint的詳細教程,至於自定義的規則如何去寫,個人建議還是先看看現有規則的原始碼,找找感覺,然後再比著寫。
下面是3個參考連結,我獲益良多,建議深入閱讀,尤其是裡面的例子…
http://oriochan.com/codeReview01.html
https://juejin.im/post/595370986fb9a06bcb7f7d56
http://blog.csdn.net/hdwhappy/article/details/61924772