2014年10月7日火曜日

EclipseのプロジェクトをGradleでビルドし、JenkinsでもRobolectricを動くようにする

このエントリーをはてなブックマークに追加 はてなブックマーク - EclipseのプロジェクトをGradleでビルドし、JenkinsでもRobolectricを動くようにする

最近のAndroid開発環境はAndroidStudioがデファクトスタンダードっぽいのですが、関わっているプロジェクトがNDKを利用しており、まだAndroidStudioでは未対応なので、Eclipseを使っております。

そんなプロジェクトなのですが、Robolectricを使ってテストしており、Jenkinsで実行させる為にコマンドラインで実行するまでにした事をメモしておきます。

どうやったか

Antでやる方法もあると思うのですが、Gradleだとそれ用のプラグインがあるのでGradleを使いました。

Github

とりあえず使える状態のサンプルをGithubにおいてあります。

RobolectricGradleSample

READMEに書いてあるような状態であれば以下で実行出来るかと思います。

$gradle clean test

EclipseでRobolectricを実行させる

以下の公式サイトに丁寧に記載があるのでこちらを参照。

Eclipse Quick Start

Gradleのビルドで必要なファイルを作る

Eclipseから簡単にGradleでビルドする時に必要なファイルを出力する事が出来るので、利用します。Eclipseを起動し、以下で完了です。

File→Export→Android→Generate Gradle build files

完了すると色々ファイルが出来ていると思うので、バージョン管理に登録しておきます。

サーバー環境でAndroidをGradleでビルドする

Androidをビルド出来る環境を作る

AndroidSDKをサーバーにインストールします。自分の場合、Linuxだったので対応するSDKをダウンロードして、後は自分のビルドしたいAndoridバージョンのSDKをインストールしておきます。

この時、 AndroidSDK Build-toolsAndroid Support Repositoryもインストールしておいてください。

AndoridSDK Build-toolsはGradleのビルド時にバージョンの指定が必要なので、インストールしたバージョンも確認しておいて下さい。

なお、CUIでAndoridSDKなどのインストール方法はこちらを参照。

特定のAndroid SDKをCUIでインストールする方法

GradleをGVMを使ってインストールする

サーバーにGradleをインストールしますが、普通にインストールするとGradleのバージョン切り替えが面倒なのでGVMを使います。

GVM

インストールします。

$curl -s get.gvmtool.net | bash

現在、利用可能なGradleのバージョンを確認します。

$gvm list gradle

1.12をインストールして利用します。(Androidをビルドするためのプラグインが1.1x系を要求するため)

$gvm install gradle 1.12
$gvm use gradle 1.12

環境変数ANDROID_HOMEを設定する

GradleのAndroidプラグインにて環境変数のANDROID_HOMEを指定する必要があるので、設定します。

$vi ~/.bashrc

追加します。

# Andorid_HOME
$export ANDROID_HOME=/Applications/adt-bundle-mac-x86_64-20140624/sdk/

適用させます。

$source ~/.bashrc

Jenkinsで実行する場合、Jenkinsの環境変数を設定する箇所で上記を設定してください。

build.gradleを編集する

Eclipseでプロジェクトを新規作成するとappcompat_v7とかもコンパイルしなければいけなかったり、lib配下にandroid-support-v4.jarとかが存在します。

Eclipseでビルドする時は良いのですが、サーバーでビルドする時に上記も用意したりするのは面倒なので、Gradleを使って依存を解決します。

という事で作成されたbuild.gradleを一部編集します。 自分の場合、以下のようにしました。

// Gradle自身の環境を設定
buildscript {

    // mavenCentralリポジトリを利用する
    repositories {
        mavenCentral()
    }

    // 利用するGradleプラグインは0.13を利用
    dependencies {
        classpath 'com.android.tools.build:gradle:0.13.0'
    }
}

// すべてでmavenCentralのリポジトリを追加
allprojects {
    repositories {
        mavenCentral()
    }
}

apply plugin: 'android'

dependencies {

    compile fileTree(dir: 'libs', include: '*.jar')

    // 削除。下記のように記載することでGradleより取得
    //compile project(':appcompat_v7')

    // 追加。Gradleによってappcompatの依存関係を解決。
    // excludeの記述によってlib配下のsupport-v4との競合を避ける。
    compile ('com.android.support:appcompat-v7:18.0+') {
        exclude module: 'support-v4'
    }
}

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }
}

変更点は以下の通りです。

  • buildscriptを追加(2~13行目)
  • allprojectsを追加(16~20行目)
  • dependenciesの一部を変更(24~36行目)

buildscriptと記述されている場所はGradle自身の依存関係や利用するプラグインの情報を指定します。 今回の指定では以下2点を指定しています。

  1. buildscriptでの依存解決ではmavenCentralリポジトリを利用利用する
  2. com.android.tools.build:gradleの0.13を利用する

allprojectsと記述されている場所では以下を指定しています。

  1. 依存解決ではmavenCentralリポジトリを利用する(buildscript)

depnedenciesでは以下を指定、変更しました。

  1. appcompat_v7プロジェクトのビルドをする、という命令の箇所を削除
  2. コンパイル時に’com.android.support:appcompat-v7:18.0’以上が必要である事を記述
  3. appcompat-v7の依存を解決する時にsupport-v4に関する依存は解決しない

1点目のappcompatについてですが、Eclipseだとプロジェクトを追加した時にappcompat_v7も一緒に作成されて、ビルドが必要となります。ただし、サーバーでは面倒なので、この役目はGradleに任せる事としてコメントアウトしています。

2点目は1点目でコメントアウトしたappcompatについてGradleで依存解決する為の記述です。コンパイルする時にこのプロジェクトはappcompat-v7:18以上に依存しているよという事を記述しています。

3点目ではsupport-v4に関しての依存部分は解決しなくてよい、という事を指定しています。Gradleで依存解決を行う時は対象のものが依存しているものも一緒に依存解決しようとします。 試しにexcludeを消して、gradle dependenciesというコマンドをタイプして各ライブラリの依存関係を表示します。

$gradle dependencies 
・・・・
_debugCompile - ## Internal use, do not manually configure ##
\--- com.android.support:appcompat-v7:18.0+ -> 18.0.0
     \--- com.android.support:support-v4:18.0.0

上記のようにappcompat-v7:18はsupport-v4に依存している事が分かります。

依存解決は通常ありがたいことなのですが、今回作成したプロジェクトではlibsフォルダ配下に既にsupport-v4が存在しています。そのため、excludeしないとビルドエラーとなってしまいます。(Multiple dex files define Landroid/support/v4/accessibilityservice/AccessibilityServiceInfoCompatとか出ます。)

libs配下のsupport-v4を削除するとEclipseのビルドができなくなってしまいますので、今回は上記のように書いてありますが、Gradleでのビルド前提の場合にはGradleで依存解決は解決する方が妥当だと思います。

AndoridをGradleでビルドする

まず、自分がビルドしたいAndoridプロジェクトをサーバーに任意の場所にcloneします。

そして、build.gradleがある場所(対象のAndroidプロジェクトのRoot)に移動します。

そこで、build.gradleを開き、buildToolsVersionがサーバーでインストールしたバージョンと合っているか確認して下さい。もし、違う場合にはエラーとなるので、書き換えてください。

ANDROID_BUILD_TOOLSというような変数にしておいて、gradle.propertiesなどに設定しておく方法がおすすめです。

さて、ビルドしてみます。

$gradle build 

もし、特にエラーがなければ、build/outputs/apk配下にapkが作成されているはずです。

build.gradldを編集する2

buidl.gradleを編集してRobolectricでテスト出来るようにします。

各変更点は後述しますが、全体で以下のようになりました。

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.13.0'
        classpath 'org.robolectric:robolectric-gradle-plugin:0.13.0'
    }
}

allprojects {
    repositories {
        mavenCentral()
    }
}

apply plugin: 'android'
apply plugin: 'robolectric'

android {
    compileSdkVersion 17
    buildToolsVersion "20.0.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
        androidTest {
            setRoot('test')
        }

        // Move the tests to tests/java, tests/res, etc...
        //instrumentTest.setRoot('tests')

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        //release.setRoot('build-types/release')
    }
}

robolectric {
    include '**/*Test.class'
}

dependencies {

    compile fileTree(dir: 'libs', include: '*.jar')
    compile ('com.android.support:appcompat-v7:18.0+') {
      exclude module: 'support-v4'
    }

    // test
    androidTestCompile('junit:junit:4.11') 
    androidTestCompile('org.robolectric:robolectric:2.3')
}

変更点は以下の通りです。

  • 依存するプラグインとしてrobolectricを追加(8,19行目)
  • テストフォルダの位置を指定(35~37行目)
  • robolectricで実行するテストクラスを指定(53~55行目)
  • テストのコンパイル時に利用するライブラリを記述(65,66行目)

自分がこの中でも特にはまったのがテストフォルダの位置を指定する箇所です。

これについてなのですが、robolectric-gradle-plugin:0.12.0までは本設定をしてもまったく有効化せず、必ずsrc/test/java配下にファイルを配置しないといけませんでした。

ただ、0.13からは任意のディレクトリの指定が可能となりました。ただし、必ず指定したディレクトリ配下にjavaというフォルダがある必要はありますので、注意が必要です。

GradleからRobolectricを実行する

早速やってみます。

$gradle clean test

gradle1.12では下記エラーになってしまいます。。。

* What went wrong:
A problem occurred evaluating root project 'RobolectricGradleSample'.
> Could not create plugin of type 'AppPlugin'.

ただし、Gradle2.1ではうまくできます。

$gvm install gradle 2.1
$gvm use gradle 2.1
$gradle clean test

こんな感じで結果がbuild/test-report/index.htmlに出力されています。

また、build/test-result/にXMLも出力されるのでこれを利用してJenkinsで実行結果のグラフ表示も可能です。

RobolectricのPluginが1.x系に対応してないからなのでしょうか。。。ちょっといまいち理由が分かっていません。知っている人がいらっしゃったら教えて頂けるとありがたいです。

0 件のコメント:

コメントを投稿