Scope Reap - What are you calling?
Let’s say we have foo/bar/build.gradle file with the following contents:
plugins {
alias(libs.plugins.androidLibrary)
}
android {
compileSdk = 36
namespace = "org.example.foo.bar"
buildTypes {
release {
kotlin {
compilerOptions { freeCompilerArgs = ["-Werror"]}
}
}
}
}
A naive read of this would make it seem that this would cause :foo:bar:compileReleaseKotlin to turn Kotlin compiler
warnings into errors. The reality is not quite that simple and depends a lot on rest of the project set up.
Using Android Gradle Plugin 9.0.0 or newer
This code will make :foo:bar:compileReleaseKotlin fail on warnings, but it will unexpectedly fail any other Kotlin
compiler task in the project, for example compileDebugKotlin and compileDebugUnitTestKotlin. Why? Turns out that
kotlin is actually a top level extension, thus affected by the placement inside of android { buildTypes { release {.
It is equivalent to:
plugins {
alias(libs.plugins.androidLibrary)
}
android {
compileSdk = 36
namespace = "org.example.foo.bar"
buildTypes {
release {}
}
}
kotlin {
compilerOptions { freeCompilerArgs = ["-Werror"] }
}
In Groovy and by default in Kotlin script you can reach to anything from outer scopes. In Kotlin, you can prevent some
of these issues with @DslMarker, but
not this specific issue.
Using Android Gradle Plugin 8.x
This version of Android Gradle Plugin did not have built-in Kotlin, so generally Gradle should fail with:
A problem occurred evaluating project ':foo:bar'.
> Could not find method kotlin() for arguments [build_5ajgggwyonmf5wfwrp0p2ga61$_run_closure2@198010b] on project ':foo:bar' of type org.gradle.api.Project.
If we succeed, why? If any parent project such as root : or :foo applies Kotlin Gradle Plugin, you are configuring
Kotlin in those projects. Let’s say foo/build.gradle has:
plugins {
alias(libs.plugins.kotlinJvm)
}
That means -Werror is actually will make :foo:compileKotlin and other Kotlin compile tasks fail on warnings.
Gradle has wired up Groovy dynamic invocation to look for kotlin in the following places:
- Plugin extensions named
kotlinin:foo:bar - Values in
ExtraPropertiesExtensionin:foo:bar(e.g.ext.kotlin = ...) - Gradle property named
kotlinin:foo:bar
If not found, then repeats that project for each parent project, and only fail it is not found anywhere up the search stack.
To make things extra confusing, Gradle properties are also layered. If you have gradle.properties with:
propA=root-propA
propB=root-propB
and you have foo/gradle.properties with:
propB=foo-propB
propC=foo-propC
And then in foo/bar/build.gradle has println("$propA - $propB - $propC") will print out:
root-propA - foo-propB - foo-propC
Scope? Scope! Cry.
This is the reason why Gradle fails when using org.gradle.unsafe.isolated-projects=true and you make a call
Project.getProperty("missingProperty").