如何提高go代码覆盖率_如何通过静态分析提高iOS代码质量
隨著項目的擴大,依靠人工codereview來保證項目的質量,越來越不現實,這時就有必要借助于一種自動化的代碼審查工具:程序靜態分析。
程序靜態分析(Program Static Analysis)是指在不運行代碼的方式下,通過詞法分析、語法分析、控制流、數據流分析等技術對程序代碼進行掃描,驗證代碼是否滿足規范性、安全性、可靠性、可維護性等指標的一種代碼分析技術。(來自百度百科)
詞法分析,語法分析等工作是由編譯器進行的,所以對iOS項目為了完成靜態分析,我們需要借助于編譯器。對于OC語言的靜態分析可以完全通過Clang,對于Swift的靜態分析除了Clange還需要借助于SourceKit。
Swift語言對應的靜態分析工具是SwiftLint,OC語言對應的靜態分析工具有Infer和OCLitn。以下會是對各個靜態分析工具的安裝和使用做一個介紹。
SwiftLint
對于Swift項目的靜態分析可以使用SwiftLint。SwiftLint 是一個用于強制檢查 Swift 代碼風格和規定的一個工具。它的實現是 Hook 了 Clang 和 SourceKit 從而能夠使用 AST 來表示源代碼文件的更多精確結果。Clange我們了解了,那SourceKit是干什么用的?
SourceKit包含在Swift項目的主倉庫,它是一套工具集,支持Swift的大多數源代碼操作特性:源代碼解析、語法突出顯示、排版、自動完成、跨語言頭生成等工作。
安裝
安裝有兩種方式,任選其一:
方式一:通過Homebrew
$ brew install swiftlint這種是全局安裝,各個應用都可以使用。
方式二:通過CocoaPods
pod 'SwiftLint', :configurations => ['Debug']這種方式相當于把SwiftLint作為一個三方庫集成進了項目,因為它只是調試工具,所以我們應該將其指定為僅Debug環境下生效。
集成進Xcode
我們需要在項目中的Build Phases,添加一個Run Script Phase。如果是通過homebrew安裝的,你的腳本應該是這樣的。
if?which?swiftlint?>/dev/null;?then??swiftlint
else
??echo?"warning:?SwiftLint?not?installed,?download?from?https://github.com/realm/SwiftLint"
fi
如果是通過cocoapods安裝的,你的腳本應該是這樣的:
"${PODS_ROOT}/SwiftLint/swiftlint"運行SwiftLint
鍵入CMD + B編譯項目,在編譯完后會運行我們剛才加入的腳本,之后我們就能看到項目中大片的警告信息。有時候build信息并不能填入項目代碼中,我們可以在編譯的log日志里查看。
定制
SwiftLint規則太多了,如果我們不想執行某一規則,或者想要濾掉對Pods庫的分析,我們可以對SwfitLint進行配置。
在項目根目錄新建一個.swiftlint.yml文件,然后填入如下內容:
disabled_rules:?#?rule?identifiers?to?exclude?from?running??-?colon
??-?trailing_whitespace
??-?vertical_whitespace
??-?function_body_length
opt_in_rules:?#?some?rules?are?only?opt-in
??-?empty_count
??#?Find?all?the?available?rules?by?running:
??#?swiftlint?rules
included:?#?paths?to?include?during?linting.?`--path`?is?ignored?if?present.
??-?Source
excluded:?#?paths?to?ignore?during?linting.?Takes?precedence?over?`included`.
??-?Carthage
??-?Pods
??-?Source/ExcludedFolder
??-?Source/ExcludedFile.swift
??-?Source/*/ExcludedFile.swift?#?Exclude?files?with?a?wildcard
analyzer_rules:?#?Rules?run?by?`swiftlint?analyze`?(experimental)
??-?explicit_self
#?configurable?rules?can?be?customized?from?this?configuration?file
#?binary?rules?can?set?their?severity?level
force_cast:?warning?#?implicitly
force_try:
??severity:?warning?#?explicitly
#?rules?that?have?both?warning?and?error?levels,?can?set?just?the?warning?level
#?implicitly
line_length:?110
#?they?can?set?both?implicitly?with?an?array
type_body_length:
??-?300?#?warning
??-?400?#?error
#?or?they?can?set?both?explicitly
file_length:
??warning:?500
??error:?1200
#?naming?rules?can?set?warnings/errors?for?min_length?and?max_length
#?additionally?they?can?set?excluded?names
type_name:
??min_length:?4?#?only?warning
??max_length:?#?warning?and?error
????warning:?40
????error:?50
??excluded:?iPhone?#?excluded?via?string
??allowed_symbols:?["_"]?#?these?are?allowed?in?type?names
identifier_name:
??min_length:?#?only?min_length
????error:?4?#?only?error
??excluded:?#?excluded?via?string?array
????-?id
????-?URL
????-?GlobalAPIKey
reporter:?"xcode"?#?reporter?type?(xcode,?json,?csv,?checkstyle,?junit,?html,?emoji,?sonarqube,?markdown)
一條rules提示如下,其對應的rules名就是function_body_length。
! Function Body Length Violation: Function body should span 40 lines or less excluding comments and whitespace: currently spans 43 lines (function_body_length)disabled_rules下填入我們不想遵循的規則。
excluded設置我們想跳過檢查的目錄,Carthage、Pod、SubModule這些一般可以過濾掉。
其他的一些像是文件長度(file_length),類型名長度(type_name),我們可以通過設置具體的數值來調節。
另外SwiftLint也支持自定義規則,我們可以根據自己的需求,定義自己的rule。
生成報告
如果我們想將此次分析生成一份報告,也是可以的(該命令是通過homebrew安裝的swiftlint):
#?reporter?type?(xcode,?json,?csv,?checkstyle,?junit,?html,?emoji,?sonarqube,?markdown)$?swiftlint?lint?--reporter?html?>?swiftlint.html
xcodebuild
xcodebuild是xcode內置的編譯命令,我們可以用它來編譯打包我們的iOS項目,接下來介紹的Infer和OCLint都是基于xcodebuild的編譯產物進行分析的,所以有必要簡單介紹一下它。
一般編譯一個項目,我們需要指定項目名,configuration,scheme,sdk等信息以下是幾個簡單的命令及說明。
#?不帶pod的項目,target名為TargetName,在Debug下,指定模擬器sdk環境進行編譯xcodebuild?-target?TargetName?-configuration?Debug?-sdk?iphonesimulator
#?帶pod的項目,workspace名為TargetName.xcworkspace,在Release下,scheme為TargetName,指定真機環境進行編譯。不指定模擬器環境會驗證證書
xcodebuild?-workspace?WorkspaceName.xcworkspace?-scheme?SchemeName?Release
#?清楚項目的編譯產物
xcodebuild?-workspace?WorkspaceName.xcworkspace?-scheme?SchemeName?Release?clean
之后對xcodebuild命令的使用都需要將這些參數替換為自己項目的參數。
Infer
Infer是Facebook開發的針對C、OC、Java語言的靜態分析工具,它同時支持對iOS和Android應用的分析。對于Facebook內部的應用像是 Messenger、Instagram 和其他一些應用均是有它進行靜態分析的。它主要檢測隱含的問題,主要包括以下幾條:
- 資源泄露,內存泄露
- 變量和參數的非空檢測
- 循環引用
- 過早的nil操作
暫不支持自定義規則。
安裝及使用
$?brew?install?infer運行infer
$?cd?projectDir#?跳過對Pods的分析
$?infer?run?--skip-analysis-in-path?Pods?--?xcodebuild?-workspace?"Project.xcworkspace"?-scheme?"Scheme"?-configuration?Debug?-sdk?iphonesimulator
我們會得到一個infer-out的文件夾,里面是各種代碼分析的文件,有txt,json等文件格式,當這樣不方便查看,我們可以將其轉成html格式:
$?infer?explore?--html點擊trace,我們會看到該問題代碼的上下文。
因為Infer默認是增量編譯,只會分析變動的代碼,如果我們想整體編譯的話,需要clean一下項目:
$?xcodebuild?-workspace?"Project.xcworkspace"?-scheme?"Scheme"?-configuration?Debug?-sdk?iphonesimulator?clean再次運行Infer去編譯。
$?infer?run?--skip-analysis-in-path?Pods?--?xcodebuild?-workspace?"Project.xcworkspace"?-scheme?"Scheme"?-configuration?Debug?-sdk?iphonesimulatorInfer的大致原理
Infer的靜態分析主要分兩個階段:
1、捕獲階段
Infer 捕獲編譯命令,將文件翻譯成 Infer 內部的中間語言。
這種翻譯和編譯類似,Infer 從編譯過程獲取信息,并進行翻譯。這就是我們調用 Infer 時帶上一個編譯命令的原因了,比如: infer -- clang -c file.c, infer -- javac File.java。結果就是文件照常編譯,同時被 Infer 翻譯成中間語言,留作第二階段處理。特別注意的就是,如果沒有文件被編譯,那么也沒有任何文件會被分析。
Infer 把中間文件存儲在結果文件夾中,一般來說,這個文件夾會在運行 infer 的目錄下創建,命名是 infer-out/。
2、分析階段
在分析階段,Infer 分析 infer-out/ 下的所有文件。分析時,會單獨分析每個方法和函數。
在分析一個函數的時候,如果發現錯誤,將會停止分析,但這不影響其他函數的繼續分析。
所以你在檢查問題的時候,修復輸出的錯誤之后,需要繼續運行 Infer 進行檢查,知道確認所有問題都已經修復。
錯誤除了會顯示在標準輸出之外,還會輸出到文件 infer-out/bug.txt 中,我們過濾這些問題,僅顯示最有可能存在的。
在結果文件夾中(infer-out),同時還有一個 csv 文件 report.csv,這里包含了所有 Infer 產生的信息,包括:錯誤,警告和信息。
OCLint
OCLint是基于Clange Tooling編寫的庫,它支持擴展,檢測的范圍比Infer要大。不光是隱藏bug,一些代碼規范性的問題,例如命名和函數復雜度也均在檢測范圍之內。
安裝OCLint
OCLint一般通過Homebrew安裝
$?brew?tap?oclint/formulae???$?brew?install?oclint
通過Hombrew安裝的版本為0.13。
$?oclint?--versionLLVM?(http://llvm.org/):
??LLVM?version?5.0.0svn-r313528
??Optimized?build.
??Default?target:?x86_64-apple-darwin19.0.0
??Host?CPU:?skylake
OCLint?(http://oclint.org/):
??OCLint?version?0.13.
??Built?Sep?18?2017?(08:58:40).
我分別用Xcode11在兩個項目上運行過OCLint,一個實例項目可以正常運行,另一個復雜的項目卻運行失敗,報如下錯誤:
1?error?generated1?error?generated
...
oclint:?error:?cannot?open?report?output?file?..../onlintReport.html
我并不清楚原因,如果你想試試0.13能否使用的話,直接跳到安裝xcpretty。如果你也遇到了這個問題,可以回來安裝oclint0.15版本。
OCLint0.15
我在oclint issuse #547這里找到了這個問題和對應的解決方案。
我們需要更新oclint至0.15版本。brew上的最新版本是0.13,github上的最新版本是0.15。我下載github上的release0.15版本,但是這個包并不是編譯過的,不清楚是不是官方自己搞錯了,只能手動編譯了。因為編譯要下載llvm和clange,這兩個包較大,所以我將編譯過后的包直接傳到了這里CodeChecker。
如果不關心編譯過程,可以下載編譯好的包,跳到設置環境變量那一步。
編譯OCLint
1、安裝CMake和Ninja這兩個編譯工具
$?brew?install?cmake?ninja2、clone OCLint項目
$?git?clone?https://github.com/oclint/oclint3、進入oclint-scripts目錄,執行make命令
$?./make成功之后會出現build文件夾,里面有個oclint-release就是編譯成功的oclint工具。
設置oclint工具的環境變量
設置環境變量的目的是為了我們能夠快捷訪問。然后我們需要配置PATH環境變量,注意OCLint_PATH的路徑為你存放oclint-release的路徑。將其添加到.zshrc,或者.bash_profile文件末尾:
OCLint_PATH=/Users/zhangferry/oclint/build/oclint-releaseexport?PATH=$OCLint_PATH/bin:$PATH
執行source .zshrc,刷新環境變量,然后驗證oclint是否安裝成功:
$?oclint?--versionOCLint?(http://oclint.org/):
OCLint?version?0.15.
Built?May?19?2020?(11:48:49).
出現這個介紹就說明我們已經完成了安裝。
安裝xcpretty
xcpretty是一個格式化xcodebuild輸出內容的腳本工具,oclint的解析依賴于它的輸出。它的安裝方式為:
$?gem?install?xcprettyOCLint的使用
在使用OCLint之前還需要一些準備工作,需要將編譯項COMPILER_INDEX_STORE_ENABLE設置為NO。
- 將 Project 和 Targets 中 Building Settings 下的 COMPILER_INDEX_STORE_ENABLE 設置為 NO
- 在 podfile 中 target 'target' do 前面添加下面的腳本,將各個pod的編譯配置也改為此選項
??installer.pods_project.targets.each?do?|target|
??????target.build_configurations.each?do?|config|
??????????config.build_settings['COMPILER_INDEX_STORE_ENABLE']?=?"NO"
??????end
??end
end
使用方式
1、進入項目根目錄,運行如下腳本:
$?xcodebuild?-workspace?ProjectName.xcworkspace?-scheme?ProjectScheme?-configuration?Debug?-sdk?iphonesimulator?|?xcpretty?-r?json-compilation-database?-o?compile_commands.json會將xcodebuild編譯過程中的一些信息記錄成一個文件compile_commands.json,如果我們在項目根目錄看到了該文件,且里面是有內容的,證明我們完成了第一步。
2、我們將這個json文件轉成方便查看的html,過濾掉對Pods文件的分析,為了防止行數上限,我們加上行數的限制:
$?oclint-json-compilation-database?-e?Pods?--?-report-type?html?-o?oclintReport.html?-rc?LONG_LINE=9999?-max-priority-1=9999?-max-priority-2=9999?-max-priority-3=9999最終會產生一個oclintReport.html文件。
OCLint支持自定義規則,因為其本身規則已經很豐富了,自定義規則的需求應該很小,也就沒有嘗試。
封裝腳本
OCLint跟Infer一樣都是通過運行幾個腳本語言進行執行的,我們可以將這幾個命令封裝成一個腳本文件,以OCLint為例,Infer也類似:
#!/bin/bash#?mark?sure?you?had?install?the?oclint?and?xcpretty
#?You?need?to?replace?these?values?with?your?own?project?configuration
workspace_name="WorkSpaceName.xcworkspace"
scheme_name="SchemeName"
#?remove?history
rm?compile_commands.json
rm?oclint_result.xml
#?clean?project
#?-sdk?iphonesimulator?means?run?simulator
xcodebuild?-workspace?$workspace_name?-scheme?$scheme_name?-configuration?Debug?-sdk?iphonesimulator?clean?||?(echo?"command?failed";?exit?1);
#?export?compile_commands.json
xcodebuild?-workspace?$workspace_name?-scheme?$scheme_name?-configuration?Debug?-sdk?iphonesimulator?\
|?xcpretty?-r?json-compilation-database?-o?compile_commands.json?\
||?(echo?"command?failed";?exit?1);
#?export?report?html
#?you?can?run?`oclint?-help`?to?see?all?USAGE
oclint-json-compilation-database?-e?Pods?--?-report-type?html?-o?oclintReport.html?\
-disable-rule?ShortVariableName?\
-rc?LONG_LINE=1000?\
||?(echo?"command?failed";?exit?1);
open?-a?"/Applications/Safari.app"?oclintReport.html
oclint-json-compilation-database命令的幾個參數說明:
-e需要忽略分析的文件,這些文件的警告不會出現在報告中
-rc需要覆蓋的規則的閥值,這里可以自定義項目的閥值,默認閥值
-enable-rule支持的規則,默認是oclint提供的都支持,可以組合-disable-rule來過濾掉一些規則規則列表
-disable-rule需要忽略的規則,根據項目需求設置
在Xcode中使用OCLint
因為OCLint提供了xcode格式的輸出樣式,所以我們可以將它作為一個腳本放在Xcode中。
1、在項目的 TARGETS 下面,點擊下方的 "+" ,選擇 cross-platform 下面的 Aggregate。輸入名字,這里命名為 OCLint
new_target2、選中該Target,進入Build Phases,添加Run Script,寫入下面腳本:
#?Type?a?script?or?drag?a?script?file?from?your?workspace?to?insert?its?path.#?內置變量
cd?${SRCROOT}
xcodebuild?clean?
xcodebuild?|?xcpretty?-r?json-compilation-database
oclint-json-compilation-database?-e?Pods?--?-report-type?xcode
可以看出該腳本跟上面的腳本一樣,只不過 將oclint-json-compilation-database命令的-report-type由html改為了xcode。而OCLint作為一個target本身就運行在特定的環境下,所以xcodebuild可以省去配置參數。
3、通過CMD + B我們編譯一下項目,執行腳本任務,會得到能夠定位到代碼的warning信息:
xcode_warning總結
以下是對這幾種靜態分析方案的對比,我們可以根據需求選擇適合自己的靜態分析方案。
| 支持語言 | Swift | C、C++、OC、Java | C、C++、OC |
| 易用性 | 簡單 | 較簡單 | 較簡單 |
| 能否集成進Xcode | 可以 | 不能集成進xcode | 可以 |
| 自帶規則豐富度 | 較多,包含代碼規范 | 相對較少,主要檢測潛在問題 | 較多,包含代碼規范 |
| 規則擴展性 | 可以 | 不可以 | 可以 |
參考
OCLint 實現 Code Review - 給你的代碼提提質量
Using OCLint in Xcode
Infer 的工作機制
LLVM & Clang 入門
總結
以上是生活随笔為你收集整理的如何提高go代码覆盖率_如何通过静态分析提高iOS代码质量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uniapp 输入框防抖节流_拉动一下控
- 下一篇: java 连接 sftp失败_java