2016/02/23

Android: よく使う静的解析ツール他のGradle定義

Androidプロジェクトで頻繁に使用されるプラグインの導入ソースプログラムをプロジェクト作成の都度書き直すのは非効率的であるため,
そういった繰り返し書かれるプログラムを下記にまとめ, 新規プロジェクト作成の際にはこれを適用することで対応できるようにする.

GitHub-AndroidProjectTemplate

導入されるプラグイン

主要な静的解析ツールと各種ユーティリティが導入される.
プラグインの導入ソースコード(gralde)は/android.gradleで定義されている.
これに含まれるプラグインは次の通り.

FindBugs
Javaプログラム(バイトコード)の静的解析ツール.
PMD
Javaプログラム(ソースコード)の静的解析ツール.
CheckStyle
プログラムの体裁チェックツール.
Lint
Androidに特化した潜在的な不具合を検出する静的解析ツール.
Jacoco
Javaプログラムのカバレッジレポート.
DexCount
APKが持つメソッド数を報告するプラグイン.
ApkSize
APKのバイナリサイズを報告するプラグイン.
GradleVersion
依存しているライブラリの最新バージョンをチェックするプラグイン.

ライブラリ

  • RxAndroid
  • RxJava
  • Dagger2
  • ButterKnife
  • Orma
  • Timber
  • Stetho
  • Okhttp
  • Robolectric
  • JUnit
  • Mockito
  • Hamcrest
  • Android support Appcompat-v7
  • Android support annotations
  • Android design support lib.
  • Android support testing lib.

android.gradleの適用

android.gradleをアプリケーションに適用するには2ステップ必要.
まずプロジェクトルートで下記を宣言する.

buildscript {
    dependencies {
        ...

        // dex method count
        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.4.1'

        // apk size
        classpath 'com.vanniktech:gradle-android-apk-size-plugin:0.2.0'

        // check for plugin updates
        classpath 'com.github.ben-manes:gradle-versions-plugin:0.11.3'
    }
}

次に, 各モジュールのbuild.gradleandroid.gradleプラグインを読み込む.

apply from: rootProject.file('android.gradle')

追加・変更されるタスク

それぞれのタスクは各バリアント毎に用意される.

/*
 * 全ビルドバリアントに共通のタスクを設定する
 */
(android.hasProperty('applicationVariants')
        ? android.'applicationVariants'
        : android.'libraryVariants').all { variant ->

また, 各静的解析ツールはデフォルトで/configに格納されたコンフィギュレーションファイルを読み込む.

findbugs{variantName}
FindBugsによる静的解析を指定のビルドバリアントに対して実行する
pmd{variantName}
PMDによる静的解析を指定のビルドバリアントに対して実行する
checkstyle{variantName}
CheckStyleによる体裁チェックを指定のビルドバリアントに対して実行する
lint{variantName}
Lintによる静的解析を指定のビルドバリアントに対して実行する
jacoco{variantName}Report
Jacocoによるカバレッジレポートを指定のビルドバリアントに対して実行する
count{variantName}Methods
DexCountによるメソッド数の計測を指定のビルドバリアントに対して実行する
size{variantName}
ApkSizeによるAPKサイズの計測を指定のビルドバリアントに対して実行する
dependencyUpdates
依存しているライブラリの最新バージョンチェックを実行する
check
デバッガブルなビルドタイプの場合, いくつかの静的解析チェックを追加で行う
pullCodeStyleSettings
AndroidStudioに適用されるコードスタイル設定ファイルをダウンロードする
checkEnvironmentSettings
開発環境の設定確認用タスク

FindBugs

FindBugsの設定ファイルは/config/findbugs.xmlに格納される.

/*
 * Findbugs
 *   see: https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.FindBugsExtension.html
 */
task("findbugs$variantName", type: FindBugs,
        dependsOn: "assemble$variantName") {
    group 'Reporting'
    description "Generate ${variantName} Findbugs reports."

    ignoreFailures = true
    reports {
        xml.enabled = false
        html.enabled = true
    }

    effort = 'max'
    reportLevel = 'low'
    source = files(android.sourceSets.main.java.srcDirs)
    classes = fileTree(dir: variant.javaCompiler.destinationDir,
            excludes: autoGenerated)
    classpath = files(configurations.compile.files)
    excludeFilter = rootProject.file('config/findbugs.xml')
}

PMD

PMDの設定ファイルは/config/pmd.xmlに格納される.

/*
 * PMD
 *   see: https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html
 */
task("pmd$variantName", type: Pmd, dependsOn: "assemble$variantName") {
    group 'Reporting'
    description "Generate ${variantName} Pmd reports."

    ignoreFailures = true
    reports {
        xml.enabled = true
        html.enabled = true
    }

    ruleSetFiles = files("${rootProject.rootDir}/config/pmd.xml")
    ruleSets = []
    source = files(variant.javaCompiler.source)
    classpath = files(configurations.compile.files)
}

CheckStyle

CheckStyleの設定ファイルは/config/checkstyle-*.xmlに格納される.
checkstyle-easy.xmlは緩い体裁チェックルール. checkstyle-hard.xmlは厳しい体裁チェックルールとなっている.
プロジェクトのコーディング規約にあったファイルをandroid.gradleで指定する.

/*
 * CheckStyle
 *   see: https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.Checkstyle.html
 */
task("checkstyle$variantName", type: Checkstyle,
        dependsOn: "assemble$variantName") {
    group 'Reporting'
    description "Generate ${variantName} Checkstyle reports."

    ignoreFailures = true
    reports {
        xml.enabled = true
        html.enabled = true
    }

    showViolations true  // CheckStyle解析結果をStdOutに出力する
    configFile = rootProject.file('config/checkstyle-easy.xml')
    source = files(android.sourceSets.main.java.srcDirs)
    classpath = files(configurations.compile.files)
}

Lint

Lintの設定ファイルは/config/lint.xmlに格納される.

// Lintの設定
//   Lint設定ファイルはプロジェクトルートのconfigフォルダに配置すること.
//   see: http://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.LintOptions.html
lintOptions {
    lintConfig rootProject.file('config/lint.xml') // Lintチェックの無効化設定.
    textReport true
    textOutput 'stdout'  // StdOutにLint結果を出力する
    htmlReport true
    htmlOutput file("${buildDir}/reports/lint/lint_result.html")
    xmlReport false
    xmlOutput file("${buildDir}/reports/lint/lint_result.xml")
    checkAllWarnings true
    checkReleaseBuilds true
    warningsAsErrors true  // Warnレベルの警告をErrorと同様に扱う
    abortOnError true      // Errorが見つかった場合にビルドを失敗させる
}

Jacoco

/*
 * Jacoco
 *   see: https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoReport.html
 */
task("jacoco${variantName}Report", type: JacocoReport,
        dependsOn: "test${variantName}UnitTest") {
    group 'Reporting'
    description "Generate ${variantName} Jacoco coverage reports."

    reports {
        xml.enabled = true
        html.enabled = true
    }

    sourceDirectories = files(android.sourceSets.main.java.srcDirs)
    executionData =
            files("${buildDir}/jacoco/test${variantName}UnitTest.exec")
    classDirectories = fileTree(dir: variant.javaCompiler.destinationDir,
            excludes: autoGenerated)
}

コンフィギュレーション

Release署名

APKのDebug/Release署名設定もandroid.gradleで定義されている.
Release署名で使用されるキーストア情報は/secretに格納されているrelease.gradleにある(secretフォルダについては後述).
android.gradle/secret/release.gradleを参照し, これを適用する.
もしrelease.gradleが見つからない場合はDebug署名の内容がRelease署名として流用される.

def releaseSettingGradleFile = rootProject.file('secret/release.gradle')
if (releaseSettingGradleFile.exists()) {
    apply from: releaseSettingGradleFile, to: android
} else {
    println "\n\t!! NOT FOUND RELEASE KEYSTORE SETTING. SIGNING DEBUG KEYSTORE !!\n"
    release {
        storeFile = debug.storeFile
        storePassword = debug.storePassword
        keyAlias = debug.keyAlias
        keyPassword = debug.keyPassword
    }
}

Debug署名はIDE標準で用意されるdebug.keystoreをプロジェクトルートに配置することで利用できる.

コードスタイル設定

AndroidStudioで使用するコードスタイル設定が/.idea/codeStyleSettings.xmlに定義されている.
android.gradlepullCodeStyleSettingsタスクを実行することで下記のシェルスクリプトが実行され, AndroidStudioのコードスタイル設定が更新される.

curl -L "https://raw.githubusercontent.com/YukiMatsumura/AndroidProjectTemplate/master/.idea/codeStyleSettings.xml" > .idea/codeStyleSettings.xml

コードスタイルを適用するにはIDEを再起動すること.

Checkタスク

FindBugs, PMD, CheckStyle, Jacocoはビルドバリアント毎に定義されたタスクを持つ.
例) findbugsDevDebug
android.gradleではDebuggableなビルドタイプに限定してこれらのタスクをCheckタスクに依存させている.
(ビルドタイプの限定を解除する場合はCIサービスでメモリ使用量が増えるため事前に確認が必要)

if (variant.buildType.debuggable) {
    check.dependsOn "pmd${variantName}"
    check.dependsOn "findbugs${variantName}"
    check.dependsOn "checkstyle${variantName}"
    check.dependsOn "jacoco${variantName}Report"
}

その他

android.gradleで定義されるcheckEnvironmentSettingsタスクはプロジェクトに必要な環境をチェックするためのタスク.
標準でJDKのバージョンチェックを実施する.

task checkEnvironmentSettings() {
    group 'Verification'
    description "Check environment settings"

    // Ormaはaptによるコード生成にJava1.8を要求する
    if (JavaVersion.current() < JavaVersion.VERSION_1_8) {
        println("\n\tYou will need Java 1.8 or higher if you use Orma.")
        println("\tCurrent Java version is really old. Found ver. " + JavaVersion.current() + ".\n")
        throw new GradleException("Please Update your Java.")
    }
}