はじめに
KotlinConfで build.gradle
をktsで書いてバリバリ補完効かせながら書いてるのを見てよさそうじゃんって試してみたらめちゃめちゃハマったのでメモです。
環境
- Android Studio 3.0
- Gradle wrapper 4.3.1
- Mac OS Sierra
サンプルプロジェクト
こんな感じのサンプルを書き換えていきます。
buildscript {
ext.kotlin_version = '1.2.0-rc-39'
repositories {
google()
jcenter()
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:3.1.2'
classpath 'com.github.gfx.ribbonizer:ribbonizer-plugin:2.1.0'
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.github.gfx.ribbonizer'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.chibatching.ktssample"
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
applicationIdSuffix '.debug'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
ribbonizer {
builder { variant, iconFile ->
if (variant.buildType.name == "debug") {
return yellowRibbonFilter(variant, iconFile)
} else {
return grayRibbonFilter(variant, iconFile)
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.google.firebase:firebase-core:11.6.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
apply plugin: 'com.google.gms.google-services'
Step 1 - ルートレベルのbuild.gradle
分量的にも難易度的にもとりあえずここからやるのがいいと思います。
素直に変換すると次のような感じになります。ほとんど同じですね。
大きく違うのはgroovyの時に ext.kotlin_version = '1.2.0-rc-39'
としていた箇所。
delegated propertyを使うことで(gradleプロジェクトの)拡張プロパティを定義することができます(ついでに名前をキャメルケースに変更したのでappレベルのほうも適宜変更してください)。
buildscript {
val kotlinVersion by extra { "1.2.0-rc-39" }
repositories {
google()
jcenter()
maven(url = "http://dl.bintray.com/kotlin/kotlin-eap-1.2")
}
dependencies {
classpath("com.android.tools.build:gradle:3.0.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
classpath("com.google.gms:google-services:3.1.2")
classpath("com.github.gfx.ribbonizer:ribbonizer-plugin:2.1.0")
}
}
allprojects {
repositories {
google()
jcenter()
maven(url = "http://dl.bintray.com/kotlin/kotlin-eap-1.2")
}
}
task("clean", Delete::class) {
delete = setOf(rootProject.buildDir)
}
ハマリポイント 1: gradle syncできない
ここで一発でgradle syncできたあなた!ラッキーですね!
僕は出来ませんでした。一番ハマったのがここです。
しかも、./gradlew
コマンドからはビルドできるのにAndroid Studioではsyncできないという状況。
KotlinConf会場のGradleブースで質問できていなかったら多分諦めてます。
ASのGradle Consoleには以下のようなエラーが出ていました。
Error:Cause: org/jetbrains/kotlin/lexer/KotlinLexer : Unsupported major.minor version 52.0
はい、後半部分はJavaで開発してるとわりとよく見るやつですね。Javaのバージョンが低いです。
ちなみに、build.gradle.ktsはJava 8以上が必要です。
コマンドラインからはビルドできているので ./gradlew -v
しても当然Java 8だと表示されます。
------------------------------------------------------------
Gradle 4.3.1
------------------------------------------------------------
Build time: 2017-11-08 08:59:45 UTC
Revision: e4f4804807ef7c2829da51877861ff06e07e006d
Groovy: 2.4.12
Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM: 1.8.0_131 (Oracle Corporation 25.131-b11)
OS: Mac OS X 10.12.6 x86_64
ASが内部で使うJavaのバージョンが悪いと想像はつくのですがASにGradleが使うJVMの設定はありません。
Gradleの中の人に助けてくれーと泣きついて出てきた解決策は「一度IntelliJ IDEAで開いて設定を変える」でした。
Ultimate版で試したので確認はしていないですがCommunity版にも多分あるんじゃないでしょうか。(雑ですいません)
ここでGradle JVMの項目を1.8に変更します。その後、ASで開き直すとGradle syncできるようになります。
.ideaフォルダのファイルに差分が出ていたので .idea/gradle.xml
の編集だけでも大丈夫なのではと思いますが未確認です。
Step 2 - appレベルのbuild.gradle
気合でガッと全部書き換えます(徐々に変えていく方法もあります - appendix)
ただ、appレベルを書き換える際、ルートレベルのbuild.gradleとsettings.gradleも変更する必要があります。
最初にこのstepでの最終型を示してハマリポイントを紹介していこうと思います。
allprojects {
val kotlinVersion by extra { "1.2.0-rc-39" }
repositories {
google()
jcenter()
maven(url = "http://dl.bintray.com/kotlin/kotlin-eap-1.2")
}
}
task("clean", Delete::class) {
delete = setOf(rootProject.buildDir)
}
import com.github.gfx.ribbonizer.FilterBuilder
import com.github.gfx.ribbonizer.GrayRibbonBuilder
import com.github.gfx.ribbonizer.YellowRibbonBuilder
plugins {
id("com.android.application") version "3.0.0"
kotlin("android") version "1.2.0-rc-39"
kotlin("android.extensions") version "1.2.0-rc-39"
id("com.github.gfx.ribbonizer") version "2.1.0"
id("com.google.gms.google-services") version "3.1.2" apply false
}
android {
compileSdkVersion(26)
defaultConfig {
applicationId = "com.chibatching.ktssample"
minSdkVersion(17)
targetSdkVersion(26)
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("debug") {
isMinifyEnabled = false
applicationIdSuffix = ".debug"
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), file("proguard-rules.pro"))
}
}
}
ribbonizer {
withGroovyBuilder {
"builder"(FilterBuilder { variant, iconFile ->
return@FilterBuilder when (variant.buildType.name) {
"debug" -> YellowRibbonBuilder().apply(variant, iconFile)
else -> GrayRibbonBuilder().apply(variant, iconFile)
}
})
}
}
val kotlinVersion: String by extra
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to arrayOf("*.jar"))))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion")
implementation("com.android.support:appcompat-v7:26.1.0")
implementation("com.android.support.constraint:constraint-layout:1.0.2")
implementation("com.google.firebase:firebase-core:11.6.0")
testImplementation("junit:junit:4.12")
androidTestImplementation("com.android.support.test:runner:1.0.1")
androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.1")
}
apply(mapOf("plugin" to "com.google.gms.google-services"))
pluginManagement {
repositories {
gradlePluginPortal()
maven { url "https://jcenter.bintray.com/" }
maven { url "https://maven.google.com" }
maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.2" }
}
resolutionStrategy {
eachPlugin {
switch (requested.id.id) {
case "com.android.application":
useModule("com.android.tools.build:gradle:${requested.version}")
break
case "com.github.gfx.ribbonizer":
useModule("com.github.gfx.ribbonizer:ribbonizer-plugin:${requested.version}")
break
case "com.google.gms.google-services":
useModule("com.google.gms:google-services:${requested.version}")
break
}
}
}
}
include ':app'
ハマリポイント 2: apply構文が(実質)使えない
appレベルのgradleの冒頭、見慣れない記述があります。
plugins {
id("com.android.application") version "3.0.0"
kotlin("android") version "1.2.0-rc-39"
kotlin("android.extensions") version "1.2.0-rc-39"
id("com.github.gfx.ribbonizer") version "2.1.0"
id("com.google.gms.google-services") version "3.1.2" apply false
}
build.gradle.ktsでは、プラグインの適用を今までのapply構文ではなくこのplugins構文で行う必要があります。
apply構文も実際には使えるのですが、その場合各プラグインのconfigurationにtype safeにアクセスができません。
つまり android { ...
等と書いてもすべてコンパイルエラーになります。
このplugins構文ではプラグインの依存解決はbuildscript
ブロックではなく、settings.gradleのpluginManagement
で行うことになります(上記サンプル参照)。
このpluginManagement
のrepositories
ブロックでgoogle()
やjcenter()
を書いても認識されないのでmavenでurlを指定して書く必要があります。
また、このrepositories
ブロックにリポジトリURLを書くだけではplugins構文に書いたplugin idが解決できないことが多くあります。
どうやら、plugin idに指定したプラグインのartifactは[指定したid]:[指定したid].gradle.plugin:[version]
の形式である必要があるらしく、この形式になっていないものはresolutionStrategy
ブロックで自ら解決しなくてはいけません。
resolutionStrategy {
eachPlugin {
switch (requested.id.id) {
case "com.android.application":
useModule("com.android.tools.build:gradle:${requested.version}")
break
書き方は難しくなく、requested.id.id
にplugins
ブロックで記載したidが入っているので、その値を見て元々buildscript
ブロックで指定していたclasspath
の値で解決するように指定するだけです。
ハマリポイント 3: 任意の箇所でapplyしたい
firebaseを使うときにはcom.google.gms.google-services
プラグインをbuild.gradleの末尾でapplyする必要があります。
しかし、plugins構文を使うと即時applyされる && pluginsブロックはファイル内に1つという制限があります。
applyのタイミングを任意に変える時はpluginのid指定時にapply false
を指定することで即時applyを防ぎ、適用したい箇所で改めてapplyを記述することで実現できます。
plugins {
...
id("com.google.gms.google-services") version "3.1.2" apply false
}
...
apply(mapOf("plugin" to "com.google.gms.google-services"))
長くなってしまったのでpart 2に続きます(予定)。
appendix
apply {
— shiraji (@shiraj_i) November 4, 2017
from "mybuild.gradle"
}
でいけるよとのことでした。若干字が汚く細かい文法がわかりません。。。自分は移行ツールがOSSで出るので完全移行そちらに任せてしまおうと決意しました。
apply {
from("${project.rootDir}/app/old_build.gradle")
}
という感じで以前のbuild.gradleを読み込んで徐々にkts化していくことができそうです