Skip to content

Commit 382ad5b

Browse files
Test Gradle plugin on relevant PRs (#2509)
* Update Gradle used in tooling subprojects * Update Kotlin in Compose Gradle plugin * Decrease verbosity of Gradle plugin tests * Disable mac sign test * Add workflow to test Gradle plugin * Fix custom jdk tests on Linux * Make Compose Gradle plugin build compatible with Configuration cache * Print tests summary * Remove unused code * Refactor tests configuration * Turn off parallel execution * Try adding windows runner * Turn off fail fast * Fix Windows test issues #2368 * Adjust default proguard rules The following rule is needed to fix tests on Windows: ``` -dontwarn org.graalvm.compiler.core.aarch64.AArch64NodeMatchRules_MatchStatementSet* ``` Other rules are just to make builds less noisy. Kotlin's `*.internal` packages often contain bytecode, which triggers ProGuard's notes. However, these notes are not actionable for most users, so we can ignore notes by default. #2393
1 parent 7e574a0 commit 382ad5b

File tree

23 files changed

+448
-128
lines changed

23 files changed

+448
-128
lines changed

.github/workflows/gradle-plugin.yml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Test Gradle plugin
2+
on:
3+
pull_request:
4+
paths:
5+
- 'gradle-plugins/**'
6+
- '.github/workflows/gradle-plugin.yml'
7+
jobs:
8+
test-gradle-plugin:
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
os: [ubuntu-20.04, macos-12, windows-2022]
13+
runs-on: ${{ matrix.os }}
14+
steps:
15+
- uses: actions/checkout@v3
16+
- uses: actions/setup-java@v3
17+
with:
18+
distribution: 'corretto'
19+
java-version: '16'
20+
- name: Test Gradle plugin
21+
shell: bash
22+
run: |
23+
cd gradle-plugins
24+
./gradlew assemble
25+
./gradlew :compose:check --continue
26+
- name: Print summary
27+
shell: bash
28+
if: always()
29+
run: |
30+
cd gradle-plugins/compose/build/test-summary
31+
for SUMMARY_FILE in `find . -name "*.md"`; do
32+
FILE_NAME=`basename $SUMMARY_FILE`
33+
echo "## $FILE_NAME" >> $GITHUB_STEP_SUMMARY
34+
cat $SUMMARY_FILE >> $GITHUB_STEP_SUMMARY
35+
done

gradle-plugins/build.gradle.kts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import com.gradle.publish.PluginBundleExtension
2+
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
23

34
plugins {
4-
val kotlinVersion = "1.5.30"
5+
val kotlinVersion = "1.7.20"
56
kotlin("jvm") version kotlinVersion apply false
67
kotlin("plugin.serialization") version kotlinVersion apply false
78
id("com.gradle.plugin-publish") version "0.17.0" apply false
@@ -26,6 +27,15 @@ subprojects {
2627
}
2728
}
2829

30+
plugins.withId("org.jetbrains.kotlin.jvm") {
31+
tasks.withType(KotlinJvmCompile::class).configureEach {
32+
// must be set to a language version of the kotlin compiler & runtime,
33+
// which is bundled to the oldest supported Gradle
34+
kotlinOptions.languageVersion = "1.5"
35+
kotlinOptions.apiVersion = "1.5"
36+
}
37+
}
38+
2939
plugins.withId("maven-publish") {
3040
configureIfExists<PublishingExtension> {
3141
repositories {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
6+
import org.gradle.api.DefaultTask
7+
import org.gradle.api.file.RegularFile
8+
import org.gradle.api.model.ObjectFactory
9+
import org.gradle.api.provider.Property
10+
import org.gradle.api.provider.SetProperty
11+
import org.gradle.api.tasks.Input
12+
import org.gradle.api.tasks.InputFile
13+
import org.gradle.api.tasks.TaskAction
14+
import java.util.*
15+
import java.util.zip.ZipFile
16+
import javax.inject.Inject
17+
18+
/**
19+
* Checks that every class in a [jarFile] matches one of [allowedPackagePrefixes]
20+
*/
21+
abstract class CheckJarPackagesTask @Inject constructor(
22+
objects: ObjectFactory
23+
) : DefaultTask() {
24+
@get:InputFile
25+
val jarFile: Property<RegularFile> = objects.fileProperty()
26+
27+
@get:Input
28+
val allowedPackagePrefixes: SetProperty<String> = objects.setProperty(String::class.java)
29+
30+
@TaskAction
31+
fun run() {
32+
ZipFile(jarFile.get().asFile).use { zip ->
33+
checkJarContainsExpectedPackages(zip)
34+
}
35+
}
36+
37+
private fun checkJarContainsExpectedPackages(jar: ZipFile) {
38+
val unexpectedClasses = arrayListOf<String>()
39+
val allowedPrefixes = allowedPackagePrefixes.get().map { it.replace(".", "/") }
40+
41+
for (entry in jar.entries()) {
42+
if (entry.isDirectory || !entry.name.endsWith(".class")) continue
43+
44+
if (allowedPrefixes.none { prefix -> entry.name.startsWith(prefix) }) {
45+
unexpectedClasses.add(entry.name)
46+
}
47+
}
48+
49+
if (unexpectedClasses.any()) {
50+
error(buildString {
51+
appendLine("All classes in ${jar.name} must match allowed prefixes:")
52+
allowedPrefixes.forEach {
53+
appendLine(" * $it")
54+
}
55+
appendLine("Non-valid classes:")
56+
val unexpectedGroups = unexpectedClasses
57+
.groupByTo(TreeMap()) { it.substringBeforeLast("/") }
58+
for ((_, classes) in unexpectedGroups) {
59+
appendLine(" * ${classes.first()}")
60+
}
61+
})
62+
}
63+
}
64+
}
65+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
6+
import org.gradle.api.DefaultTask
7+
import org.gradle.api.file.ConfigurableFileCollection
8+
import org.gradle.api.file.RegularFileProperty
9+
import org.gradle.api.model.ObjectFactory
10+
import org.gradle.api.tasks.InputFiles
11+
import org.gradle.api.tasks.OutputFile
12+
import org.gradle.api.tasks.TaskAction
13+
import java.io.File
14+
import javax.inject.Inject
15+
16+
abstract class SerializeClasspathTask @Inject constructor(
17+
objects: ObjectFactory
18+
) : DefaultTask() {
19+
@get:InputFiles
20+
val classpathFileCollection: ConfigurableFileCollection = objects.fileCollection()
21+
22+
@get:OutputFile
23+
val outputFile: RegularFileProperty = objects.fileProperty()
24+
25+
@TaskAction
26+
fun run() {
27+
val classpath = classpathFileCollection.files.joinToString(File.pathSeparator) { it.absolutePath }
28+
val outputFile = outputFile.get().asFile
29+
outputFile.parentFile.mkdirs()
30+
outputFile.writeText(classpath)
31+
}
32+
}

gradle-plugins/buildSrc/src/main/kotlin/gradleUtils.kt

+36-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55

66
import org.gradle.api.JavaVersion
77
import org.gradle.api.Project
8+
import org.gradle.api.Task
89
import org.gradle.api.artifacts.dsl.DependencyHandler
10+
import org.gradle.api.file.RegularFile
911
import org.gradle.api.plugins.JavaPlugin
12+
import org.gradle.api.provider.Provider
13+
import org.gradle.api.tasks.TaskContainer
14+
import org.gradle.api.tasks.TaskProvider
15+
import org.gradle.api.tasks.bundling.Jar
1016
import org.gradle.api.tasks.testing.Test
1117
import org.gradle.kotlin.dsl.dependencies
18+
import org.gradle.kotlin.dsl.register
1219
import org.gradle.kotlin.dsl.withType
1320
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
1421
import java.io.File
@@ -35,19 +42,46 @@ fun Test.configureJavaForComposeTest() {
3542
}
3643
}
3744

38-
fun Project.configureJUnit() {
45+
fun Project.configureAllTests(fn: Test.() -> Unit = {}) {
3946
fun DependencyHandler.testImplementation(notation: Any) =
4047
add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, notation)
4148

4249
dependencies {
4350
testImplementation(platform("org.junit:junit-bom:5.7.0"))
4451
testImplementation("org.junit.jupiter:junit-jupiter")
52+
testImplementation("org.junit.platform:junit-platform-launcher")
4553
}
4654

4755
tasks.withType<Test>().configureEach {
4856
useJUnitPlatform()
4957
testLogging {
5058
events("passed", "skipped", "failed")
5159
}
60+
fn()
5261
}
53-
}
62+
}
63+
64+
fun Test.systemProperties(map: Map<String, Any>) {
65+
for ((k, v) in map) {
66+
systemProperty(k, v)
67+
}
68+
}
69+
70+
fun TaskProvider<*>.dependsOn(vararg dependencies: Any) {
71+
configure {
72+
dependsOn(dependencies)
73+
}
74+
}
75+
76+
inline fun <reified T : Task> TaskContainer.registerVerificationTask(
77+
name: String,
78+
crossinline fn: T.() -> Unit
79+
): TaskProvider<T> =
80+
register(name, T::class) {
81+
fn()
82+
}.apply {
83+
named("check").dependsOn(this)
84+
}
85+
86+
val Provider<out Jar>.archiveFile: Provider<RegularFile>
87+
get() = flatMap { it.archiveFile }

gradle-plugins/compose/build.gradle.kts

+14-73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2-
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.getCurrentOperatingSystem
3-
import java.util.zip.ZipFile
42

53
plugins {
64
kotlin("jvm")
@@ -61,8 +59,6 @@ dependencies {
6159
compileOnly(kotlin("native-utils"))
6260

6361
testImplementation(gradleTestKit())
64-
testImplementation(platform("org.junit:junit-bom:5.7.0"))
65-
testImplementation("org.junit.jupiter:junit-jupiter")
6662
testImplementation(kotlin("gradle-plugin-api"))
6763

6864
// include relocated download task to avoid potential runtime conflicts
@@ -89,62 +85,17 @@ val jar = tasks.named<Jar>("jar") {
8985
this.duplicatesStrategy = DuplicatesStrategy.INCLUDE
9086
}
9187

92-
// __SUPPORTED_GRADLE_VERSIONS__
93-
//testGradleVersion("6.7.1") // min supported by kotlin 1.7.0 gradle plugin https://kotlinlang.org/docs/gradle.html
94-
// despite that, some tests didn't pass
95-
testGradleVersion("7.1.1")
96-
testGradleVersion("7.3.3")
97-
98-
val javaHomeForTests: String? = when {
99-
// __COMPOSE_NATIVE_DISTRIBUTIONS_MIN_JAVA_VERSION__
100-
JavaVersion.current() >= JavaVersion.VERSION_15 -> System.getProperty("java.home")
101-
else -> System.getenv("JDK_15")
102-
?: System.getenv("JDK_FOR_GRADLE_TESTS")
103-
}
104-
val isWindows = getCurrentOperatingSystem().isWindows
88+
val supportedGradleVersions = project.property("compose.tests.gradle.versions")
89+
.toString().split(",")
90+
.map { it.trim() }
10591

10692
val gradleTestsPattern = "org.jetbrains.compose.test.tests.integration.*"
10793

10894
// check we don't accidentally including unexpected classes (e.g. from embedded dependencies)
109-
val checkJar by tasks.registering {
95+
tasks.registerVerificationTask<CheckJarPackagesTask>("checkJar") {
11096
dependsOn(jar)
111-
112-
doLast {
113-
val file = jar.get().archiveFile.get().asFile
114-
ZipFile(file).use { zip ->
115-
checkJarContainsExpectedPackages(zip)
116-
}
117-
}
118-
}
119-
120-
// we want to avoid accidentally including unexpected jars/packages, e.g kotlin-stdlib etc
121-
fun checkJarContainsExpectedPackages(jar: ZipFile) {
122-
val expectedPackages = arrayOf(
123-
"org/jetbrains/compose",
124-
"kotlinx/serialization"
125-
)
126-
val unexpectedClasses = arrayListOf<String>()
127-
128-
for (entry in jar.entries()) {
129-
if (entry.isDirectory || !entry.name.endsWith(".class")) continue
130-
131-
if (expectedPackages.none { prefix -> entry.name.startsWith(prefix) }) {
132-
unexpectedClasses.add(entry.name)
133-
}
134-
}
135-
136-
if (unexpectedClasses.any()) {
137-
error(buildString {
138-
appendLine("Some classes from ${jar.name} are not from 'org.jetbrains.compose' package:")
139-
unexpectedClasses.forEach {
140-
appendLine(" * $it")
141-
}
142-
})
143-
}
144-
}
145-
146-
tasks.check {
147-
dependsOn(checkJar)
97+
jarFile.set(jar.archiveFile)
98+
allowedPackagePrefixes.addAll("org.jetbrains.compose", "kotlinx.serialization")
14899
}
149100

150101
tasks.test {
@@ -154,34 +105,24 @@ tasks.test {
154105
excludeTestsMatching(gradleTestsPattern)
155106
}
156107
}
157-
fun testGradleVersion(gradleVersion: String) {
158-
val taskProvider = tasks.register("testGradle-$gradleVersion", Test::class) {
159-
tasks.test.get().let { defaultTest ->
160-
classpath = defaultTest.classpath
161-
}
108+
109+
for (gradleVersion in supportedGradleVersions) {
110+
tasks.registerVerificationTask<Test>("testGradle-$gradleVersion") {
111+
classpath = tasks.test.get().classpath
162112
systemProperty("compose.tests.gradle.version", gradleVersion)
163113
filter {
164114
includeTestsMatching(gradleTestsPattern)
165115
}
166116
}
167-
tasks.named("check") {
168-
dependsOn(taskProvider)
169-
}
170117
}
171118

172-
configureJUnit()
173-
174-
tasks.withType<Test>().configureEach {
119+
configureAllTests {
175120
configureJavaForComposeTest()
176-
177121
dependsOn(":publishToMavenLocal")
178-
179122
systemProperty("compose.tests.compose.gradle.plugin.version", BuildProperties.deployVersion(project))
180-
for ((k, v) in project.properties) {
181-
if (k.startsWith("compose.")) {
182-
systemProperty(k, v.toString())
183-
}
184-
}
123+
val summaryDir = project.buildDir.resolve("test-summary")
124+
systemProperty("compose.tests.summary.file", summaryDir.resolve("$name.md").absolutePath)
125+
systemProperties(project.properties.filter { it.key.startsWith("compose.") })
185126
}
186127

187128
task("printAllAndroidxReplacements") {

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
4444
KotlinPlatformType.js -> isApplicableJsTarget(kotlinCompilation.target)
4545
KotlinPlatformType.androidJvm -> true
4646
KotlinPlatformType.native -> true
47+
KotlinPlatformType.wasm -> false
4748
}
4849

4950
private fun isApplicableJsTarget(kotlinTarget: KotlinTarget): Boolean {

0 commit comments

Comments
 (0)