1. 程式人生 > >Android git commit之前進行checkStyle檢查

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

“)” 前面不能有空格