概要
Gradle では静的解析ツールを使うためのプラグインが標準で提供されています。それらを Gradle プロジェクトに導入する方法を説明します。
間違っている部分があったらご指摘か編集リクエストをくださいますと幸いです。
記事の背景
『知識ゼロから学ぶソフトウェアテスト【改訂版】』という本を読んでいたら、ふと自分のプロジェクトの Cyclomatic Complexity (循環的複雑度)を計測したくなりました。
なぜ Gradle なのか
Java だと Eclipse にプラグインがあるのですが、Eclipse はコードを書くことだけに使いたいと個人的に思っておりまして、ついでに、Gradle プラグインであれば環境の再現が容易だと考え、Gradle から利用できないかと思って調べてみたら、PMD プラグインが標準で用意されていることを知りました。このプラグインは日本語の解説記事も結構多く、それを参考に進めたら特に問題なく計測ができました。
別に既存の記事があるなら、そのリンクを記載すればよくて、わざわざ記事を書かなくてもよいのではないか
今回はほかの静的解析ツールを使うプラグインも併せて、簡単な使い方を紹介します。
静的解析とは
プログラムを実行せずに解析する手法をそう呼ぶそうです。Java の場合、ソースコードやバイトコードをツールで解析させます。機械が自動的に実行することで、人の目では見つけにくい不具合や不整合を見つけ出し、将来埋め込まれうるバグを未然に防いだり、可読性の高いコードに書き換えたりする手助けとできます。
実行環境
Product | Version |
---|---|
Java SE | 1.8.0_111 |
OS | Windows 10 |
Gradle | 3.2.1 |
対象プロジェクト
以前作成した RatPack のサンプルコード を使います。
src
├─main
│ ├─java
│ │ └─jp
│ │ └─toastkid
│ │ ├─libs
│ │ │ └─tinysegmenter
│ │ │ CharacterClassifier.java
│ │ │ ScoreMap.java
│ │ │ TinySegmenter.java
│ │ │
│ │ └─ratpack
│ │ │ Main.java
│ │ │
│ │ └─models
│ │ WordCloud.java
│ │
│ └─resources
│ │ .ratpack
│ │ wordcloud.html
│ │
│ └─public
│ ├─images
│ │ favicon.png
│ │ icon.png
│ │
│ ├─javascripts
│ │ │ jquery-1.6.4.min.js
│ │ │
│ │ ├─d3
│ │ │ d3.js
│ │ │ LICENSE(d3)
│ │ │
│ │ └─d3-cloud
│ │ d3.layout.cloud.js
│ │ index.js
│ │ LICENSE
│ │ package.json
│ │ README.md
│ │
│ └─stylesheets
│ main.css
│
└─test
└─java
└─jp
└─toastkid
└─ratpack
└─models
WordCloudTest.java
PMD
プログラミングの基本的な指標から、問題のあるコードを指摘してくれる静的解析ツールです。
導入
プラグイン追加を指定するだけで使えます。
apply plugin: 'pmd'
実行
$ gradle pmdMain
を実行してください。
$ gradle pmdMain
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:pmdMain
BUILD SUCCESSFUL
Total time: 2.938 secs
レポートファイルは build/reports/pmd
に HTML と XML 形式で出力されます。
rule 追加
デフォルトで使われる basic ではチェック項目が少ないので、少し追加してみましょう。PMDでは下記リンク先に掲載されている rule を使うことができます。
Cyclomatic Complexity は CodeSize で計測できます。今回は Braces と CodeSize を追加してみましょう。
なお、rule を指定する際は java-
という prefix が必要でした。
pmd {
ruleSets = [
'java-braces',
'java-codesize',
]
}
実行
$ gradle pmdMain
を実行してください。
$ gradle pmdMain
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:pmdMain FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':pmdMain'.
> 10 PMD rule violations were found. See the report at: file:///C:/Users/Toast%20kid/Documents/workspace/ratpack_word_cloud-master/build/reports/pmd/main.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 2.266 secs
指摘数が一定以上あるため、ビルドが強制的に失敗しました。
指摘事項
project\src\main\java\jp\toastkid\libs\tinysegmenter\ScoreMap.java 1492 The constructor 'ScoreMap' has a Cyclomatic Complexity of 43.
「ScoreMap.java のコンストラクタは Cyclomatic Complexity が43もある」と指摘されました。
この値は低ければ低いほどコードが簡潔であることを表しており、20を超えると要注意、50を超える場合は相当複雑とのことです。CodeSize のデフォルトでは10以上で Warning が出ます。
Cyclomatic Complexity の計算式
C = プログラムのルート数 - プログラムの分岐点数 + 2
指摘が多すぎる場合でもビルドを失敗扱いにしない
それが良いことだとは必ずしも言えませんが、そういう場合は ignoreFailures = true
を追加すればビルドが成功します。
build.gradle に追記
pmd {
ignoreFailures = true
ruleSets = [
……
}
実行
$ gradle pmdMain
を実行してください。
$ gradle pmdMain
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:pmdMain
10 PMD rule violations were found. See the report at: file:///C:/Users/Toast%20kid/Documents/workspace/ratpack_word_cloud-master/build/reports/pmd/main.html
BUILD SUCCESSFUL
Total time: 2.25 secs
参考
FindBugs
Java のコードからバグの温床になりそうな部分を指摘してくれる静的解析ツールです。
導入
build.gradle に追記します。
apply plugin: 'findbugs'
実行
$ gradle findbugsMain
コマンドで解析を実行します。
$ gradle findbugsMain
:compileJava
:processResources
:classes
:findbugsMain
Download https://repo1.maven.org/maven2/com/google/code/findbugs/findbugs/3.0.1/findbugs-3.0.1.pom
...(中略)...
Download https://repo1.maven.org/maven2/org/ow2/asm/asm/5.0.2/asm-5.0.2.jar
:findbugsMain
FindBugs rule violations were found. See the report at: projet/build/reports/findbugs/main.xml
レポート
解析結果はXMLで出力されます。HTML で見たいのですがオプションが見当たりませんでした。
<BugInstance type="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" priority="2" rank="13" abbrev="NP" category="STYLE">
<Class classname="jp.toastkid.jfx.common.Style">
<SourceLine classname="jp.toastkid.jfx.common.Style" start="36" end="137" sourcefile="Style.java" sourcepath="jp/toastkid/jfx/common/Style.java"/>
</Class>
<Method classname="jp.toastkid.jfx.common.Style" name="findJarResourceDir" signature="()Ljava/util/List;" isStatic="true">
<SourceLine classname="jp.toastkid.jfx.common.Style" start="117" end="137" startBytecode="0" endBytecode="633" sourcefile="Style.java" sourcepath="jp/toastkid/jfx/common/Style.java"/>
</Method>
<LocalVariable name="?" register="-1" pc="219" role="LOCAL_VARIABLE_UNKNOWN"/>
<SourceLine classname="jp.toastkid.jfx.common.Style" start="133" end="133" startBytecode="219" endBytecode="219" sourcefile="Style.java" sourcepath="jp/toastkid/jfx/common/Style.java" role="SOURCE_LINE_INVOKED"/>
<SourceLine classname="jp.toastkid.jfx.common.Style" start="133" end="133" startBytecode="216" endBytecode="216" sourcefile="Style.java" sourcepath="jp/toastkid/jfx/common/Style.java" role="SOURCE_LINE_KNOWN_NULL"/>
<Property name="edu.umd.cs.findbugs.detect.NullDerefProperty.DEREFS_ARE_CLONED" value="true"/>
</BugInstance>
指摘が多すぎる場合でもビルドを失敗扱いにしない
このプラグインも ignoreFailures = true
を追加すれば、どれだけ warning が出ていてもビルドが失敗扱いになりません。
findbugs {
ignoreFailures = true
参考
Checkstyle
Checkstyle は Java のソースコードがコーディング規約に則っていることを確認するためのツールです。
導入
build.gradle に下記を1行追加します。
apply plugin: 'checkstyle'
実行(失敗)
$ gradle check
でチェックを実行します。このプラグイン単独でのチェックをする場合は $ gradle checkstyleMain
で可能です。
$ gradle check
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:checkstyleMain
Download https://repo1.maven.org/maven2/com/puppycrawl/tools/checkstyle/5.9/checkstyle-5.9.pom
……(中略)……
Download https://repo1.maven.org/maven2/com/google/guava/guava-jdk5/14.0.1/guava-jdk5-14.0.1.jar
:checkstyleMain FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':checkstyleMain'.
> Unable to create a Checker: unable to find project\config\checkstyle\checkstyle.xml
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 7.326 secs
設定ファイルの追加
何もない状態でチェックを実行すると上記のように失敗します。checkstyleの設定ファイルを用意する必要があります。
というわけで、下記から Sun の規約を再現した設定ファイルを取得します。
プロジェクトのルートに config/checkstyle
フォルダを追加して、そこに checkstyle.xml
を置いてください。
再度実行(失敗)
先ほどとコマンドは同じです。
$ gradle check
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:checkstyleMain FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':checkstyleMain'.
> Unable to create a Checker: Property 'fileExtensions' in module Checker does not exist, please check the documentation
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 1.782 secs
fileExtensions が存在しないと言われています。
コメントアウトして実行
当該箇所をコメントアウトして実行してみます。
$ gradle check
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:checkstyleMain
[ant:checkstyle] project\src\main\java\jp\toastkid\libs\tinysegmenter\CharacterClassifier.java:0: Missing package-info.java file.
[ant:checkstyle] project\src\main\java\jp\toastkid\libs\tinysegmenter\CharacterClassifier.java:0: ファイルが新しい行で終了していません。
……(あまりに指摘が多すぎるので省略)……
[ant:checkstyle] project\src\main\java\jp\toastkid\ratpack\models\WordCloud.java:79:58: 'str' には @param タグが必要です。
:checkstyleMain FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: project/build/reports/checkstyle/main.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 3.639 secs
いやあ、今回用意したのが実にひどいコードでしたね……このプラグインでも ignoreFailures
が false の場合は、warning が出ているとビルドを失敗させます。レポートは生成されていますので見てみましょう。
レポート
レポートファイルは build/reports/checkstyle/
以下に、html と xml で生成されます。
指摘が多すぎる場合でもビルドを失敗扱いにしない
このプラグインも ignoreFailures = true
を追加すれば、どれだけ warning が出ていてもビルドが失敗扱いになりません。
checkstyle {
ignoreFailures = true
参考
JaCoCo
テストレポートを出力するプラグインです。テストカバレッジを計測して視覚的に参照できます。名前は "Java Code Coverage" のアクロニムのようです。
導入
build.gradle に JaCoCo プラグインを追加します。
apply plugin: "jacoco"
実行
テストのビルドが実行されている必要があります。今回は $ gradle test jacocoTestReport
で実行します。
$ gradle test jacocoTestReport
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:jacocoTestReport
BUILD SUCCESSFUL
Total time: 3.448 secs
レポート
デフォルトでは build/reports/jacoco/test/html/index.html
に HTML 形式でテストレポートが生成されています。
packages
classes
methods
packages -> classes -> methods の順に細かくなっていきます。
詳細
メソッド名を選択するとクラス内でどの部分がテストコードから呼び出されたかを緑背景、どこを通らなかったかを赤背景で示してくれます。非常にわかりやすいですね。
参考
JDpend
package 単位での静的解析をするツールのようです。
導入
apply plugin: 'jdepend'
実行
$ gradle jdependMain
で実行します。
$ gradle jdependMain
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jdependMain
Download https://repo1.maven.org/maven2/jdepend/jdepend/2.9.1/jdepend-2.9.1.pom
Download https://repo1.maven.org/maven2/org/apache/ant/ant-jdepend/1.9.6/ant-jdepend-1.9.6.pom
Download https://repo1.maven.org/maven2/org/apache/ant/ant-parent/1.9.6/ant-parent-1.9.6.pom
Download https://repo1.maven.org/maven2/jdepend/jdepend/2.9.1/jdepend-2.9.1.jar
Download https://repo1.maven.org/maven2/org/apache/ant/ant-jdepend/1.9.6/ant-jdepend-1.9.6.jar
[ant:jdependreport] ependMain
[ant:jdependreport] Unknown constant: 18
BUILD SUCCESSFUL
Total time: 3.719 secs
レポート
レポートファイルは build/reports/jdepend
に XML 形式で生成されます。
指摘が多すぎる場合でもビルドを失敗扱いにしない
このプラグインも ignoreFailures = true
を追加すれば、どれだけ warning が出ていてもビルドが失敗扱いになりません。
JDepend {
ignoreFailures = true
参考
プロジェクトレポートプラグイン
静的解析ツールとは関係ありませんが、こういうプラグインもあるらしいと知ったので、ついでに試してみます。
導入
build.gradle にプラグインを追記します。
apply plugin: 'project-report'
実行
$ gradle projectReport
ですべてのレポートを生成します。
$ gradle projectReport
:dependencyReport
:htmlDependencyReport
:propertyReport
:taskReport
:projectReport
BUILD SUCCESSFUL
Total time: 1.472 secs
レポート
build/reports/project/dependencies/root.html
に出力されます。表示には JavaScript の実行許可が必要です。
アプリケーションのコンパイル依存だけでなくプラグインの依存も表示してくれます。テキスト形式が良い場合は build/reports/project/dependencies.txt
に生成されていますので、そちらを参照するとよいでしょう。
参考
License Gradle Plugin
標準のプラグインではありませんが、導入が容易で役に立つと思いましたので、ついでに書いておきます。これはプロジェクトのライセンスを依存まで辿ってレポートに出力してくれるプラグインです。
会社によってはOSSライブラリの利用前や成果物リリース前にライセンスをチェックする必要があるかもしれませんが、そういう時に逐一 pom.xml を辿るのが苦痛な方は、このプラグインの導入を検討してみてもよいかもしれません。
導入
gradleでdependenciesのライセンス一覧を出力する を参考にすれば、すぐ導入できます。
具体的には、 build.gradle に下記の通り追記すれば準備は完了です。
apply plugin: "com.github.hierynomus.license"
......
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.nl.javadude.gradle.plugins:license-gradle-plugin:0.13.1"
}
}
実行
$ gradle downloadLicenses
を実行してください。
$ gradle downloadLicenses
:downloadLicenses
BUILD SUCCESSFUL
Total time: 10.753 secs
レポート
build/reports/license/
フォルダに2種類の HTML と XML、合わせて4ファイルが生成されています。
ファイル名 | 説明 |
---|---|
license-dependency | ライセンス単位でのまとめ |
dependency-license | 依存単位でのまとめ |
license-dependency
dependency-license
GPL 系のライセンスが入っていないかをチェックするなら license-dependency
が役立ちますし、個々のライセンスを調べる必要がある場合は dependency-license
がよいでしょう。
参考
まとめ
Gradle プラグインから簡単に用いることのできる静的解析ツールの使い方を紹介しました。こうしたツールは毎回コマンドを打って実行するのではなく、 CI を通して定期的に自動実行していくことが重要です。
サンプル
今回生成されたレポートのフォルダ階層
build
│
└─reports
├─jacoco
│ └─test
│ └─html
│ │ index.html
│ │ jacoco-sessions.html
│ │
│ ├─jacoco-resources
│ │ branchfc.gif
│ │ branchnc.gif
│ │ branchpc.gif
│ │ bundle.gif
│ │ class.gif
│ │ down.gif
│ │ greenbar.gif
│ │ group.gif
│ │ method.gif
│ │ package.gif
│ │ prettify.css
│ │ prettify.js
│ │ redbar.gif
│ │ report.css
│ │ report.gif
│ │ session.gif
│ │ sort.gif
│ │ sort.js
│ │ source.gif
│ │ up.gif
│ │
│ ├─jp.toastkid.libs.tinysegmenter
│ │ CharacterClassifier.html
│ │ CharacterClassifier.java.html
│ │ index.html
│ │ index.source.html
│ │ ScoreMap.html
│ │ ScoreMap.java.html
│ │ TinySegmenter.html
│ │ TinySegmenter.java.html
│ │
│ ├─jp.toastkid.ratpack
│ │ index.html
│ │ index.source.html
│ │ Main.html
│ │ Main.java.html
│ │
│ └─jp.toastkid.ratpack.models
│ index.html
│ index.source.html
│ WordCloud.html
│ WordCloud.java.html
│
├─license
│ dependency-license.html
│ dependency-license.xml
│ license-dependency.html
│ license-dependency.xml
│
├─pmd
│ main.html
│ main.xml
│
├─project
│ │ dependencies.txt
│ │ properties.txt
│ │ tasks.txt
│ │
│ └─dependencies
│ │ index.html
│ │ root.html
│ │ root.js
│ │
│ ├─css
│ │ base-style.css
│ │ style.css
│ │ tree.css
│ │
│ ├─images
│ │ d.gif
│ │ d.png
│ │ throbber.gif
│ │
│ └─js
│ jquery.jstree.js
│ jquery.min-1.11.0.js
│ script.js
│
└─tests
└─test
│ index.html
│
├─classes
│ jp.toastkid.ratpack.models.WordCloudTest.html
│
├─css
│ base-style.css
│ style.css
│
├─js
│ report.js
│
└─packages
jp.toastkid.ratpack.models.html