This message was deleted.
# plugin-development
s
This message was deleted.
e
build.gradle.kts
Copy code
plugins {
    `kotlin-dsl`
}
dependencies {
    compileOnly("com.android.tools.build:gradle-api:7.1.3")
    testImplementation("junit:junit:4.13.2")
}
src/main/kotlin/example.gradle.kts
Copy code
val android: com.android.build.api.dsl.ApplicationExtension by extensions
println(android)
src/test/kotlin/ExamplePluginTest.kt
Copy code
import java.io.File
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class ExamplePluginTest {
    @get:Rule
    val tempDir = TemporaryFolder()

    @Test
    fun test() {
        File(tempDir.root, "settings.gradle").writeText(
            """
            |include 'app'
            |""".trimMargin()
        )
        File(tempDir.root, "build.gradle").writeText(
            """
            |buildscript {
            |    repositories {
            |        google()
            |        mavenCentral()
            |    }
            |    dependencies {
            |        classpath 'com.android.tools.build:gradle:7.1.3'
            |    }
            |}
            |""".trimMargin()
        )
        File(tempDir.newFolder("app"), "build.gradle").writeText(
            """
            |plugins {
            |    id 'com.android.application'
            |    id 'example'
            |}
            |""".trimMargin()
        )
        GradleRunner.create()
            .withProjectDir(tempDir.root)
            .withPluginClasspath()
            .forwardOutput()
            .build()
    }
}
the test fails with
Copy code
FAILURE: Build failed with an exception.

* What went wrong:
* com/android/build/api/dsl/ApplicationExtension
* > com.android.build.api.dsl.ApplicationExtension
*
* * Try:
* > Run with --stacktrace option to get the stack trace.
* > Run with --info or --debug option to get more log output.
* > Run with --scan to get full insights.
*
* * Get more help at <https://help.gradle.org>
*
* BUILD FAILED in 3s
*
I have a workaround by using
Copy code
File(tempDir.root, "build.gradle").writeText(
            """
            |buildscript {
            |    repositories {
            |        google()
            |        mavenCentral()
            |    }
            |    dependencies {
            |        classpath 'com.android.tools.build:gradle:7.1.3'
            |        classpath files(${
                PluginUnderTestMetadataReading.readImplementationClasspath(javaClass.classLoader)
                    .joinToString(separator = ", ") { "'${it.absolutePath}'" }
            })
            |    }
            |}
            |""".trimMargin()
        )
instead of
.withPluginClasspath()
, but I'm not sure if it's a good approach since
PluginUnderTestMetadataReading
is in an internal package
why is whatever
GradleRunner.withPluginClasspath()
is doing behaving differently from this?
changing
build.gradle.kts
to
Copy code
plugins {
    `kotlin-dsl`
}
dependencies {
    implementation("com.android.tools.build:gradle-api:7.1.3")
    testImplementation("junit:junit:4.13.2")
}
doesn't fix things, it just changes the error
c
Implementation doesn't change it? It's weird About the compileOnly is the usual
e
implementation changes the problem to a ClassCastException
I think this example is reproducible
c
In any case what you want is very common, and the fix is always adding a configuration to the PluginUnderTestMetadata. You may just add your compileOnly configuration if it suits you
I was now looking at a repo from @tony but it seems he found another fix (even if I don't see how)
e
thanks for the tip. I simplified it down to
Copy code
tasks.pluginUnderTestMetadata {
    pluginClasspath.from(configurations.compileOnly)
}
for my use case
👍 1
c
Groovy?
e
no, build.gradle.kts
c
In kts I would add the
.configure {...}
For configuration avoidance
e
the generated task accessors are lazy
c
Oh, good
t
My personal workaround:
Copy code
// See <https://github.com/gradle/gradle/issues/7974>
val additionalPluginClasspath by configurations.creating

dependencies {
    compileOnly("com.android.tools.build:gradle:${Version.ANDROID_GRADLE_PLUGIN_VERSION}")
    additionalPluginClasspath("com.android.tools.build:gradle:${Version.ANDROID_GRADLE_PLUGIN_VERSION}")
}

tasks {
    pluginUnderTestMetadata {
        this.pluginClasspath.from(additionalPluginClasspath)
    }
}
I prefer declaring the dependency twice, in different configurations, than adding
compileOnly
to the
pluginClasspath
, as that allows me to add other things to
compileOnly
(or
compileOnlyApi
) independently of that testing need. I could also have made
compileOnly.extendsFrom(additionalPluginClasspath)
fwiw.
e
yes, I see that in the general case. in my specific case, I only have this one dependency; I can always extend it in the future if needed
c
I did duplicate the configuration too because I run the tests with different AGP versions (using a gradle property) in some of my plugins (just not in the one I shared)
What I want to know about @tony solution is how can he run the tests with different AGP versions...
t
Fwiw: I don't. I don't even use AGP myself as I don't do Android stuff (PWA FTW!) so AGP support is best effort (I haven't migrated to the new variant API, and really wonder whether I should, or just remove AGP support from my plugin and let interested people, if any, rebuild it in a new, Android-specific, plugin –based on mine, or maybe not even)
c
AGP is just a detail here, is an plugin dependency that you don't want to expose transitively
t
It's an "optional dependency", plugin behaves differently with
java-base
plugin vs AGP. This is https://plugins.gradle.org/plugin/net.ltgt.errorprone fwiw (plugin extends annotation processor configurations and tries to determine which
JavaCompile
tasks are test)
AGP support could easily be split into its own plugin on top of mine.
t
I don't use withPluginClasspath in my OSS plugin (I do use it for some simple cases at work). Instead I just publish my plugin to a local repo and apply it like you would in a normal build. this makes the tests more realistic imo. it also makes it possible for me to instrument my test builds to run against different versions of AGP in the same test invocation
👍 3
1