LoginSignup
29
40

More than 5 years have passed since last update.

Gradle プロジェクトで静的解析ツールを使う

Posted at

概要

Gradle では静的解析ツールを使うためのプラグインが標準で提供されています。それらを Gradle プロジェクトに導入する方法を説明します。

間違っている部分があったらご指摘か編集リクエストをくださいますと幸いです。 :bow:

記事の背景

『知識ゼロから学ぶソフトウェアテスト【改訂版】』という本を読んでいたら、ふと自分のプロジェクトの Cyclomatic Complexity (循環的複雑度)を計測したくなりました。

:question: なぜ Gradle なのか

Java だと Eclipse にプラグインがあるのですが、Eclipse はコードを書くことだけに使いたいと個人的に思っておりまして、ついでに、Gradle プラグインであれば環境の再現が容易だと考え、Gradle から利用できないかと思って調べてみたら、PMD プラグインが標準で用意されていることを知りました。このプラグインは日本語の解説記事も結構多く、それを参考に進めたら特に問題なく計測ができました。

:do_not_litter: 別に既存の記事があるなら、そのリンクを記載すればよくて、わざわざ記事を書かなくてもよいのではないか

今回はほかの静的解析ツールを使うプラグインも併せて、簡単な使い方を紹介します。

静的解析とは

プログラムを実行せずに解析する手法をそう呼ぶそうです。Java の場合、ソースコードやバイトコードをツールで解析させます。機械が自動的に実行することで、人の目では見つけにくい不具合や不整合を見つけ出し、将来埋め込まれうるバグを未然に防いだり、可読性の高いコードに書き換えたりする手助けとできます。

実行環境

Product Version
Java SE 1.8.0_111
OS Windows 10
Gradle 3.2.1

対象プロジェクト

以前作成した RatPack のサンプルコード を使います。

フォルダ階層(tree /f)
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

プログラミングの基本的な指標から、問題のあるコードを指摘してくれる静的解析ツールです。

導入

プラグイン追加を指定するだけで使えます。

build.gradle
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 形式で出力されます。

pmd1.png

rule 追加

デフォルトで使われる basic ではチェック項目が少ないので、少し追加してみましょう。PMDでは下記リンク先に掲載されている rule を使うことができます。

Cyclomatic Complexity は CodeSize で計測できます。今回は Braces と CodeSize を追加してみましょう。
なお、rule を指定する際は java-という prefix が必要でした。

build.gradle
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

指摘数が一定以上あるため、ビルドが強制的に失敗しました。

指摘事項

pmd2.png

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

:warning: 指摘が多すぎる場合でもビルドを失敗扱いにしない

それが良いことだとは必ずしも言えませんが、そういう場合は ignoreFailures = true を追加すればビルドが成功します。

build.gradle に追記

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 に追記します。

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 で見たいのですがオプションが見当たりませんでした。

Sample
  <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>

:warning: 指摘が多すぎる場合でもビルドを失敗扱いにしない

このプラグインも ignoreFailures = true を追加すれば、どれだけ warning が出ていてもビルドが失敗扱いになりません。

build.gradle
findbugs {
  ignoreFailures = true

参考


Checkstyle

Checkstyle は Java のソースコードがコーディング規約に則っていることを確認するためのツールです。

導入

build.gradle に下記を1行追加します。

build.gradle
apply plugin: 'checkstyle'

:no_entry: 実行(失敗)

$ 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 を置いてください。

:no_entry: 再度実行(失敗)

先ほどとコマンドは同じです。

実行
$ 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 で生成されます。

cs.png

:warning: 指摘が多すぎる場合でもビルドを失敗扱いにしない

このプラグインも ignoreFailures = true を追加すれば、どれだけ warning が出ていてもビルドが失敗扱いになりません。

build.gradle
checkstyle {
  ignoreFailures = true

参考


JaCoCo

テストレポートを出力するプラグインです。テストカバレッジを計測して視覚的に参照できます。名前は "Java Code Coverage" のアクロニムのようです。

導入

build.gradle に JaCoCo プラグインを追加します。

build.gradle
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

jacoco.png

classes

jacoco2.png

methods

jacoco3.png

packages -> classes -> methods の順に細かくなっていきます。

詳細

jacoco4.png

メソッド名を選択するとクラス内でどの部分がテストコードから呼び出されたかを緑背景、どこを通らなかったかを赤背景で示してくれます。非常にわかりやすいですね。

参考


JDpend

package 単位での静的解析をするツールのようです。

導入

build.gradle
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 が出ていてもビルドが失敗扱いになりません。

build.gradle
JDepend {
  ignoreFailures = true

参考


プロジェクトレポートプラグイン

静的解析ツールとは関係ありませんが、こういうプラグインもあるらしいと知ったので、ついでに試してみます。

導入

build.gradle にプラグインを追記します。

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 の実行許可が必要です。

projectReport.png

アプリケーションのコンパイル依存だけでなくプラグインの依存も表示してくれます。テキスト形式が良い場合は build/reports/project/dependencies.txtに生成されていますので、そちらを参照するとよいでしょう。

参考


License Gradle Plugin

標準のプラグインではありませんが、導入が容易で役に立つと思いましたので、ついでに書いておきます。これはプロジェクトのライセンスを依存まで辿ってレポートに出力してくれるプラグインです。
会社によってはOSSライブラリの利用前や成果物リリース前にライセンスをチェックする必要があるかもしれませんが、そういう時に逐一 pom.xml を辿るのが苦痛な方は、このプラグインの導入を検討してみてもよいかもしれません。

導入

gradleでdependenciesのライセンス一覧を出力する を参考にすれば、すぐ導入できます。

具体的には、 build.gradle に下記の通り追記すれば準備は完了です。

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

license2.png

dependency-license

license1.png

GPL 系のライセンスが入っていないかをチェックするなら license-dependency が役立ちますし、個々のライセンスを調べる必要がある場合は dependency-license がよいでしょう。

参考


まとめ

Gradle プラグインから簡単に用いることのできる静的解析ツールの使い方を紹介しました。こうしたツールは毎回コマンドを打って実行するのではなく、 CI を通して定期的に自動実行していくことが重要です。

サンプル

今回生成されたレポートのフォルダ階層

フォルダ階層(tree /f)
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
29
40
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
40