Android git commit之前進行checkStyle檢查
轉自: https://blog.csdn.net/u010479969/article/details/52782060
背景:開發的人員越來越多,水平習慣參差不齊,這就導致了程式碼的維護越來越複雜,所以希望有一個規範,可以規範大家的提交,所以出現了我所做的這個通過hook實現在程式碼commit之前進行檢查。
實現效果:針對我所提交的程式碼進行checkStyle檢查。
實現:
1.修改pre-commit檔案。
在.git下面的hooks資料夾中增加pre-commit 檔案,
修改許可權chmod a+x pre-commit
#!/bin/sh # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi SCRIPT_DIR=$(dirname "$0") SCRIPT_ABS_PATH=`cd "$SCRIPT_DIR"; pwd` cd $SCRIPT_ABS_PATH/../../UCarNew/ cd .. FILE_LIST=$(git status) cd UCarNew RESULT_INFO=$($SCRIPT_ABS_PATH/../../UCarNew/gradlew -Dorg.gradle.project.checkstyle="$FILE_LIST" checkstyle) if [ $? -eq 0 ]; then echo "checkstyle OK" else [[ $ERROR_INFO =~ "checkstyle" ]] && exit 1 fi cd ..
2.在build.gradle檔案中新增checkstyle的task
task checkstyle(type :Checkstyle) { source 'src' source 'biz_mine' source 'base' source 'sdk' source 'thirdparty' source 'patch' source 'plugin' if(project.hasProperty('checkstyle')){ def ft = fc(checkstyle); for(int i =0;i<ft.size();i++){ String spliter = ft.getAt(i); String[] fileName = spliter.split("/"); include '**/'+fileName[fileName.size()-1]; } } exclude '**/gen/**' exclude '**/R.java' exclude '**/BuildConfig.java' configFile new File("$rootProject.rootDir/config/checkstyle","checkstyle.xml") classpath = files() } def filterCommitter(String androidbootClassFiles){ ArrayList<String> filterList = new ArrayList<String>(); String [] files = androidbootClassFiles.split("\\n") for(String file : files){ if(file.contains("UCarNew")){ String[] b = file.split("modified: "); filterList.add(b[1]) } } return filterList; } ext { fc = this.&filterCommitter }
這樣就實現了我們需要的功能。
接下來還需要需改checkstyle的配置檔案
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> <!--/** * 參考: checkStyle官方翻譯: https://blog.csdn.net/lzs109/article/details/46008667 */--> <!--檢查器,根配置模組,不可刪除--> <module name="Checker"> <!--編碼--> <property name="charset" value="UTF-8" /> <!--提示級別,預設warning--> <property name="severity" value="warning" /> <!--<property name="severity" value="error" />--> <!--檢查檔案的長度(行) 預設不超過2000行--> <module name="FileLength"> <property name="severity" value="error" /> <property name="max" value="2000" /> </module> <module name="TreeWalker"> <!--必須匯入類的完整路徑,即不能使用*匯入所需的類--> <module name="AvoidStarImport"> <property name="severity" value="error" /> </module> <!--檢查是否匯入了指定的非法包。 預設情況下,這項檢查會拒絕所有的sun.*包,因為直接使用sun.*包的程式肯定不是100%的純Java程式。 想要拒絕其他的包,將illegalPkgs屬性設定為你指定的非法包列表即可。--> <module name="IllegalImport"> <property name="severity" value="error" /> </module> <!--檢查是否匯入了不必顯示匯入的類,闢如java.lang包下的類不需要匯入--> <module name="RedundantImport" /> <!--檢查是否匯入的包沒有使用--> <module name="UnusedImports"> <property name="severity" value="error" /> </module> <!--空格檢查--> <!--<module name="EmptyForIteratorPad" />--> <!--<module name="MethodParamPad" />--> <!--<module name="NoWhitespaceAfter" />--> <!--<module name="NoWhitespaceBefore" />--> <!--<module name="OperatorWrap" />--> <!--<module name="ParenPad" />--> <!--<module name="TypecastParenPad" />--> <!--<module name="WhitespaceAfter" />--> <!--<module name="WhitespaceAround" />--> <!--檢查類和介面的javadoc 預設不檢查author 和version tags authorFormat: 檢查author標籤的格式 versionFormat: 檢查version標籤的格式 scope: 可以檢查的類的範圍,例如:public只能檢查public修飾的類,private可以檢查所有的類 excludeScope: 不能檢查的類的範圍,例如:public,public的類將不被檢查,但訪問許可權小於public的類仍然會檢查,其他的許可權以此類推 tokens: 該屬性適用的型別,例如:CLASS_DEF,INTERFACE_DEF --> <!--<module name="JavadocType">--> <!--<!–<property name="authorFormat" value="\S" />–>--> <!--<!–<property name="scope" value="protected" />–>--> <!--<property name="tokens" value="CLASS_DEF,INTERFACE_DEF" />--> <!--</module>--> <!--檢查方法的javadoc的註釋 scope: 可以檢查的方法的範圍,例如:public只能檢查public修飾的方法,private可以檢查所有的方法 allowMissingParamTags: 是否忽略對引數註釋的檢查 allowMissingThrowsTags: 是否忽略對throws註釋的檢查 allowMissingReturnTag: 是否忽略對return註釋的檢查 --> <module name="JavadocMethod"> <property name="scope" value="private" /> <property name="allowMissingParamTags" value="false" /> <property name="allowMissingThrowsTags" value="false" /> <property name="allowMissingReturnTag" value="false" /> <property name="tokens" value="METHOD_DEF" /> <property name="allowUndeclaredRTE" value="true" /> <property name="allowThrowsTagsForSubclasses" value="true" /> <!--允許get set 方法沒有註釋--> <property name="allowMissingPropertyJavadoc" value="true" /> </module> <!--檢查類變數的註釋 scope: 檢查變數的範圍,例如:public只能檢查public修飾的變數,private可以檢查所有的變數 --> <module name="JavadocVariable"> <property name="scope" value="public" /> </module> <!--檢查左大括號規範 option: 定義左大括號'{'顯示位置,eol在同一行顯示,nl在下一行顯示 maxLineLength: 大括號'{'所在行行最多容納的字元數 tokens: 該屬性適用的型別,例:CLASS_DEF,INTERFACE_DEF,METHOD_DEF,CTOR_DEF --> <module name="LeftCurly"> <property name="option" value="eol" /> <property name="tokens" value="CLASS_DEF,INTERFACE_DEF,METHOD_DEF" /> </module> <!--NeedBraces 檢查是否應該使用括號的地方沒有加括號 tokens: 定義檢查的型別 --> <module name="NeedBraces"> <property name="severity" value="error" /> </module> <!--檢查右大括號'}'規範 option: 右大括號是否單獨一行顯示 tokens: 定義檢查的型別 --> <module name="RightCurly"> <property name="option" value="alone" /> <property name="tokens" value="CLASS_DEF,INTERFACE_DEF,METHOD_DEF" /> </module> <!--檢查在重寫了equals方法後是否重寫了hashCode方法--> <module name="EqualsHashCode" /> <!--檢查是否有不合法的例項化操作,是否使用工廠方法更好。 解釋:根據不同的專案,對於某些類來說,可能通過工廠方法來建立類例項更好,而不是呼叫類構造器。 闢如一個簡單的示例就是java.lang.Boolean類。為了節省記憶體和CPU週期,最好使用預定義的常量TRUE和FALSE。構造器的呼叫應當被替換為呼叫Boolean.valueOf()方法。而非new Boolean();--> <module name="IllegalInstantiation" /> <!--區域性變數名稱final--> <module name="LocalFinalVariableName" /> <!--區域性變數名稱--> <module name="LocalVariableName" /> <!--成員變數名稱static,non-final 規則:純大寫可包含陣列,以_拼接,不可超過30個字元--> <module name="StaticVariableName"> <property name="format" value="(^[A-Z0-9_]{0,30}$)" /> </module> <!--包名稱 規則:純小寫可包含數字--> <module name="PackageName"> <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" /> </module> <!--類,介面名稱 規則:大寫字母開頭,駝峰式,可包含數字,不超過50個字元--> <module name="TypeName"> <property name="format" value="(^[A-Z][a-zA-Z0-9]{0,50}$)" /> </module> <!--方法名稱 規則:小寫字母開頭,駝峰式,可包含數字,不超過50個字元--> <module name="MethodName"> <property name="format" value="(^[a-z][a-zA-Z0-9]{0,50}$)" /> </module> <!--成員變數名稱non-static 規則:以m開頭,駝峰式,可包含數字,不超過50字元--> <module name="MemberName"> <property name="format" value="(^[m][A-Z0-9][a-zA-Z0-9]{0,50}$)" /> </module> <!--方法引數名稱 規則:小寫字母開頭,駝峰式,可包含數字,不超過50字元--> <module name="ParameterName"> <property name="format" value="(^[a-z][a-zA-Z0-9_]{0,50}$)" /> </module> <!--常量名稱(static, final fields)--> <module name="ConstantName"> <property name="format" value="(^[A-Z0-9_]{0,50}$)" /> </module> <!--程式碼縮排--> <module name="Indentation" /> <!--檢查throws子句中是否聲明瞭多餘的異常,例如重複異常、未檢查的異常或一個已宣告丟擲的異常的子類。--> <module name="RedundantThrows"> <property name="logLoadErrors" value="true" /> <property name="suppressLoadErrors" value="true" /> </module> <!--是否出現簡化過於複雜的布林表示式 解釋:複雜的布林邏輯會使得程式碼難以理解和維護。--> <module name="SimplifyBooleanExpression" /> <!--檢查是否有過於複雜的布林型別return語句。例如下面的程式碼: if (valid()) return false; else return true; 可以寫成: return !valid(); 這項檢查是從PMD規則中借鑑而來的。--> <module name="SimplifyBooleanReturn" /> <!--檢查一個只有私有構造器的類是否被宣告為final--> <module name="FinalClass" /> <!--確保工具類(在API中只有靜態方法和欄位的類)沒有任何公有構造器。 解釋:例項化工具類沒有任何意義。因此,工具類的構造器應當是私有的或者受保護的(如果你打算以後擴充套件子類)。一個常見的錯誤便是忘記隱藏預設構造器。 如果你打算將工具類的構造器宣告為受保護的,那麼你可以考慮下面的構造器實現技術,藉此可以禁止子類的例項化: public class StringUtils // 不是final類,允許子類繼承 { protected StringUtils() { throw new UnsupportedOperationException(); // 防止子類呼叫 } public static int count(char c, String s) { // ... } }--> <module name="HideUtilityClassConstructor"> <property name="severity" value="error" /> </module> <!--檢查類成員的可見性。只有static final的類成員可以是公有的,其他的類成員必須是私有的,除非設定了protectedAllowed屬性或packageAllowed屬性。 如果類成員的名稱和指定的公有成員正則表示式匹配,那麼這項檢查就不會標記這個類成員(預設包含“^serialVersionUID$”)。 解釋:強制封裝--> <module name="VisibilityModifier" /> <!-- 每一行只能定義一個變數 --> <module name="MultipleVariableDeclarations"> <property name="severity" value="error" /> </module> <!--檢查再定義陣列時,採用java風格還是c風格,例如:int[] num是java風格,int num[]是c風格。預設是java風格–>--> <module name="ArrayTypeStyle" /> <!--檢查程式碼中是否含有“幻數”,幻數就是沒有被定義為常量的數值文字。預設情況下,-1、0、1、2不會被認為是幻數。 避免硬編碼--> <module name="MagicNumber" /> <!-- A check for TODO: comments. Actually it is a generic regular expression matcher on Java comments. To check for other patterns in Java comments, set property format. 檢查是否存在TODO(待處理) TODO是javaIDE自動生成的。一般程式碼寫完後要去掉。 --> <module name="TodoComment" /> <!--檢查long型別的常量在定義時是否由大寫的“L”開頭。注意,是“L”,不是“l”。 這是由《Java Language Specification》的第3.10.1章節所建議的編碼規約。 解釋:小寫字母“l”看起來非常像數字“1”。--> <module name="UpperEll" /> <!--檢查switch語句是否含有default子句。 解釋:在每個switch語句中引入一條預設分支通常是一個很好的主意。 即使開發者確信所有當前可能的分支都能覆蓋到,這也應當在default分支中表達出來,例如,使用一條斷言。 這種方法使得程式碼可以應付以後的修改,例如,在一個列舉型別中引入新的型別。--> <module name="MissingSwitchDefault"> <property name="severity" value="error" /> </module> <!--檢查switch語句中是否存在跨越分支。如果一個case分支的程式碼中缺少break、return、throw或continue語句,那麼就會導致跨越分支。 這項檢查可以通過特殊的註釋以抑制警告。預設情況下,在有跨越分支的case分支程式碼中新增“fallthru”、“fall through”、“fallthrough”、“falls through”、“fallsthrough”等註釋(區分大小寫)時,便可抑制警告。包含以上單詞的註釋必須在一行中,並且必須在當前case分支程式碼的最後一行中,或者與case語句在同一行,如以下程式碼所示: switch (i){ case 0: i++; // fall through case 1: i++; // falls through case 2: { i++; } // fallthrough case 3: i++; /* fallthru */case 4: i++ break; } 注意:這項檢查假設case分支程式碼中沒有不可達的程式碼。--> <module name="FallThrough"> <property name="severity" value="error" /> </module> <!--檢查方法引數,或者建構函式內參數個數--> <module name="ParameterNumber"> <property name="max" value="5" /> </module> <!--每行字元數--> <module name="LineLength"> <property name="max" value="200" /> </module> <!--檢查方法和構造器的長度。 解釋:如果一個方法變得非常長,那麼就會難以理解。因此,過長的方法通常應當被重構成若干個較小的獨立的方法,每個方法專注於一個特定的任務。--> <module name="MethodLength"> <property name="max" value="300" /> </module> <!--ModifierOrder 檢查修飾符的順序,預設是 public,protected,private,abstract,static,final,transient,volatile,synchronized,native--> <module name="ModifierOrder" /> <!--在以下部分檢查是否有多餘的修飾符: 1. 介面和註解的定義; 2. final類的方法的final修飾符; 3. 被宣告為static的內部介面宣告。 解釋:《Java Language Specification》強烈不建議在介面定義中使用“public”和“abstract”來宣告方法。 介面中的變數和註解預設就是public、static、final的,因此,這些修飾符也是多餘的。 因為註解是介面的一種形式,所以它們的欄位預設也是public、static、final的,正如它們的註解欄位預設是public和abstract的。 定義為final的類是不能被繼承的,因此,final類的方法的final修飾符也是多餘的。--> <module name="RedundantModifier" /> <!---字串比較必須使用 equals()--> <module name="StringLiteralEquality" /> <!--if-else巢狀語句個數 最多4層--> <module name="NestedIfDepth"> <property name="max" value="4" /> </module> <!--try-catch 巢狀語句個數 最多2層--> <module name="NestedTryDepth"> <property name="max" value="2" /> </module> <!--限制return語句的數量。預設值為2。可以忽略檢查指定的方法(預設忽略equals()方法)。 解釋:過多的返回點可能表明程式碼嘗試處理過多的業務,可能會難以理解。--> <module name="ReturnCount"> <property name="max" value="5" /> <property name="format" value="^$" /> </module> </module> </module>
到這裡就完成了整個配置了,可以根據自己的需求來配置不同的選項了
最後再說一下如何使用
每次git commit 或者通過android studio commit程式碼的時候,都會先對修改的java檔案進行檢查,如果有問題,
則會在build/reports/checkstyle/checkstyle.xml中將錯誤列舉出來。
以這一條舉例說明:
39行第9列 錯誤,宣告變數順序錯誤,進行的是
com.puppycrawl.tools.checkstyle.checks.coding.DeclarationOrderCheck
檢查。
具體錯誤原因可以參考下面表格
錯誤提示 | 錯誤說明 |
missing a javadoc comment | 缺少類註釋 |
Line longer than X characters | 行長度超過X個字元(包括空格) |
Return count is X(max allowed 3) | 一個方法內的返回數量是X(最大值只能為3) |
Nested if-else depth is X(max allowed is 3) | 最大的if-else巢狀層數為X(最大隻能為3) |
Array brackets at illegal position | 陣列的方括號“[]”的位置不正確(檢查陣列型別的定義是String[] args,而不是String args[]) |
Line matchs the illegal pattern 'System\.out\.println' | 本行包含System.out.println語句 |
ctor def modifier at indentation level 8 not at corrent indentation 4 | 縮排不正確,一般是因為沒有在Eclipse中使用4個空格代替tab鍵引起。 |
'static' modifier out of order with the JLS suggestions | static修飾符沒有按照JLS的建議來排序(eg.寫成public final static...應該改成public static final) |
Name 'X' must match pattern '^[A-Z][A-Z0-9][_A-Z0-9+]$'(正則表示式) | 名稱不符合正則表示式'^[A-Z][A-Z0-9][_A-Z0-9+]$'(即為大寫字母,數字、下劃線等)。 一般在靜態變數沒有大寫時提示,包名不是全部訊息時提示,類名不是大寫開頭時提示,方法名不是小寫開頭時提示 |
Variable access definition in wrong order | 變數定義順序不正確(例如在類成員變數定義時,將private型別的變數定義在public型別的變數之前) |
Static variable definition in wrong order | 靜態變數定義順序不正確(例如在建構函式之後定義靜態變數) |
Instance variable definition in wrong order | 成員變數定義順序不正確(例如在建構函式之後定義成員變數) |
X is a magic number | X是一個魔術數字(非0、1、2的數字) |
if construct must use '{}' | if結構必須使用'{}' |
Got an exception - Unexpected character 0xfffd in identifier | 因為沒有設定checkstyle配置檔案的charset為UTF-8,而類檔案使用UTF-8編碼,並且含有中文 |
“{” should be on the previous line | “{” 應該位於前一行 |
Methods is missing a javadoc comment | 方法前面缺少javadoc註釋 |
Expected @throws tag for “Exception” | 在註釋中希望有@throws的說明 |
“.” Is preceeded with whitespace | “.” 前面不能有空格 |
“.” Is followed by whitespace | “.” 後面不能有空格 |
“=” is not preceeded with whitespace“=” | 前面缺少空格 |
“=” is not followed with whitespace | “=” 後面缺少空格 |
“}” should be on the same line | “}” 應該與下條語句位於同一行 |
Unused @param tag for “unused” | 沒有引數“unused”,不需註釋 |
Variable “X” missing javadoc | 變數“CA”缺少javadoc註釋 |
Line contains a tab character | 行含有”tab” 字元 |
Redundant “Public” modifier | 冗餘的“public” modifier |
final modifier out of order with the JSL suggestion | final修飾符的順序錯誤 |
Avoid using the “.*” form of import | Import格式避免使用“.*” |
Redundant import from the same package | 從同一個包中Import內容 |
Unused import-X Import | import的X類沒有被使用 |
Duplicate import to line X | 重複Import同一個內容 |
Import from illegal package | 從非法包中 Import內容 |
“while” construct must use “{}” | “while” 語句缺少“{}” |
Variable “X” must be private and have accessor method | 變數“X”應該是private的,並且有呼叫它的方法 |
Variable “X” must match pattern “^[a-z][a-zA-Z0-9]*$” | 變數“X”不符合命名規則“^[a-z][a-zA-Z0-9]*$” |
“(” is followed by whitespace | “(” 後面不能有空格 |
“)” is proceeded by whitespace | “)” 前面不能有空格 |