From 88035297df5a286679a840d1eeb247344061e8c9 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:17:40 +0100 Subject: [PATCH 1/5] Update to Gradle 8 - bump Gradle Plugin Publish Plugin version - build script updates & improvements - use Java Toolchains to set Java version - use jvm-test-fixtures plugin for functionalTest sources --- build.gradle.kts | 53 ++++++++++++++++--- .../kotlin/kotlinx/validation/api/TestDsl.kt | 2 + 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 11e93bfd..5a1412fb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,9 @@ -import com.gradle.publish.* -import java.io.* -import kotlinx.validation.build.* +import kotlinx.validation.build.mavenCentralMetadata +import kotlinx.validation.build.mavenRepositoryPublishing +import kotlinx.validation.build.signPublicationIfKeyPresent import org.gradle.api.attributes.TestSuiteType.FUNCTIONAL_TEST -import org.gradle.api.internal.tasks.testing.* import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.* - plugins { kotlin("jvm") @@ -72,7 +69,7 @@ dependencies { implementation("org.ow2.asm:asm-tree:9.2") implementation("com.googlecode.java-diff-utils:diffutils:1.3.0") compileOnly("org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin:1.8.10") - //compileOnly("com.android.tools.build:gradle:${androidGradlePluginVersion}") + compileOnly("com.android.tools.build:gradle:${androidGradlePluginVersion}") // The test needs the full kotlin multiplatform plugin loaded as it has no visibility of previously loaded plugins, // unlike the regular way gradle loads plugins. @@ -193,3 +190,45 @@ testing { tasks.withType().configureEach { onlyIf("only sign if signatory is present") { signatory?.keyId != null } } + +@Suppress("UnstableApiUsage") +testing { + suites { + withType().configureEach { + useJUnit() + dependencies { + implementation(project()) + implementation("org.assertj:assertj-core:3.18.1") + implementation(project.dependencies.kotlin("test-junit").toString()) + } + } + + val test by getting(JvmTestSuite::class) { + description = "Regular unit tests" + } + + val functionalTest by creating(JvmTestSuite::class) { + testType.set(FUNCTIONAL_TEST) + description = "Functional Plugin tests using Gradle TestKit" + + dependencies { + implementation(files(createClasspathManifest)) + + implementation(gradleApi()) + implementation(gradleTestKit()) + } + + targets.configureEach { + testTask.configure { + shouldRunAfter(test) + } + } + } + + gradlePlugin.testSourceSets(functionalTest.sources) + + tasks.check { + dependsOn(functionalTest) + } + } +} diff --git a/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt b/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt index b7a540cd..d73a8b86 100644 --- a/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt +++ b/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt @@ -9,6 +9,8 @@ import kotlinx.validation.API_DIR import org.gradle.testkit.runner.GradleRunner import org.intellij.lang.annotations.Language import java.io.* +import java.io.File +import java.io.InputStreamReader internal fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { val baseKotlinScope = BaseKotlinScope() From 070b32341aa5b1f6f7c124ab1a29b0a58a02578a Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:06:25 +0100 Subject: [PATCH 2/5] initial convention plugins - create convention plugins for common and java-specific config - add Gradle version catalog - simplify some build config --- build.gradle.kts | 43 +++++++++++-------- buildSrc/build.gradle.kts | 28 +----------- buildSrc/settings.gradle.kts | 6 +++ .../{ => main/kotlin}/MavenCentralMetadata.kt | 2 +- buildSrc/src/{ => main/kotlin}/Publishing.kt | 0 .../main/kotlin/conventions/base.gradle.kts | 32 ++++++++++++++ .../kotlin/conventions/java-base.gradle.kts | 22 ++++++++++ gradle.properties | 4 -- gradle/libs.versions.toml | 39 +++++++++++++++++ settings.gradle.kts | 13 ------ 10 files changed, 128 insertions(+), 61 deletions(-) rename buildSrc/src/{ => main/kotlin}/MavenCentralMetadata.kt (97%) rename buildSrc/src/{ => main/kotlin}/Publishing.kt (100%) create mode 100644 buildSrc/src/main/kotlin/conventions/base.gradle.kts create mode 100644 buildSrc/src/main/kotlin/conventions/java-base.gradle.kts create mode 100644 gradle/libs.versions.toml diff --git a/build.gradle.kts b/build.gradle.kts index 5a1412fb..0bb7d1b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,11 +9,17 @@ plugins { kotlin("jvm") `java-gradle-plugin` id("com.gradle.plugin-publish") + kotlinx.validation.build.conventions.`java-base` signing `maven-publish` `jvm-test-suite` } +group = "org.jetbrains.kotlinx" +providers.gradleProperty("DeployVersion").orNull?.let { + version = it +} + sourceSets { test { java.srcDir("src/test/kotlin") @@ -53,9 +59,6 @@ val createClasspathManifest = tasks.register("createClasspathManifest") { } } -val kotlinVersion: String by project -val androidGradlePluginVersion: String = "7.2.2" - configurations.implementation { exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7") @@ -64,17 +67,17 @@ configurations.implementation { dependencies { implementation(gradleApi()) - implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.6.0") - implementation("org.ow2.asm:asm:9.2") - implementation("org.ow2.asm:asm-tree:9.2") - implementation("com.googlecode.java-diff-utils:diffutils:1.3.0") - compileOnly("org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin:1.8.10") - compileOnly("com.android.tools.build:gradle:${androidGradlePluginVersion}") + implementation(libs.kotlinx.metadata) + implementation(libs.ow2.asm) + implementation(libs.ow2.asmTree) + implementation(libs.javaDiffUtils) + compileOnly(libs.gradlePlugin.android) + compileOnly(libs.gradlePlugin.kotlin) // The test needs the full kotlin multiplatform plugin loaded as it has no visibility of previously loaded plugins, // unlike the regular way gradle loads plugins. - testPluginRuntimeConfiguration("org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin:$kotlinVersion") - testPluginRuntimeConfiguration("com.android.tools.build:gradle:${androidGradlePluginVersion}") + testPluginRuntimeConfiguration(libs.gradlePlugin.android) + testPluginRuntimeConfiguration(libs.gradlePlugin.kotlin) } tasks.compileKotlin { @@ -112,11 +115,17 @@ tasks.withType().configureEach { jvmArgs("-ea") } -properties["DeployVersion"]?.let { version = it } - publishing { - mavenRepositoryPublishing(project) - mavenCentralMetadata() + publications { + create("maven") { + from(components["java"]) + artifact(tasks.javadocJar) + artifact(tasks.sourcesJar) + } + + mavenRepositoryPublishing(project) + mavenCentralMetadata() + } publications.withType().all { signPublicationIfKeyPresent(this) @@ -198,8 +207,8 @@ testing { useJUnit() dependencies { implementation(project()) - implementation("org.assertj:assertj-core:3.18.1") - implementation(project.dependencies.kotlin("test-junit").toString()) + implementation(libs.assertJ.core) + implementation(libs.kotlin.test) } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 67f7dbc8..08cd884b 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,35 +10,11 @@ plugins { `kotlin-dsl` } -val props = Properties().apply { - project.file("../gradle.properties").inputStream().use { load(it) } -} - -val kotlinVersion: String = props.getProperty("kotlinVersion") - dependencies { - implementation(kotlin("gradle-plugin-api", kotlinVersion)) -} - -sourceSets { - configureEach { - when (name) { - SourceSet.MAIN_SOURCE_SET_NAME -> { - kotlin.setSrcDirs(listOf("src")) - resources.setSrcDirs(listOf("resources")) - } - - else -> { - kotlin.setSrcDirs(emptyList()) - resources.setSrcDirs(emptyList()) - } - } - java.setSrcDirs(emptyList()) - groovy.setSrcDirs(emptyList()) - } + implementation(libs.gradlePlugin.kotlin) + implementation(libs.gradlePlugin.pluginPublishing) } - tasks.withType().configureEach { kotlinOptions { allWarningsAsErrors = true diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 9146c9cd..a53be96e 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -22,4 +22,10 @@ dependencyResolutionManagement { mavenCentral() gradlePluginPortal() } + + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } } diff --git a/buildSrc/src/MavenCentralMetadata.kt b/buildSrc/src/main/kotlin/MavenCentralMetadata.kt similarity index 97% rename from buildSrc/src/MavenCentralMetadata.kt rename to buildSrc/src/main/kotlin/MavenCentralMetadata.kt index 5acc8ac3..771aaecb 100644 --- a/buildSrc/src/MavenCentralMetadata.kt +++ b/buildSrc/src/main/kotlin/MavenCentralMetadata.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. + * Copyright 2016-2023 JetBrains s.r.o. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package kotlinx.validation.build diff --git a/buildSrc/src/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt similarity index 100% rename from buildSrc/src/Publishing.kt rename to buildSrc/src/main/kotlin/Publishing.kt diff --git a/buildSrc/src/main/kotlin/conventions/base.gradle.kts b/buildSrc/src/main/kotlin/conventions/base.gradle.kts new file mode 100644 index 00000000..232dc36f --- /dev/null +++ b/buildSrc/src/main/kotlin/conventions/base.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +package kotlinx.validation.build.conventions + +import java.time.Duration + +plugins { + base +} + +// common config for all projects + +if (project != rootProject) { + project.version = rootProject.version + project.group = rootProject.group +} + +tasks.withType().configureEach { + // https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives + isPreserveFileTimestamps = false + isReproducibleFileOrder = true +} + +tasks.withType().configureEach { + timeout.set(Duration.ofMinutes(60)) +} + +tasks.withType().configureEach { + includeEmptyDirs = false +} diff --git a/buildSrc/src/main/kotlin/conventions/java-base.gradle.kts b/buildSrc/src/main/kotlin/conventions/java-base.gradle.kts new file mode 100644 index 00000000..006a8c1b --- /dev/null +++ b/buildSrc/src/main/kotlin/conventions/java-base.gradle.kts @@ -0,0 +1,22 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ +package kotlinx.validation.build.conventions + +plugins { + id("kotlinx.validation.build.conventions.base") + `java` +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } + withSourcesJar() + withJavadocJar() +} + +tasks.withType().configureEach { + useJUnit() +} diff --git a/gradle.properties b/gradle.properties index b1f1f18e..b6f07ba7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,3 @@ version=0.13.1-SNAPSHOT -group=org.jetbrains.kotlinx - -kotlinVersion=1.8.10 -pluginPublishVersion=1.1.0 kotlin.stdlib.default.dependency=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..b5a36423 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,39 @@ +[versions] + +kotlin = "1.8.10" + +javaDiffUtils = "1.3.0" # "4.12" +junit = "5.9.2" +kotest = "5.5.5" +kotlinx-bcv = "0.13.0" +ow2Asm = "9.2" + +gradlePluginPublishPlugin = "1.1.0" +androidGradlePlugin = "7.2.2" + +testGradleVersion = "7.6" # the minimal supported Gradle plugin version, used in functional tests + +[libraries] + +kotlinx-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.6.0" } + +javaDiffUtils = { module = "com.googlecode.java-diff-utils:diffutils", version.ref = "javaDiffUtils" } +#javaDiffUtils = { module = "io.github.java-diff-utils:java-diff-utils", version.ref = "javaDiffUtils" } + +ow2-asm = { module = "org.ow2.asm:asm", version.ref = "ow2Asm" } +ow2-asmTree = { module = "org.ow2.asm:asm-tree", version.ref = "ow2Asm" } + +## region Test Libraries +junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } + +assertJ-core = { module = "org.assertj:assertj-core", version = "3.18.1" } + +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +## endregion + +## region Gradle Plugins +gradlePlugin-pluginPublishing = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "gradlePluginPublishPlugin" } +gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +gradlePlugin-android = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" } +## endregion diff --git a/settings.gradle.kts b/settings.gradle.kts index b8b73b5a..e822cdb7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,19 +12,6 @@ pluginManagement { gradlePluginPortal() mavenCentral() } - - resolutionStrategy { - val kotlinVersion: String by settings - val pluginPublishVersion: String by settings - eachPlugin { - if (requested.id.namespace?.startsWith("org.jetbrains.kotlin") == true) { - useVersion(kotlinVersion) - } - if (requested.id.id == "com.gradle.plugin-publish") { - useVersion(pluginPublishVersion) - } - } - } } @Suppress("UnstableApiUsage") From 5a1884445fdc3f1cd10a392361cd95015c758b81 Mon Sep 17 00:00:00 2001 From: aSemy <897017+aSemy@users.noreply.github.com> Date: Mon, 13 Mar 2023 23:35:07 +0100 Subject: [PATCH 3/5] comment out AGP --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0bb7d1b9..95967333 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -71,7 +71,7 @@ dependencies { implementation(libs.ow2.asm) implementation(libs.ow2.asmTree) implementation(libs.javaDiffUtils) - compileOnly(libs.gradlePlugin.android) + //compileOnly(libs.gradlePlugin.android) compileOnly(libs.gradlePlugin.kotlin) // The test needs the full kotlin multiplatform plugin loaded as it has no visibility of previously loaded plugins, From a3c33d56f0697043b8709892dad73587013545f2 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Thu, 27 Apr 2023 10:06:27 +0200 Subject: [PATCH 4/5] update libs.versions.toml --- build.gradle.kts | 7 +++++-- gradle/libs.versions.toml | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 95967333..7364689f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,8 @@ val createClasspathManifest = tasks.register("createClasspathManifest") { .withPropertyName("outputDir") doLast { - file(outputDir.resolve("plugin-classpath.txt")).writeText( + file(outputDir.resolve("plugin-classpath.txt")) + .writeText( testPluginRuntimeConfiguration.joinToString( "\n" ) @@ -71,9 +72,11 @@ dependencies { implementation(libs.ow2.asm) implementation(libs.ow2.asmTree) implementation(libs.javaDiffUtils) - //compileOnly(libs.gradlePlugin.android) compileOnly(libs.gradlePlugin.kotlin) + // Android support is not yet implemented https://github.com/Kotlin/binary-compatibility-validator/issues/94 + //compileOnly(libs.gradlePlugin.android) + // The test needs the full kotlin multiplatform plugin loaded as it has no visibility of previously loaded plugins, // unlike the regular way gradle loads plugins. testPluginRuntimeConfiguration(libs.gradlePlugin.android) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5a36423..c2058f04 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.8.10" -javaDiffUtils = "1.3.0" # "4.12" +javaDiffUtils = "1.3.0" junit = "5.9.2" kotest = "5.5.5" kotlinx-bcv = "0.13.0" @@ -11,19 +11,21 @@ ow2Asm = "9.2" gradlePluginPublishPlugin = "1.1.0" androidGradlePlugin = "7.2.2" -testGradleVersion = "7.6" # the minimal supported Gradle plugin version, used in functional tests - [libraries] +## region Application libraries kotlinx-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.6.0" } javaDiffUtils = { module = "com.googlecode.java-diff-utils:diffutils", version.ref = "javaDiffUtils" } -#javaDiffUtils = { module = "io.github.java-diff-utils:java-diff-utils", version.ref = "javaDiffUtils" } +# update Java Diff Utils versions https://github.com/Kotlin/binary-compatibility-validator/issues/130 +#javaDiffUtils = { module = "io.github.java-diff-utils:java-diff-utils", version = "4.12" } ow2-asm = { module = "org.ow2.asm:asm", version.ref = "ow2Asm" } ow2-asmTree = { module = "org.ow2.asm:asm-tree", version.ref = "ow2Asm" } +## endregion -## region Test Libraries + +## region Test libraries junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } @@ -32,6 +34,7 @@ assertJ-core = { module = "org.assertj:assertj-core", version = "3.18.1" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } ## endregion + ## region Gradle Plugins gradlePlugin-pluginPublishing = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "gradlePluginPublishPlugin" } gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From 84857fb3773564603580b0fff836d0ca7f047daf Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 1 May 2023 19:06:40 +0200 Subject: [PATCH 5/5] fixes after rebase --- build.gradle.kts | 52 ++----------------- .../kotlin/kotlinx/validation/api/TestDsl.kt | 16 ++++-- 2 files changed, 14 insertions(+), 54 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7364689f..4908f803 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,11 +52,7 @@ val createClasspathManifest = tasks.register("createClasspathManifest") { doLast { file(outputDir.resolve("plugin-classpath.txt")) - .writeText( - testPluginRuntimeConfiguration.joinToString( - "\n" - ) - ) + .writeText(testPluginRuntimeConfiguration.joinToString("\n")) } } @@ -164,8 +160,8 @@ testing { useJUnit() dependencies { implementation(project()) - implementation("org.assertj:assertj-core:3.18.1") - implementation(project.dependencies.kotlin("test-junit").toString()) + implementation(libs.assertJ.core) + implementation(libs.kotlin.test) } } @@ -202,45 +198,3 @@ testing { tasks.withType().configureEach { onlyIf("only sign if signatory is present") { signatory?.keyId != null } } - -@Suppress("UnstableApiUsage") -testing { - suites { - withType().configureEach { - useJUnit() - dependencies { - implementation(project()) - implementation(libs.assertJ.core) - implementation(libs.kotlin.test) - } - } - - val test by getting(JvmTestSuite::class) { - description = "Regular unit tests" - } - - val functionalTest by creating(JvmTestSuite::class) { - testType.set(FUNCTIONAL_TEST) - description = "Functional Plugin tests using Gradle TestKit" - - dependencies { - implementation(files(createClasspathManifest)) - - implementation(gradleApi()) - implementation(gradleTestKit()) - } - - targets.configureEach { - testTask.configure { - shouldRunAfter(test) - } - } - } - - gradlePlugin.testSourceSets(functionalTest.sources) - - tasks.check { - dependsOn(functionalTest) - } - } -} diff --git a/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt b/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt index d73a8b86..0b8d8c63 100644 --- a/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt +++ b/src/functionalTest/kotlin/kotlinx/validation/api/TestDsl.kt @@ -5,12 +5,10 @@ package kotlinx.validation.api +import java.io.* import kotlinx.validation.API_DIR import org.gradle.testkit.runner.GradleRunner import org.intellij.lang.annotations.Language -import java.io.* -import java.io.File -import java.io.InputStreamReader internal fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { val baseKotlinScope = BaseKotlinScope() @@ -42,7 +40,11 @@ internal fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRu /** * same as [file][FileContainer.file], but prepends "src/${sourceSet}/kotlin" before given `classFileName` */ -internal fun FileContainer.kotlin(classFileName: String, sourceSet: String = "main", fn: AppendableScope.() -> Unit) { +internal fun FileContainer.kotlin( + classFileName: String, + sourceSet: String = "main", + fn: AppendableScope.() -> Unit, +) { require(classFileName.endsWith(".kt")) { "ClassFileName must end with '.kt'" } @@ -54,7 +56,11 @@ internal fun FileContainer.kotlin(classFileName: String, sourceSet: String = "ma /** * same as [file][FileContainer.file], but prepends "src/${sourceSet}/java" before given `classFileName` */ -internal fun FileContainer.java(classFileName: String, sourceSet: String = "main", fn: AppendableScope.() -> Unit) { +internal fun FileContainer.java( + classFileName: String, + sourceSet: String = "main", + fn: AppendableScope.() -> Unit, +) { require(classFileName.endsWith(".java")) { "ClassFileName must end with '.java'" }