From d1e8f537e026c9d8c44d696cc30bcf1e4a016d39 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 14 Mar 2023 18:31:36 +0800 Subject: [PATCH 01/28] Adapt the "default-web-frontend-conventions" Plugin into a class Plugin and add an extension to make `htmlGenerationProjectPath` configurable --- .../build.gradle.kts | 29 +++--- .../DefaultWebFrontendConventionsPlugin.kt | 90 +++++++++++++++++++ ...terial-web-frontend-conventions.gradle.kts | 4 +- ...efault-web-frontend-conventions.gradle.kts | 49 ---------- .../main/kotlin/VersionsAndDependencies.kt | 2 +- 5 files changed, 110 insertions(+), 64 deletions(-) create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt delete mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts diff --git a/architecture-common-gradle-plugins/build.gradle.kts b/architecture-common-gradle-plugins/build.gradle.kts index b010bd8..f3140d3 100644 --- a/architecture-common-gradle-plugins/build.gradle.kts +++ b/architecture-common-gradle-plugins/build.gradle.kts @@ -50,23 +50,30 @@ gradlePlugin { "kotlin-multiplatform-jvm-and-js-browser-app-conventions", "Kotlin Multiplatform app conventions with the JVM target and the JS browser target" ) - - scriptConventionsPlugin( - "default-web-frontend-conventions", - "Default web frontend conventions for our projects with Compose for Web and kotlinx.html HTML generation" - ) + run { + val name = "default-web-frontend-conventions" + create(name) { + id = "$`package`.$name" + implementationClass = "$`package`.DefaultWebFrontendConventionsPlugin" + displayName = "Default web frontend conventions" + description = + "Default web frontend conventions for our projects with Compose for Web and kotlinx.html HTML generation" + } + } // TODO scriptConventionsPlugin( "default-material-web-frontend-conventions", "(not implemented yet) Default web frontend conventions for our projects with Compose for Web, kotlinx.html HTML generation, and Material Design" ) - val name = "generate-kotlin-js-browser-webroot-for-vertx-web" - create(name) { - id = "$`package`.$name" - implementationClass = "$`package`.GenerateKotlinJsBrowserWebrootForVertxWebPlugin" - displayName = "Generate Kotlin/JS browser webroot for Vert.x Web" - description = "Generate webroot from a Kotlin/JS subproject with browser target for Vert.x Web" + run { + val name = "generate-kotlin-js-browser-webroot-for-vertx-web" + create(name) { + id = "$`package`.$name" + implementationClass = "$`package`.GenerateKotlinJsBrowserWebrootForVertxWebPlugin" + displayName = "Generate Kotlin/JS browser webroot for Vert.x Web" + description = "Generate webroot from a Kotlin/JS subproject with browser target for Vert.x Web" + } } } } diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt new file mode 100644 index 0000000..0413fb6 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt @@ -0,0 +1,90 @@ +package com.huanshankeji + +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.api.tasks.JavaExec +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.maven +import org.gradle.kotlin.dsl.repositories +import org.jetbrains.compose.ComposePlugin.Dependencies +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet + +class DefaultWebFrontendConventionsPlugin : Plugin { + interface Extension { + val htmlGenerationProjectPath: Property + } + + override fun apply(target: Project) = target.run { + plugins.apply("com.huanshankeji.kotlin-multiplatform-js-browser-app-conventions") + plugins.apply("org.jetbrains.compose") + + + val extension = extensions.create("defaultWebFrontendConventions") + + repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + + kotlin { + js(IR) { + browser { + commonWebpackConfig { + outputFileName = "app.js" + } + testTask { + testLogging.showStandardStreams = true + useKarma { + useChromeHeadless() + useFirefox() + } + } + } + binaries.executable() + } + sourceSets { + named("jsMain") { + dependencies { + implementation(compose.web.core) + implementation(compose.runtime) + } + } + } + } + + + val generatedResourcesFile = buildDir.resolve("generatedResources") + + tasks.named("jsProcessResources") { + val htmlGenerationRun = tasks.getByPath( + extension.htmlGenerationProjectPath.getOrElse(project.path + ":html-generation") + ":run" + ) as JavaExec + htmlGenerationRun.args(generatedResourcesFile.absolutePath) + dependsOn(htmlGenerationRun) + } + + kotlin.sourceSets { get("jsMain").resources.srcDir(generatedResourcesFile) } + } + + + // copied and adapted from generated code + + fun Project.kotlin(configure: Action) = + extensions.configure("kotlin", configure) + + val Project.kotlin: KotlinMultiplatformExtension + get() = extensions.getByName("kotlin") as KotlinMultiplatformExtension + + fun KotlinMultiplatformExtension.sourceSets(configure: Action>): Unit = + (this as ExtensionAware).extensions.configure("sourceSets", configure) + + val KotlinMultiplatformExtension.compose: Dependencies + get() = (this as ExtensionAware).extensions.getByName("compose") as Dependencies +} \ No newline at end of file diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts index b006172..8ed3a0d 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts @@ -1,7 +1,5 @@ package com.huanshankeji -plugins { - id("com.huanshankeji.default-web-frontend-conventions") -} +apply(DefaultWebFrontendConventionsPlugin::class) // TODO diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts deleted file mode 100644 index ec175b8..0000000 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts +++ /dev/null @@ -1,49 +0,0 @@ -package com.huanshankeji - -plugins { - id("com.huanshankeji.kotlin-multiplatform-js-browser-app-conventions") - id("org.jetbrains.compose") -} - -repositories { - google() - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") -} - -kotlin { - js(IR) { - browser { - commonWebpackConfig { - outputFileName = "app.js" - } - testTask { - testLogging.showStandardStreams = true - useKarma { - useChromeHeadless() - useFirefox() - } - } - } - binaries.executable() - } - sourceSets { - val jsMain by getting { - dependencies { - implementation(compose.web.core) - implementation(compose.runtime) - } - } - } -} - - -val generatedResourcesFile = buildDir.resolve("generatedResources") - -tasks.named("jsProcessResources") { - val htmlGenerationRun = tasks.getByPath(project.path + ":html-generation:run") as JavaExec - htmlGenerationRun.args(generatedResourcesFile.absolutePath) - dependsOn(htmlGenerationRun) -} - -kotlin.sourceSets { get("jsMain").resources.srcDir(generatedResourcesFile) } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 0a1d1d8..3a51e27 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -6,7 +6,7 @@ val commonGradleClasspathDependencies = CommonGradleClasspathDependencies(common val kotlinVersion = "1.8.10" // for Compose 1.3.1 -val alignedPluginVersion = "0.4.1-SNAPSHOT" +val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. From ca625854d4957784c14bfc0a9f442d87709db93d Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 14 Mar 2023 18:47:25 +0800 Subject: [PATCH 02/28] Partially revert the plugin changed in the previous commit back into a script plugin Revert "Adapt the "default-web-frontend-conventions" Plugin into a class Plugin and add an extension to make `htmlGenerationProjectPath` configurable" This reverts commit d1e8f537e026c9d8c44d696cc30bcf1e4a016d39. --- .../build.gradle.kts | 15 ++-- .../DefaultWebFrontendConventionsPlugin.kt | 90 ------------------- ...terial-web-frontend-conventions.gradle.kts | 4 +- ...efault-web-frontend-conventions.gradle.kts | 57 ++++++++++++ 4 files changed, 65 insertions(+), 101 deletions(-) delete mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts diff --git a/architecture-common-gradle-plugins/build.gradle.kts b/architecture-common-gradle-plugins/build.gradle.kts index f3140d3..56a72a5 100644 --- a/architecture-common-gradle-plugins/build.gradle.kts +++ b/architecture-common-gradle-plugins/build.gradle.kts @@ -50,16 +50,11 @@ gradlePlugin { "kotlin-multiplatform-jvm-and-js-browser-app-conventions", "Kotlin Multiplatform app conventions with the JVM target and the JS browser target" ) - run { - val name = "default-web-frontend-conventions" - create(name) { - id = "$`package`.$name" - implementationClass = "$`package`.DefaultWebFrontendConventionsPlugin" - displayName = "Default web frontend conventions" - description = - "Default web frontend conventions for our projects with Compose for Web and kotlinx.html HTML generation" - } - } + + scriptConventionsPlugin( + "default-web-frontend-conventions", + "Default web frontend conventions for our projects with Compose for Web and kotlinx.html HTML generation" + ) // TODO scriptConventionsPlugin( "default-material-web-frontend-conventions", diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt deleted file mode 100644 index 0413fb6..0000000 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/DefaultWebFrontendConventionsPlugin.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.huanshankeji - -import org.gradle.api.Action -import org.gradle.api.NamedDomainObjectContainer -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.plugins.ExtensionAware -import org.gradle.api.provider.Property -import org.gradle.api.tasks.JavaExec -import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.maven -import org.gradle.kotlin.dsl.repositories -import org.jetbrains.compose.ComposePlugin.Dependencies -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet - -class DefaultWebFrontendConventionsPlugin : Plugin { - interface Extension { - val htmlGenerationProjectPath: Property - } - - override fun apply(target: Project) = target.run { - plugins.apply("com.huanshankeji.kotlin-multiplatform-js-browser-app-conventions") - plugins.apply("org.jetbrains.compose") - - - val extension = extensions.create("defaultWebFrontendConventions") - - repositories { - google() - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") - } - - kotlin { - js(IR) { - browser { - commonWebpackConfig { - outputFileName = "app.js" - } - testTask { - testLogging.showStandardStreams = true - useKarma { - useChromeHeadless() - useFirefox() - } - } - } - binaries.executable() - } - sourceSets { - named("jsMain") { - dependencies { - implementation(compose.web.core) - implementation(compose.runtime) - } - } - } - } - - - val generatedResourcesFile = buildDir.resolve("generatedResources") - - tasks.named("jsProcessResources") { - val htmlGenerationRun = tasks.getByPath( - extension.htmlGenerationProjectPath.getOrElse(project.path + ":html-generation") + ":run" - ) as JavaExec - htmlGenerationRun.args(generatedResourcesFile.absolutePath) - dependsOn(htmlGenerationRun) - } - - kotlin.sourceSets { get("jsMain").resources.srcDir(generatedResourcesFile) } - } - - - // copied and adapted from generated code - - fun Project.kotlin(configure: Action) = - extensions.configure("kotlin", configure) - - val Project.kotlin: KotlinMultiplatformExtension - get() = extensions.getByName("kotlin") as KotlinMultiplatformExtension - - fun KotlinMultiplatformExtension.sourceSets(configure: Action>): Unit = - (this as ExtensionAware).extensions.configure("sourceSets", configure) - - val KotlinMultiplatformExtension.compose: Dependencies - get() = (this as ExtensionAware).extensions.getByName("compose") as Dependencies -} \ No newline at end of file diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts index 8ed3a0d..b006172 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-material-web-frontend-conventions.gradle.kts @@ -1,5 +1,7 @@ package com.huanshankeji -apply(DefaultWebFrontendConventionsPlugin::class) +plugins { + id("com.huanshankeji.default-web-frontend-conventions") +} // TODO diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts new file mode 100644 index 0000000..198ab45 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts @@ -0,0 +1,57 @@ +package com.huanshankeji + +plugins { + id("com.huanshankeji.kotlin-multiplatform-js-browser-app-conventions") + id("org.jetbrains.compose") +} + +interface Extension { + val htmlGenerationProjectPath: Property +} + +val extension = extensions.create("defaultWebFrontendConventions") + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +kotlin { + js(IR) { + browser { + commonWebpackConfig { + outputFileName = "app.js" + } + testTask { + testLogging.showStandardStreams = true + useKarma { + useChromeHeadless() + useFirefox() + } + } + } + binaries.executable() + } + sourceSets { + jsMain { + dependencies { + implementation(compose.web.core) + implementation(compose.runtime) + } + } + } +} + + +val generatedResourcesFile = buildDir.resolve("generatedResources") + +tasks.named("jsProcessResources") { + val htmlGenerationRun = tasks.getByPath( + extension.htmlGenerationProjectPath.getOrElse(project.path + ":html-generation") + ":run" + ) as JavaExec + htmlGenerationRun.args(generatedResourcesFile.absolutePath) + dependsOn(htmlGenerationRun) +} + +kotlin.sourceSets { get("jsMain").resources.srcDir(generatedResourcesFile) } From 36bedd014c5af51ff6d9edc439ba0569a246c9fd Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 11 May 2023 17:47:04 +0800 Subject: [PATCH 03/28] Make adopting `CommonGradleClasspathDependencies.SerializationPlugin` slightly more straightforward --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- .../huanshankeji/CommonGradleClasspathDependencies.kt | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 3a51e27..3a7081d 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -10,7 +10,7 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. -val commonGradleDependenciesVersion = "0.5.0-20230310-SNAPSHOT" +val commonGradleDependenciesVersion = "0.6.0-20230310-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.5.0-20230310".apply { diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt index 5f5f86b..3920b80 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt @@ -1,5 +1,6 @@ package com.huanshankeji +import org.gradle.kotlin.dsl.kotlin import org.gradle.plugin.use.PluginDependenciesSpec class CommonGradleClasspathDependencies(val versions: CommonVersions) { @@ -8,7 +9,13 @@ class CommonGradleClasspathDependencies(val versions: CommonVersions) { inner class SerializationPlugin internal constructor() { val moduleName = "plugin.serialization" - val version get() = versions.kotlin + val defaultVersion get() = versions.kotlin + + fun PluginDependenciesSpec.applyPluginWithoutVersion() = + kotlin(moduleName) + + fun PluginDependenciesSpec.applyPluginWithVersion(version: String = defaultVersion) = + applyPluginWithoutVersion().version(version) } val serializationPlugin = SerializationPlugin() From e0f8b5ecb14c45e69e67dcab4945ee39f860ac8e Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Fri, 9 Jun 2023 15:26:09 +0800 Subject: [PATCH 04/28] Add the "reflect" module in "kotlin-common" --- .../src/main/kotlin/com/huanshankeji/CommonDependencies.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt index 51f3126..944d305 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt @@ -15,6 +15,7 @@ class CommonDependencies(val versions: CommonVersions = CommonVersions()) { fun arrow(version: String = defaultVersion) = module("arrow", version) fun coroutines(version: String = defaultVersion) = module("coroutines", version) fun exposed(version: String = defaultVersion) = module("exposed", version) + fun reflect(version: String = defaultVersion) = module("reflect", version) inner class Ktor internal constructor() { fun module(module: String, version: String = defaultVersion) = From 6c80b8a3f855caa47a2f8763f7b75945898956dd Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Fri, 9 Jun 2023 16:00:52 +0800 Subject: [PATCH 05/28] Bump the Gradle wrapper and all dependency versions --- buildSrc/build.gradle.kts | 6 ++--- .../main/kotlin/VersionsAndDependencies.kt | 2 +- .../kotlin/com/huanshankeji/CommonVersions.kt | 24 +++++++++---------- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bb27cda..5ffe532 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,6 +1,6 @@ plugins { `kotlin-dsl` - // Gradle 8.0.2's dependent Kotlin version is 1.8.10. + // Gradle 8.1.1's dependent Kotlin version is 1.8.10. //kotlin("jvm") version "1.8.10" } @@ -25,9 +25,9 @@ dependencies { } */ //implementation(kotlin("gradle-plugin", "1.8.10")) // for Compose 1.3.1 - implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.0.6") // This version has to be used for Gradle 8.0.1. + implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.0.7") // This version has to be used for Gradle 8.1.1. - implementation("com.gradle.publish:plugin-publish-plugin:1.1.0") + implementation("com.gradle.publish:plugin-publish-plugin:1.2.0") // This is a bootstrapping dependency (cross-version self-dependency). Try not to update its version unless necessary. implementation("com.huanshankeji.team:gradle-plugins:0.3.0") { exclude("org.jetbrains.kotlin") } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 3a7081d..c6532b8 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -10,7 +10,7 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. -val commonGradleDependenciesVersion = "0.6.0-20230310-SNAPSHOT" +val commonGradleDependenciesVersion = "0.6.0-20230609-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.5.0-20230310".apply { diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt index 88441bc..7dadd14 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt @@ -5,20 +5,20 @@ import kotlinVersion class CommonVersions( val kotlin: String = kotlinVersion, - val kotlinCommon: String = "0.2.1", + val kotlinCommon: String = "0.3.0", - val kotlinxCoroutines: String = "1.6.4", + val kotlinxCoroutines: String = "1.7.1", val kotlinxHtml: String = "0.8.0", - val kotlinxSerialization: String = "1.4.0-RC", + val kotlinxSerialization: String = "1.5.1", val kotlinxDatetime: String = "0.4.0", - val exposed: String = "0.38.2", - val ktor: String = "2.0.3", - val composeMultiplatform: String = "1.3.1", + val exposed: String = "0.41.1", + val ktor: String = "2.3.1", + val composeMultiplatform: String = "1.4.0", - val vertx: String = "4.3.2", - val arrow: String = "1.1.2", - val orgJunit: String = "5.9.0", - val kotest: String = "5.4.0", - val postgreSql: String = "42.4.0", - val slf4j: String = "1.7.36", + val vertx: String = "4.4.3", + val arrow: String = "1.2.0-RC", + val orgJunit: String = "5.9.3", + val kotest: String = "5.6.2", + val postgreSql: String = "42.5.4", + val slf4j: String = "1.7.36", // TODO: consider replacing with kotlin-logging (https://github.com/oshai/kotlin-logging) ) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 761b8f0..8707e8b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 90761712faae851d1ba94673da4c45717c108541 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Fri, 9 Jun 2023 18:27:52 +0800 Subject: [PATCH 06/28] Bump another `kotlinVersion` left out --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index c6532b8..8f6b57e 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -4,7 +4,7 @@ import com.huanshankeji.CommonVersions val commonVersions = CommonVersions() val commonGradleClasspathDependencies = CommonGradleClasspathDependencies(commonVersions) -val kotlinVersion = "1.8.10" // for Compose 1.3.1 +val kotlinVersion = "1.8.20" // for Compose 1.4.0 val alignedPluginVersion = "0.5.0-SNAPSHOT" @@ -15,4 +15,4 @@ val commonGradleDependenciesVersion = "0.6.0-20230609-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.5.0-20230310".apply { require(!endsWith("SNAPSHOT")) -} +} // TODO: update From 7598255d63295c483e7884be684ee07a6711314d Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Fri, 9 Jun 2023 18:35:23 +0800 Subject: [PATCH 07/28] Update `pluginProjectDependentStableCommonGradleDependenciesVersion` --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 8f6b57e..e2ddfed 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,6 +13,6 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.6.0-20230609-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.5.0-20230310".apply { +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.6.0-20230609".apply { require(!endsWith("SNAPSHOT")) -} // TODO: update +} From 268d1e448d7765ed2abf8c65acb0a1155f303d39 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 17 Jun 2023 15:38:14 +0800 Subject: [PATCH 08/28] Make an error message more detailed --- .../kotlin-jvm-library-maven-publish-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/kotlin-jvm-library-maven-publish-conventions.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/kotlin-jvm-library-maven-publish-conventions.gradle.kts index c7b3b12..b3ad03f 100644 --- a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/kotlin-jvm-library-maven-publish-conventions.gradle.kts +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/kotlin-jvm-library-maven-publish-conventions.gradle.kts @@ -20,5 +20,5 @@ publishing { } afterEvaluate { - require(java.toolchain.languageVersion.isPresent) { "Specify an explicit `java.toolchain.languageVersion` when publishing a JVM library." } + require(java.toolchain.languageVersion.isPresent) { "Specify an explicit `java.toolchain.languageVersion` (or via `kotlin.jvmToolchain()`) when publishing a JVM library." } } From ae2cfc16d6f8143998c6aa2ad2004e255a7e33f3 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 8 Jul 2023 19:23:29 +0800 Subject: [PATCH 09/28] Add kotlinx.benchmark and Testcontainers to the common dependencies and refactor the Kotlin Serialization plugin in it A corrected commit data for snapshot dependencies to be analyzable: Jul 4, 2023 0:00am GMT+0800 --- .../main/kotlin/VersionsAndDependencies.kt | 7 ++-- .../com/huanshankeji/CommonDependencies.kt | 29 +++++++++++++++ .../CommonGradleClasspathDependencies.kt | 35 ++++++++++++++----- .../kotlin/com/huanshankeji/CommonVersions.kt | 4 ++- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index e2ddfed..50f6f04 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -10,9 +10,10 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. -val commonGradleDependenciesVersion = "0.6.0-20230609-SNAPSHOT" +val commonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.6.0-20230609".apply { - require(!endsWith("SNAPSHOT")) +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT".apply { + // TODO: temporararily commented out for debugging purposes + //require(!endsWith("SNAPSHOT")) } diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt index 944d305..b13aae1 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonDependencies.kt @@ -76,6 +76,17 @@ class CommonDependencies(val versions: CommonVersions = CommonVersions()) { fun datetime(version: String = versions.kotlinxDatetime) = kotlinx("datetime", version) + + inner class Benchmark internal constructor() { + val defaultVersion = versions.kotlinxBenchmark + fun module(module: String, version: String = defaultVersion) = + kotlinx("benchmark-$module", version) + + fun runtime(version: String = defaultVersion) = + module("runtime", version) + } + + val benchmark = Benchmark() } val kotlinx = Kotlinx() @@ -211,4 +222,22 @@ class CommonDependencies(val versions: CommonVersions = CommonVersions()) { } val slf4j = Slf4j() + + inner class TestContainers internal constructor() { + val defaultVersion = versions.testContainers + fun moduleWithoutVersion(module: String) = + "org.testcontainers:$module" + + fun moduleWithVersion(module: String, version: String = defaultVersion) = + "${moduleWithoutVersion(module)}:$version" + + fun DependencyHandler.platformBom(version: String = defaultVersion) = + platform(moduleWithVersion("testcontainers-bom", version)) + + val testContainers = moduleWithoutVersion("testcontainers") + val junitJupiter = moduleWithoutVersion("junit-jupiter") + val postgreSql = moduleWithoutVersion("postgresql") + } + + val testContainers = TestContainers() } \ No newline at end of file diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt index 3920b80..a79291f 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt @@ -7,18 +7,25 @@ class CommonGradleClasspathDependencies(val versions: CommonVersions) { inner class Kotlin internal constructor() { val group = "org.jetbrains.kotlin" - inner class SerializationPlugin internal constructor() { - val moduleName = "plugin.serialization" - val defaultVersion get() = versions.kotlin + inner class Plugin internal constructor() { + inner class Serialization internal constructor() { + val moduleName = "plugin.serialization" + val defaultVersion get() = versions.kotlin - fun PluginDependenciesSpec.applyPluginWithoutVersion() = - kotlin(moduleName) + fun PluginDependenciesSpec.applyPluginWithoutVersion() = + kotlin(moduleName) - fun PluginDependenciesSpec.applyPluginWithVersion(version: String = defaultVersion) = - applyPluginWithoutVersion().version(version) + fun PluginDependenciesSpec.applyPluginWithVersion(version: String = defaultVersion) = + applyPluginWithoutVersion().version(version) + } + + val serialization = Serialization() } - val serializationPlugin = SerializationPlugin() + val plugin = Plugin() + + @Deprecated("Use `plugin.serialization` instead.") + val serializationPlugin = plugin.serialization } val kotlin = Kotlin() @@ -41,4 +48,16 @@ class CommonGradleClasspathDependencies(val versions: CommonVersions) { } val composeMultiplatform = ComposeMultiplatform() + + inner class Kotlinx internal constructor() { + inner class Benchmark { + val defaultVersion = versions.kotlinxBenchmark + fun PluginDependenciesSpec.applyPluginWithVersion(version: String = defaultVersion) = + id("org.jetbrains.kotlinx.benchmark").version(defaultVersion) + } + + val benchmark = Benchmark() + } + + val kotlinx = Kotlinx() } diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt index 7dadd14..e673812 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt @@ -2,7 +2,7 @@ package com.huanshankeji import kotlinVersion -class CommonVersions( +class CommonVersions @JvmOverloads constructor( val kotlin: String = kotlinVersion, val kotlinCommon: String = "0.3.0", @@ -11,6 +11,7 @@ class CommonVersions( val kotlinxHtml: String = "0.8.0", val kotlinxSerialization: String = "1.5.1", val kotlinxDatetime: String = "0.4.0", + val kotlinxBenchmark: String = "0.4.8", val exposed: String = "0.41.1", val ktor: String = "2.3.1", val composeMultiplatform: String = "1.4.0", @@ -21,4 +22,5 @@ class CommonVersions( val kotest: String = "5.6.2", val postgreSql: String = "42.5.4", val slf4j: String = "1.7.36", // TODO: consider replacing with kotlin-logging (https://github.com/oshai/kotlin-logging) + val testContainers: String = "1.18.3" ) \ No newline at end of file From 6f4f3cf9fbc6ab3c3a5b72fccdc9429ab44422af Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 8 Jul 2023 20:47:33 +0800 Subject: [PATCH 10/28] Revert "Add kotlinx.benchmark and Testcontainers to the common dependencies and refactor the Kotlin Serialization plugin in it" to enable the project to build and publish This reverts commit ae2cfc16d6f8143998c6aa2ad2004e255a7e33f3. A corrected commit data for snapshot dependencies to be analyzable: Jul 4, 2023 0:00am GMT+0800 --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 50f6f04..319560a 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,7 +13,6 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT".apply { - // TODO: temporararily commented out for debugging purposes - //require(!endsWith("SNAPSHOT")) +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.6.0-20230609".apply { + require(!endsWith("SNAPSHOT")) } From 08508084339634c5bd1316b9ba7cf488f6edcc37 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 8 Jul 2023 22:01:48 +0800 Subject: [PATCH 11/28] Revert "Revert "Add kotlinx.benchmark and Testcontainers to the common dependencies and refactor the Kotlin Serialization plugin in it" to enable the project to build and publish" to make dependent projects compatible with Compose Multiplatform 1.4.0 This reverts commit 6f4f3cf9fbc6ab3c3a5b72fccdc9429ab44422af. A corrected commit date for snapshot dependencies to be analyzable: Jul 4, 2023 0:00am GMT+0800 --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 319560a..50f6f04 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,6 +13,7 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.6.0-20230609".apply { - require(!endsWith("SNAPSHOT")) +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT".apply { + // TODO: temporararily commented out for debugging purposes + //require(!endsWith("SNAPSHOT")) } From 863074311fd8bc19ccc1bac0c8280d6bdbec9b47 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 11 Nov 2023 10:00:50 +0800 Subject: [PATCH 12/28] Bump the Gradle wrapper to 8.4 Java 17 has to be used. --- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 29 +++++++++++++---------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 38440 zcmZ6SQ*<3%6s?oSw$a$O+t{{kpWwtNwr$(CZL_f&H8y*DImUnA_rrded+oL6w`Tc1 z#Mv!G{V@^ba0IDo(ixEwh?a02n}!DKIvnc^%1aoMq1+#kAhJLP_oX|jgEPsr;V`0k zL+b-2$5Bo(3c}Buz&-hcO_SO<6900dXYt$Ter6+Mad;kQkW8)Rc`!+|ia-jh^V%qoFb5JQ`

cw$=gQbRBDL9uPo|6z1d&0P$^g)iXXq+aG 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 CEjxVx delta 36364 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%0Dd@gG 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 Pg;r%T9-eUK&({9{Yi$yT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8707e8b..8838ba9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d4..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From b8876eaba17be7f58ee3a6ec6455631953f15273 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Sat, 11 Nov 2023 10:05:58 +0800 Subject: [PATCH 13/28] Bump the version of `org.gradle.kotlin:gradle-kotlin-dsl-plugins` along with Gradle --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 5ffe532..ea714aa 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { } */ //implementation(kotlin("gradle-plugin", "1.8.10")) // for Compose 1.3.1 - implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.0.7") // This version has to be used for Gradle 8.1.1. + implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.1.2") // This version has to be used for Gradle 8.4. implementation("com.gradle.publish:plugin-publish-plugin:1.2.0") From b9c46986582a78549d57ea9076684369b93929d9 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Mon, 13 Nov 2023 01:10:37 +0800 Subject: [PATCH 14/28] Update the common dependency versions --- .../main/kotlin/VersionsAndDependencies.kt | 4 +-- .../kotlin/com/huanshankeji/CommonVersions.kt | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 50f6f04..804f949 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -4,13 +4,13 @@ import com.huanshankeji.CommonVersions val commonVersions = CommonVersions() val commonGradleClasspathDependencies = CommonGradleClasspathDependencies(commonVersions) -val kotlinVersion = "1.8.20" // for Compose 1.4.0 +val kotlinVersion = "1.9.20" // for Compose 1.4.0 // TODO remove this comment val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. -val commonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT" +val commonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT".apply { diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt index e673812..7d512c7 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt @@ -7,20 +7,20 @@ class CommonVersions @JvmOverloads constructor( val kotlinCommon: String = "0.3.0", - val kotlinxCoroutines: String = "1.7.1", - val kotlinxHtml: String = "0.8.0", - val kotlinxSerialization: String = "1.5.1", - val kotlinxDatetime: String = "0.4.0", - val kotlinxBenchmark: String = "0.4.8", - val exposed: String = "0.41.1", - val ktor: String = "2.3.1", - val composeMultiplatform: String = "1.4.0", + val kotlinxCoroutines: String = "1.7.3", + val kotlinxHtml: String = "0.9.1", + val kotlinxSerialization: String = "1.6.0", + val kotlinxDatetime: String = "0.4.1", + val kotlinxBenchmark: String = "0.4.9", + val exposed: String = "0.44.1", + val ktor: String = "2.3.6", + val composeMultiplatform: String = "1.5.10", // this is usually only used in classpath dependencies - val vertx: String = "4.4.3", - val arrow: String = "1.2.0-RC", - val orgJunit: String = "5.9.3", - val kotest: String = "5.6.2", - val postgreSql: String = "42.5.4", + val vertx: String = "4.4.6", + val arrow: String = "1.2.1", + val orgJunit: String = "5.10.1", + val kotest: String = "5.8.0", + val postgreSql: String = "42.6.0", val slf4j: String = "1.7.36", // TODO: consider replacing with kotlin-logging (https://github.com/oshai/kotlin-logging) - val testContainers: String = "1.18.3" + val testContainers: String = "1.19.1" ) \ No newline at end of file From b806e22489c08051685da0b02c9ad6ec5ff94fc1 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Mon, 13 Nov 2023 01:11:11 +0800 Subject: [PATCH 15/28] Update `pluginProjectDependentStableCommonGradleDependenciesVersion` --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 804f949..e577f3b 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,7 +13,7 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20230621-SNAPSHOT".apply { +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT".apply { // TODO: temporararily commented out for debugging purposes //require(!endsWith("SNAPSHOT")) } From 2d13c10af6ca02791314c9d561202466bd2e9696 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Mon, 13 Nov 2023 03:54:40 +0800 Subject: [PATCH 16/28] Update `pluginProjectDependentStableCommonGradleDependenciesVersion` again to not use the snapshot --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index e577f3b..3694006 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,7 +13,6 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT".apply { - // TODO: temporararily commented out for debugging purposes - //require(!endsWith("SNAPSHOT")) +val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20231111".apply { + require(!endsWith("SNAPSHOT")) } From a90e0c7275b9a109fa4730f6b4b4bf0a036b522b Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Mon, 13 Nov 2023 03:54:59 +0800 Subject: [PATCH 17/28] Bump the Gradle publish plugin to 1.2.1 --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index ea714aa..f19667e 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -27,7 +27,7 @@ dependencies { //implementation(kotlin("gradle-plugin", "1.8.10")) // for Compose 1.3.1 implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.1.2") // This version has to be used for Gradle 8.4. - implementation("com.gradle.publish:plugin-publish-plugin:1.2.0") + implementation("com.gradle.publish:plugin-publish-plugin:1.2.1") // This is a bootstrapping dependency (cross-version self-dependency). Try not to update its version unless necessary. implementation("com.huanshankeji.team:gradle-plugins:0.3.0") { exclude("org.jetbrains.kotlin") } From d873deabe3cfba0ad7bcb5c2f5490b33489e3caf Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 14 Nov 2023 05:23:23 +0800 Subject: [PATCH 18/28] Bump the bootstrapping "common-gradle-dependencies" version to 0.7.0-20231111 --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index f19667e..e221c7e 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -32,5 +32,5 @@ dependencies { // This is a bootstrapping dependency (cross-version self-dependency). Try not to update its version unless necessary. implementation("com.huanshankeji.team:gradle-plugins:0.3.0") { exclude("org.jetbrains.kotlin") } // This is also a bootstrapping dependency. - implementation("com.huanshankeji:common-gradle-dependencies:0.5.0-20230310") { exclude("org.jetbrains.kotlin") } + implementation("com.huanshankeji:common-gradle-dependencies:0.7.0-20231111") { exclude("org.jetbrains.kotlin") } } From d16fbb11fe8d59ba8b096e2b478fca74f1e55a6f Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Fri, 17 Nov 2023 00:19:25 +0800 Subject: [PATCH 19/28] Improve a deprecation annotation --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- .../com/huanshankeji/CommonGradleClasspathDependencies.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 3694006..f1c8bda 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -10,7 +10,7 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" // "x.y.z" indicates the version of the way of organizing the code, // and the date indicates the version when the dependency versions are updated. -val commonGradleDependenciesVersion = "0.7.0-20231111-SNAPSHOT" +val commonGradleDependenciesVersion = "0.7.1-20231111-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20231111".apply { diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt index a79291f..4d761a3 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt @@ -24,7 +24,7 @@ class CommonGradleClasspathDependencies(val versions: CommonVersions) { val plugin = Plugin() - @Deprecated("Use `plugin.serialization` instead.") + @Deprecated("Use `plugin.serialization` instead.", ReplaceWith("plugin.serialization")) val serializationPlugin = plugin.serialization } From 41e27e776b8fc75b8726c9b1505789515f2881d9 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 23 Nov 2023 15:18:22 +0800 Subject: [PATCH 20/28] Add support functions and plugins for feature variants for different operating systems and CPU architectures `./gradlew build` (and tasks depending on it) somehow has to run twice to work. I haven't identified the cause yet. --- README.md | 1 + .../build.gradle.kts | 6 + .../jvm/native/osandarch/Distributions.kt | 3 + .../FeatureVariantsAndDependencies.kt | 223 ++++++++++++++++++ .../jvm/native/osandarch/OsAndArch.kt | 48 ++++ .../register-feature-variants.gradle.kts | 13 + .../com/huanshankeji/FeatureVariants.kt | 26 ++ ...jvm-test-common-feature-variant.gradle.kts | 14 +- 8 files changed, 321 insertions(+), 13 deletions(-) create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/Distributions.kt create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt diff --git a/README.md b/README.md index 51c9da7..a5b011b 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,4 @@ configurations.all { ## Developer notices 1. IntelliJ IDEA doesn't work well with applying plugins to script plugins in project sources. If a script plugin's code does not resolve, try restarting IntelliJ IDEA. +1. `./gradlew build` (and tasks depending on it) somehow has to run twice to work. I haven't identified the cause yet. diff --git a/architecture-common-gradle-plugins/build.gradle.kts b/architecture-common-gradle-plugins/build.gradle.kts index 56a72a5..003efd6 100644 --- a/architecture-common-gradle-plugins/build.gradle.kts +++ b/architecture-common-gradle-plugins/build.gradle.kts @@ -70,5 +70,11 @@ gradlePlugin { description = "Generate webroot from a Kotlin/JS subproject with browser target for Vert.x Web" } } + + scriptConventionsPlugin( + "jvm.native.osandarch.register-feature-variants", + "Register the OS and architecture feature variants", + "Registers feature variants for different operating systems (Linux, Windows, macOS) and CPU architectures." + ) } } diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/Distributions.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/Distributions.kt new file mode 100644 index 0000000..7aaf284 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/Distributions.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.jvm.native.osandarch + +// not implemented yet diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt new file mode 100644 index 0000000..adafb92 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt @@ -0,0 +1,223 @@ +package com.huanshankeji.jvm.native.osandarch + +import com.huanshankeji.jvm.native.osandarch.Os.* +import com.huanshankeji.jvm.native.osandarch.SourceSetType.Main +import com.huanshankeji.jvm.native.osandarch.SourceSetType.RegisterSeparate +import com.huanshankeji.registerFeatureVariantWithNewSourceSet +import com.huanshankeji.registerFeatureVariantWithSourceSet +import gradle.kotlin.dsl.accessors._4d764c3096d994e4212b8a01328470fd.runtimeOnly +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.kotlin.dsl.DependencyHandlerScope +import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo +import org.gradle.kotlin.dsl.get + +val featureVariantNameCharRegex = Regex("[a-zA-Z0-9]") +val featureVariantNameRegex = Regex("${featureVariantNameCharRegex.pattern}+") +fun String.toFeatureVariantName() = + featureVariantNameCharRegex.findAll(this).map { it.value }.joinToString("") + +fun String.isValidFeatureVariantName() = + matches(featureVariantNameRegex) + +fun String.camelCaseToKebabCase() = + replace(Regex("[A-Z]")) { "-${it.value.lowercase()}" } + + +fun String.capitalizeFirstChar() = + replaceFirstChar { it.uppercaseChar() } + + +infix fun String.camelCaseConcat(other: String) = + this + other.capitalizeFirstChar() + + +fun OsAndArch.getFeatureVariantName() = + os.featureVariantName camelCaseConcat arch.featureVariantName + + +/** + * @param featureVariantName usually in camel case + * @param dependencyIdentifier usually in kebab case (sometimes mixed with snake case, for example "X86_64") + */ +data class Config(val featureVariantName: String, val dependencyIdentifier: String) // TODO classifier + +fun Pair>.toConfigSequence(): Sequence { + val (os, archs) = this + return archs.asSequence().map { arch -> + Config(os.featureVariantName camelCaseConcat arch.featureVariantName, "${os.identifier}-${arch.identifier}") + } +} + +fun Pair>.toConfigs() = + toConfigSequence().toList() + +object Configs { + /* + // TODO use `entries` when the language version is bumped to 1.9 + val ofAllOss = Os.values().map { + Config(it.featureVariantName, it.identifier) + } + */ + + private fun Os.getConfigs() = + (this to supportedOsArchMap.getValue(this)).toConfigs() + + val linux = Linux.getConfigs() + val windows = Windows.getConfigs() + val macos = Macos.getConfigs() + + val all = supportedOsArchs.flatMap { it.toConfigSequence() } +} + +enum class SourceSetType { + Main, RegisterSeparate +} + +fun JavaPluginExtension.registerFeatureVariants(sourceSetType: SourceSetType) { + when (sourceSetType) { + Main -> { + val mainSourceSet = sourceSets["main"] + for (config in Configs.all) + registerFeatureVariantWithSourceSet(config.featureVariantName, mainSourceSet) + } + + RegisterSeparate -> + for (config in Configs.all) + registerFeatureVariantWithNewSourceSet(config.featureVariantName) + } +} + +fun DependencyHandlerScope.addAllFeatureVariantDependencies( + featureVariantNames: List, targetConfigurationType: String, dependencyNotation: Any +) { + for (featureVariantName in featureVariantNames) + add(featureVariantName camelCaseConcat targetConfigurationType, dependencyNotation) +} + + +// TODO some functions related to feature variants can be extracted to a separate feature variant package + +/** + * @param configs use a predefined one in [Configs] or make your own with [Config] + * @param targetConfigurationType the type of the dependency configuration + * (see https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:what-are-dependency-configurations + * and https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) + */ +fun DependencyHandlerScope.addAllFeatureVariantDependenciesWithIdentifiersInNameSuffixes( + configs: List, + targetConfigurationType: String, + group: String, + namePrefix: String, + version: String? = null +) { + for ((featureVariantName, dependencyIdentifier) in configs) + addExternalModuleDependencyTo( + this, + featureVariantName + targetConfigurationType.capitalizeFirstChar(), + group, "$namePrefix-$dependencyIdentifier", version, + null, null, null, null + ) +} + +/** + * @param configs use a predefined one in [Configs] or make your own with [Config] + * @param targetConfigurationType the type of the dependency configuration + * (see https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:what-are-dependency-configurations + * and https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) + */ +fun DependencyHandlerScope.addAllFeatureVariantDependenciesWithIdentifiersInClassifiers( + configs: List, + targetConfigurationType: String, + group: String, + name: String, + version: String? = null +) { + for ((featureVariantName, dependencyIdentifier) in configs) + addExternalModuleDependencyTo( + this, + featureVariantName + targetConfigurationType.replaceFirstChar { it.uppercaseChar() }, + group, name, version, + null, dependencyIdentifier, null, null + ) +} + + +fun getCapabilityNotation(group: String, name: String, featureVariantName: String) = + "$group:$name-${featureVariantName.camelCaseToKebabCase()}" + +inline fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDependencies( + featureVariantNames: List, targetConfiguration: (featureVariantName: String?) -> String, + group: String, name: String, version: String? = null +) { + addExternalModuleDependencyTo(this, targetConfiguration(null), group, name, version, null, null, null, null) + for (featureVariantName in featureVariantNames) + addExternalModuleDependencyTo( + this, + targetConfiguration(featureVariantName), + group, name, version, + null, null, null + ) { + capabilities { + requireCapability(getCapabilityNotation(group, name, featureVariantName)) + } + } +} + +// TODO remove +/* +fun DependencyHandlerScope.addFeatureVariantTransitiveCapabilityDependencies( + featureVariantNames: List, targetConfigurationType: String, + group: String, name: String, version: String? = null +) { + for (featureVariantName in featureVariantNames) + addExternalModuleDependencyTo( + this, + featureVariantName camelCaseConcat targetConfigurationType, + group, name, version, + null, null, null + ) { + capabilities { + requireCapability(getCapabilityNotation(group, name, featureVariantName)) + } + } +} +*/ + +fun DependencyHandlerScope.addDependencyWithFeatureVariantTransitiveCapabilityDependencies( + featureVariantNames: List, targetConfigurationType: String, + group: String, name: String, version: String? = null +) = + addDependencyWithFeatureVariantCapabilityDependencies( + featureVariantNames, + { featureVariantName -> + featureVariantName?.camelCaseConcat(targetConfigurationType) ?: targetConfigurationType + }, + group, name, version + ) + +/** + * Adds all the feature variant dependencies to the main variant. + * Use this function without the `register-feature-variants` plugin. + */ +fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDependenciesToOneConfiguration( + featureVariantNames: List, targetConfiguration: String, + group: String, name: String, version: String? = null +) = + addDependencyWithFeatureVariantCapabilityDependencies( + featureVariantNames, + { _ -> targetConfiguration }, + group, name, version + ) + +// TODO remove. This function is not necessary as all feature variant dependencies are added when running code and packaging distributions. +fun DependencyHandlerScope.addConventionalDependencyWithOsAndArch( + osAndArchForMain: OsAndArch = getCurrentOsAndArch(), sourceSets: SourceSetContainer, + featureVariantNames: List, targetConfigurationType: String, + group: String, name: String, version: String? = null +) { + addDependencyWithFeatureVariantTransitiveCapabilityDependencies( + featureVariantNames, targetConfigurationType, group, name, version + ) + runtimeOnly(sourceSets[osAndArchForMain.getFeatureVariantName()]) +} diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt new file mode 100644 index 0000000..131a134 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt @@ -0,0 +1,48 @@ +package com.huanshankeji.jvm.native.osandarch + +import com.huanshankeji.jvm.native.osandarch.CpuArchitecture.Aarch64 +import com.huanshankeji.jvm.native.osandarch.CpuArchitecture.X8664 +import com.huanshankeji.jvm.native.osandarch.Os.* +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform +import java.util.* + +// TODO possibly use feature variant name +enum class Os(val identifier: String) { + Linux("linux"), Windows("windows"), Macos("osx"); + + // Note: not using `get()` in an enum here causes initialization errors. + val featureVariantName: String get() = identifier.toFeatureVariantName() +} + +enum class CpuArchitecture(val identifier: String) { + X8664("x86_64"), Aarch64("aarch64"); + + val featureVariantName: String get() = identifier.toFeatureVariantName() +} + + +val supportedOsArchs = listOf( + Linux to listOf(X8664, Aarch64), + Windows to listOf(X8664), + Macos to listOf(X8664, Aarch64) +) +val supportedOsArchMap = EnumMap(supportedOsArchs.toMap()) + +data class OsAndArch(val os: Os, val arch: CpuArchitecture) + +fun getCurrentOsAndArch(): OsAndArch { + val currentOperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + val os = when { + currentOperatingSystem.isLinux -> Linux + currentOperatingSystem.isWindows -> Windows + currentOperatingSystem.isMacOsX -> Macos + else -> throw IllegalArgumentException("Huanshankeji architecture common unsupported operating system: $currentOperatingSystem") + } + val currentArchitecture = DefaultNativePlatform.getCurrentArchitecture() + val arch = when { + currentArchitecture.isAmd64 -> X8664 + currentArchitecture.isArm64 -> Aarch64 + else -> throw IllegalArgumentException("Huanshankeji architecture common unsupported CPU architecture: $currentOperatingSystem") + } + return OsAndArch(os, arch) +} diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts new file mode 100644 index 0000000..dc8b4c9 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts @@ -0,0 +1,13 @@ +package com.huanshankeji.jvm.native.osandarch + +plugins { + java +} + +interface Extension { + val sourceSetType: Property +} + +val extension = extensions.create("registerOsAndArchFeatureVariants") + +java.registerFeatureVariants(extension.sourceSetType.getOrElse(SourceSetType.Main)) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt new file mode 100644 index 0000000..f4dc315 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt @@ -0,0 +1,26 @@ +package com.huanshankeji + +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.get + +fun JavaPluginExtension.registerFeatureVariantWithNewSourceSet( + featureVariantName: String, sourceSetName: String = featureVariantName +) { + sourceSets.register(sourceSetName) { + val mainSourceSet = sourceSets["main"] + compileClasspath += mainSourceSet.compileClasspath + mainSourceSet.output // TODO this may not be always necessary + //runtimeClasspath += + //runtimeClasspath += mainSourceSet.runtimeClasspath + mainSourceSet.output + } + + registerFeatureVariantWithSourceSet(featureVariantName, sourceSets[sourceSetName]) +} + +fun JavaPluginExtension.registerFeatureVariantWithSourceSet( + featureVariantName: String, sourceSet: SourceSet +) = registerFeature(featureVariantName) { + usingSourceSet(sourceSet) + withJavadocJar() + withSourcesJar() +} diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm-test-common-feature-variant.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm-test-common-feature-variant.gradle.kts index 69261c9..5455d41 100644 --- a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm-test-common-feature-variant.gradle.kts +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm-test-common-feature-variant.gradle.kts @@ -5,16 +5,4 @@ plugins { } val testCommon = "testCommon" - -sourceSets.register(testCommon) { - val mainSourceSet = sourceSets["main"] - compileClasspath += mainSourceSet.compileClasspath + mainSourceSet.output - //runtimeClasspath += - //runtimeClasspath += mainSourceSet.runtimeClasspath + mainSourceSet.output -} - -java.registerFeature(testCommon) { - usingSourceSet(sourceSets[testCommon]) - withJavadocJar() - withSourcesJar() -} +java.registerFeatureVariantWithNewSourceSet(testCommon) From 031e9235e96ee284ca7f2765ef9c7bd8cee15429 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 23 Nov 2023 15:30:22 +0800 Subject: [PATCH 21/28] Add a TODO --- .../src/main/kotlin/com/huanshankeji/CommonVersions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt index 7d512c7..465db30 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt @@ -16,7 +16,7 @@ class CommonVersions @JvmOverloads constructor( val ktor: String = "2.3.6", val composeMultiplatform: String = "1.5.10", // this is usually only used in classpath dependencies - val vertx: String = "4.4.6", + val vertx: String = "4.4.6", // TODO bump to "4.5.0". There are some breaking changes however. See https://github.com/vert-x3/wiki/wiki/4.5.0-Deprecations-and-breaking-changes. val arrow: String = "1.2.1", val orgJunit: String = "5.10.1", val kotest: String = "5.8.0", From 66a4e166e85ffb159a25512584ed35731d3d702d Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 23 Nov 2023 18:14:48 +0800 Subject: [PATCH 22/28] Clean up and refine the "OS and architecture" feature variant APIs as added in the previous commit --- .../build.gradle.kts | 2 +- .../jvm/native/osandarch/DefaultSupported.kt | 26 +++ .../FeatureVariantsAndDependencies.kt | 173 ++++++------------ .../jvm/native/osandarch/OsAndArch.kt | 25 +-- ...ult-supported-feature-variants.gradle.kts} | 2 +- .../com/huanshankeji/FeatureVariants.kt | 13 ++ .../com/huanshankeji/NamingConventions.kt | 16 ++ 7 files changed, 117 insertions(+), 140 deletions(-) create mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/DefaultSupported.kt rename architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/{register-feature-variants.gradle.kts => register-default-supported-feature-variants.gradle.kts} (69%) create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/NamingConventions.kt diff --git a/architecture-common-gradle-plugins/build.gradle.kts b/architecture-common-gradle-plugins/build.gradle.kts index 003efd6..0df2024 100644 --- a/architecture-common-gradle-plugins/build.gradle.kts +++ b/architecture-common-gradle-plugins/build.gradle.kts @@ -72,7 +72,7 @@ gradlePlugin { } scriptConventionsPlugin( - "jvm.native.osandarch.register-feature-variants", + "jvm.native.osandarch.register-default-supported-feature-variants", "Register the OS and architecture feature variants", "Registers feature variants for different operating systems (Linux, Windows, macOS) and CPU architectures." ) diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/DefaultSupported.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/DefaultSupported.kt new file mode 100644 index 0000000..fc5a6cc --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/DefaultSupported.kt @@ -0,0 +1,26 @@ +package com.huanshankeji.jvm.native.osandarch + + +object DefaultSupported { + object ArchsByOs { + val linux = listOf(CpuArchitecture.X8664, CpuArchitecture.Aarch64) + val windows = listOf(CpuArchitecture.X8664) + val macos = listOf(CpuArchitecture.X8664, CpuArchitecture.Aarch64) + } + + object OsAndArchs { + val linux = ArchsByOs.linux.map { OsAndArch(Os.Linux, it) } + val windows = ArchsByOs.windows.map { OsAndArch(Os.Windows, it) } + val macos = ArchsByOs.macos.map { OsAndArch(Os.Macos, it) } + + val all = listOf(linux, windows, macos).flatten() + val allFeatureVariantNames = all.map { it.featureVariantName } + + // TODO use `entries` when the language version is bumped to 1.9 + val futureAll = Os.values().flatMap { os -> + CpuArchitecture.values().map { arch -> + OsAndArch(os, arch) + } + } + } +} \ No newline at end of file diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt index adafb92..91c139d 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt @@ -1,152 +1,115 @@ package com.huanshankeji.jvm.native.osandarch -import com.huanshankeji.jvm.native.osandarch.Os.* +import com.huanshankeji.* import com.huanshankeji.jvm.native.osandarch.SourceSetType.Main import com.huanshankeji.jvm.native.osandarch.SourceSetType.RegisterSeparate -import com.huanshankeji.registerFeatureVariantWithNewSourceSet -import com.huanshankeji.registerFeatureVariantWithSourceSet -import gradle.kotlin.dsl.accessors._4d764c3096d994e4212b8a01328470fd.runtimeOnly import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo import org.gradle.kotlin.dsl.get -val featureVariantNameCharRegex = Regex("[a-zA-Z0-9]") -val featureVariantNameRegex = Regex("${featureVariantNameCharRegex.pattern}+") -fun String.toFeatureVariantName() = - featureVariantNameCharRegex.findAll(this).map { it.value }.joinToString("") +val OsAndArch.featureVariantName get() = camelCaseIdentifier -fun String.isValidFeatureVariantName() = - matches(featureVariantNameRegex) - -fun String.camelCaseToKebabCase() = - replace(Regex("[A-Z]")) { "-${it.value.lowercase()}" } - - -fun String.capitalizeFirstChar() = - replaceFirstChar { it.uppercaseChar() } - - -infix fun String.camelCaseConcat(other: String) = - this + other.capitalizeFirstChar() - - -fun OsAndArch.getFeatureVariantName() = - os.featureVariantName camelCaseConcat arch.featureVariantName - - -/** - * @param featureVariantName usually in camel case - * @param dependencyIdentifier usually in kebab case (sometimes mixed with snake case, for example "X86_64") - */ -data class Config(val featureVariantName: String, val dependencyIdentifier: String) // TODO classifier - -fun Pair>.toConfigSequence(): Sequence { - val (os, archs) = this - return archs.asSequence().map { arch -> - Config(os.featureVariantName camelCaseConcat arch.featureVariantName, "${os.identifier}-${arch.identifier}") - } -} - -fun Pair>.toConfigs() = - toConfigSequence().toList() - -object Configs { - /* - // TODO use `entries` when the language version is bumped to 1.9 - val ofAllOss = Os.values().map { - Config(it.featureVariantName, it.identifier) - } - */ - - private fun Os.getConfigs() = - (this to supportedOsArchMap.getValue(this)).toConfigs() - - val linux = Linux.getConfigs() - val windows = Windows.getConfigs() - val macos = Macos.getConfigs() - - val all = supportedOsArchs.flatMap { it.toConfigSequence() } -} enum class SourceSetType { Main, RegisterSeparate } -fun JavaPluginExtension.registerFeatureVariants(sourceSetType: SourceSetType) { +fun JavaPluginExtension.registerDefaultSupportedFeatureVariants(sourceSetType: SourceSetType) { when (sourceSetType) { Main -> { val mainSourceSet = sourceSets["main"] - for (config in Configs.all) - registerFeatureVariantWithSourceSet(config.featureVariantName, mainSourceSet) + for (osAndArch in DefaultSupported.OsAndArchs.all) + registerFeatureVariantWithSourceSet(osAndArch.featureVariantName, mainSourceSet) } RegisterSeparate -> - for (config in Configs.all) - registerFeatureVariantWithNewSourceSet(config.featureVariantName) + for (osAndArch in DefaultSupported.OsAndArchs.all) + registerFeatureVariantWithNewSourceSet(osAndArch.featureVariantName) } } -fun DependencyHandlerScope.addAllFeatureVariantDependencies( - featureVariantNames: List, targetConfigurationType: String, dependencyNotation: Any -) { - for (featureVariantName in featureVariantNames) - add(featureVariantName camelCaseConcat targetConfigurationType, dependencyNotation) -} + +/** + * @param identifier usually in kebab case (sometimes mixed with snake case, for example "X86_64") + */ +data class FeatureVariantDependencyConfig(val osAndArch: OsAndArch, val identifier: String) + +fun DependencyHandlerScope.addDependencyToFeatureVariants( + osAndArchs: List, targetConfigurationType: String, dependencyNotation: Any +) = + addDependencyToFeatureVariants( + osAndArchs.map { it.featureVariantName }, targetConfigurationType, dependencyNotation + ) // TODO some functions related to feature variants can be extracted to a separate feature variant package /** - * @param configs use a predefined one in [Configs] or make your own with [Config] + * @param osAndArchs use a predefined one in [DefaultSupported.OsAndArchs] or make your own with [FeatureVariantDependencyConfig] * @param targetConfigurationType the type of the dependency configuration * (see https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:what-are-dependency-configurations * and https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) */ -fun DependencyHandlerScope.addAllFeatureVariantDependenciesWithIdentifiersInNameSuffixes( - configs: List, +fun DependencyHandlerScope.addDependenciesToFeatureVariantsWithIdentifiersInNameSuffixes( + osAndArchs: List, targetConfigurationType: String, - group: String, - namePrefix: String, - version: String? = null + group: String, namePrefix: String, version: String? = null ) { - for ((featureVariantName, dependencyIdentifier) in configs) + for ((osAndArch, dependencyIdentifier) in osAndArchs) addExternalModuleDependencyTo( this, - featureVariantName + targetConfigurationType.capitalizeFirstChar(), + osAndArch.featureVariantName camelCaseConcat targetConfigurationType, group, "$namePrefix-$dependencyIdentifier", version, null, null, null, null ) } +/** @see addDependenciesToFeatureVariantsWithIdentifiersInNameSuffixes */ +fun DependencyHandlerScope.addDependenciesToFeatureVariantsWithIdentifiersInNameSuffixes( + osAndArchs: List, getIdentifier: (OsAndArch) -> String, + targetConfigurationType: String, + group: String, namePrefix: String, version: String? = null +) = + addDependenciesToFeatureVariantsWithIdentifiersInNameSuffixes(osAndArchs.map { + FeatureVariantDependencyConfig(it, getIdentifier(it)) + }, targetConfigurationType, group, namePrefix, version) + /** - * @param configs use a predefined one in [Configs] or make your own with [Config] + * @param configs use a predefined one in [DefaultSupported.OsAndArchs] or make your own with [FeatureVariantDependencyConfig] * @param targetConfigurationType the type of the dependency configuration * (see https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:what-are-dependency-configurations * and https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) */ -fun DependencyHandlerScope.addAllFeatureVariantDependenciesWithIdentifiersInClassifiers( - configs: List, +fun DependencyHandlerScope.addDependenciesToFeatureVariantsWithIdentifiersInClassifiers( + configs: List, targetConfigurationType: String, - group: String, - name: String, - version: String? = null + group: String, name: String, version: String? = null ) { - for ((featureVariantName, dependencyIdentifier) in configs) + for ((osAndArch, dependencyIdentifier) in configs) addExternalModuleDependencyTo( this, - featureVariantName + targetConfigurationType.replaceFirstChar { it.uppercaseChar() }, + osAndArch.featureVariantName camelCaseConcat targetConfigurationType, group, name, version, null, dependencyIdentifier, null, null ) } +/** @see addDependenciesToFeatureVariantsWithIdentifiersInClassifiers */ +fun DependencyHandlerScope.addDependenciesToFeatureVariantsWithIdentifiersInClassifiers( + osAndArchs: List, getIdentifier: (OsAndArch) -> String, + targetConfigurationType: String, + group: String, name: String, version: String? = null +) = + addDependenciesToFeatureVariantsWithIdentifiersInClassifiers(osAndArchs.map { + FeatureVariantDependencyConfig(it, getIdentifier(it)) + }, targetConfigurationType, group, name, version) + fun getCapabilityNotation(group: String, name: String, featureVariantName: String) = "$group:$name-${featureVariantName.camelCaseToKebabCase()}" -inline fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDependencies( +private inline fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDependencies( featureVariantNames: List, targetConfiguration: (featureVariantName: String?) -> String, group: String, name: String, version: String? = null ) { @@ -164,26 +127,6 @@ inline fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDepen } } -// TODO remove -/* -fun DependencyHandlerScope.addFeatureVariantTransitiveCapabilityDependencies( - featureVariantNames: List, targetConfigurationType: String, - group: String, name: String, version: String? = null -) { - for (featureVariantName in featureVariantNames) - addExternalModuleDependencyTo( - this, - featureVariantName camelCaseConcat targetConfigurationType, - group, name, version, - null, null, null - ) { - capabilities { - requireCapability(getCapabilityNotation(group, name, featureVariantName)) - } - } -} -*/ - fun DependencyHandlerScope.addDependencyWithFeatureVariantTransitiveCapabilityDependencies( featureVariantNames: List, targetConfigurationType: String, group: String, name: String, version: String? = null @@ -209,15 +152,3 @@ fun DependencyHandlerScope.addDependencyWithFeatureVariantCapabilityDependencies { _ -> targetConfiguration }, group, name, version ) - -// TODO remove. This function is not necessary as all feature variant dependencies are added when running code and packaging distributions. -fun DependencyHandlerScope.addConventionalDependencyWithOsAndArch( - osAndArchForMain: OsAndArch = getCurrentOsAndArch(), sourceSets: SourceSetContainer, - featureVariantNames: List, targetConfigurationType: String, - group: String, name: String, version: String? = null -) { - addDependencyWithFeatureVariantTransitiveCapabilityDependencies( - featureVariantNames, targetConfigurationType, group, name, version - ) - runtimeOnly(sourceSets[osAndArchForMain.getFeatureVariantName()]) -} diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt index 131a134..9e2468a 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt @@ -1,34 +1,25 @@ package com.huanshankeji.jvm.native.osandarch +import com.huanshankeji.camelCaseConcat import com.huanshankeji.jvm.native.osandarch.CpuArchitecture.Aarch64 import com.huanshankeji.jvm.native.osandarch.CpuArchitecture.X8664 import com.huanshankeji.jvm.native.osandarch.Os.* +import com.huanshankeji.kebabCaseConcat import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform -import java.util.* // TODO possibly use feature variant name enum class Os(val identifier: String) { - Linux("linux"), Windows("windows"), Macos("osx"); - - // Note: not using `get()` in an enum here causes initialization errors. - val featureVariantName: String get() = identifier.toFeatureVariantName() + Linux("linux"), Windows("windows"), Macos("osx") } enum class CpuArchitecture(val identifier: String) { - X8664("x86_64"), Aarch64("aarch64"); - - val featureVariantName: String get() = identifier.toFeatureVariantName() + X8664("x8664"), Aarch64("aarch64") } - -val supportedOsArchs = listOf( - Linux to listOf(X8664, Aarch64), - Windows to listOf(X8664), - Macos to listOf(X8664, Aarch64) -) -val supportedOsArchMap = EnumMap(supportedOsArchs.toMap()) - -data class OsAndArch(val os: Os, val arch: CpuArchitecture) +data class OsAndArch(val os: Os, val arch: CpuArchitecture) { + val kebabCaseIdentifier = os.identifier kebabCaseConcat arch.identifier + val camelCaseIdentifier = os.identifier camelCaseConcat arch.identifier +} fun getCurrentOsAndArch(): OsAndArch { val currentOperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts similarity index 69% rename from architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts rename to architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts index dc8b4c9..734c7a2 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-feature-variants.gradle.kts +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts @@ -10,4 +10,4 @@ interface Extension { val extension = extensions.create("registerOsAndArchFeatureVariants") -java.registerFeatureVariants(extension.sourceSetType.getOrElse(SourceSetType.Main)) +java.registerDefaultSupportedFeatureVariants(extension.sourceSetType.getOrElse(SourceSetType.Main)) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt index f4dc315..8178b04 100644 --- a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt @@ -2,6 +2,7 @@ package com.huanshankeji import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.get fun JavaPluginExtension.registerFeatureVariantWithNewSourceSet( @@ -24,3 +25,15 @@ fun JavaPluginExtension.registerFeatureVariantWithSourceSet( withJavadocJar() withSourcesJar() } + + +fun DependencyHandlerScope.addDependencyToFeatureVariants( + featureVariantNames: List, targetConfigurationType: String, dependencyNotation: Any +) { + for (featureVariantName in featureVariantNames) + add(featureVariantName camelCaseConcat targetConfigurationType, dependencyNotation) +} + + +fun String.isValidFeatureVariantName() = + matches(camelCaseRegex) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/NamingConventions.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/NamingConventions.kt new file mode 100644 index 0000000..bbc31fd --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/NamingConventions.kt @@ -0,0 +1,16 @@ +package com.huanshankeji + +val camelCaseCharRegex = Regex("[a-zA-Z0-9]") +val camelCaseRegex = Regex("${camelCaseCharRegex.pattern}+") + +infix fun String.kebabCaseConcat(other: String) = + "$this-$other" + +fun String.capitalizeFirstChar() = + replaceFirstChar { it.uppercaseChar() } + +infix fun String.camelCaseConcat(other: String) = + this + other.capitalizeFirstChar() + +fun String.camelCaseToKebabCase() = + replace(Regex("[A-Z]")) { "-${it.value.lowercase()}" } From 57e43efd5aff6daf9629ca58fea2f17b154bdba2 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Mon, 29 Jan 2024 05:17:26 +0800 Subject: [PATCH 23/28] Implement the kotlinx.benchmark conventions plugins --- .../build.gradle.kts | 1 - .../huanshankeji/VersionsAndDependencies.kt | 4 -- .../FeatureVariantsAndDependencies.kt | 8 +-- ...ault-supported-feature-variants.gradle.kts | 2 + buildSrc/build.gradle.kts | 7 ++- ...gned-version-plugin-conventions.gradle.kts | 10 ++++ .../CommonGradleClasspathDependencies.kt | 3 ++ kotlin-common-gradle-plugins/build.gradle.kts | 19 +++++++ .../main/kotlin/com/huanshankeji/Internal.kt | 6 +++ .../com/huanshankeji/SourceSetConfig.kt | 17 +++++++ .../kotlin/com/huanshankeji/SourceSetType.kt | 5 ++ .../huanshankeji/VersionsAndDependencies.kt | 7 +++ .../KotlinxBenchmarkConventionsExtension.kt | 13 +++++ ...tlinx-benchmark-jvm-conventions.gradle.kts | 49 +++++++++++++++++++ ...hmark-multiplatform-conventions.gradle.kts | 39 +++++++++++++++ 15 files changed, 178 insertions(+), 12 deletions(-) delete mode 100644 architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/Internal.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetConfig.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetType.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/KotlinxBenchmarkConventionsExtension.kt create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts create mode 100644 kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-multiplatform-conventions.gradle.kts diff --git a/architecture-common-gradle-plugins/build.gradle.kts b/architecture-common-gradle-plugins/build.gradle.kts index 0df2024..542816e 100644 --- a/architecture-common-gradle-plugins/build.gradle.kts +++ b/architecture-common-gradle-plugins/build.gradle.kts @@ -14,7 +14,6 @@ dependencies { /* This project depends on a specific version of the Maven dependency of "common-gradle-dependencies" since now they are developed together in the same branch `main`, enabling it to always depend on a release version. */ - implementation("com.huanshankeji:common-gradle-dependencies:$pluginProjectDependentStableCommonGradleDependenciesVersion") implementation(commonGradleClasspathDependencies.composeMultiplatform.gradlePlugin.pluginProject()) } diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt deleted file mode 100644 index 3e509be..0000000 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.huanshankeji - -internal val commonVersions = CommonVersions() -internal val commonDependencies = CommonDependencies(commonVersions) diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt index 91c139d..0c031c4 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt @@ -1,8 +1,8 @@ package com.huanshankeji.jvm.native.osandarch import com.huanshankeji.* -import com.huanshankeji.jvm.native.osandarch.SourceSetType.Main -import com.huanshankeji.jvm.native.osandarch.SourceSetType.RegisterSeparate +import com.huanshankeji.SourceSetType.Main +import com.huanshankeji.SourceSetType.RegisterSeparate import org.gradle.api.plugins.JavaPluginExtension import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo @@ -11,10 +11,6 @@ import org.gradle.kotlin.dsl.get val OsAndArch.featureVariantName get() = camelCaseIdentifier -enum class SourceSetType { - Main, RegisterSeparate -} - fun JavaPluginExtension.registerDefaultSupportedFeatureVariants(sourceSetType: SourceSetType) { when (sourceSetType) { Main -> { diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts index 734c7a2..a439482 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts @@ -1,5 +1,7 @@ package com.huanshankeji.jvm.native.osandarch +import com.huanshankeji.SourceSetType + plugins { java } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index e221c7e..767449c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,12 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0") } */ - //implementation(kotlin("gradle-plugin", "1.8.10")) // for Compose 1.3.1 + implementation( + kotlin( + "gradle-plugin", + "1.9.20" + ) + ) // for `KotlinCompilationTask` and the version is for Compose 1.5.1 implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.1.2") // This version has to be used for Gradle 8.4. implementation("com.gradle.publish:plugin-publish-plugin:1.2.1") diff --git a/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts b/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts index 7a20939..1a6aee4 100644 --- a/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts @@ -1,5 +1,15 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask + plugins { id("conventions") } version = alignedPluginVersion + +dependencies { + implementation("com.huanshankeji:common-gradle-dependencies:$pluginProjectDependentStableCommonGradleDependenciesVersion") +} + +tasks.named>("compileKotlin").configure { + compilerOptions.freeCompilerArgs.add("-opt-in=com.huanshankeji.InternalApi") +} diff --git a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt index 4d761a3..c44d290 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt @@ -54,6 +54,9 @@ class CommonGradleClasspathDependencies(val versions: CommonVersions) { val defaultVersion = versions.kotlinxBenchmark fun PluginDependenciesSpec.applyPluginWithVersion(version: String = defaultVersion) = id("org.jetbrains.kotlinx.benchmark").version(defaultVersion) + + fun pluginProject(version: String = defaultVersion) = + "org.jetbrains.kotlinx:kotlinx-benchmark-plugin:$version" } val benchmark = Benchmark() diff --git a/kotlin-common-gradle-plugins/build.gradle.kts b/kotlin-common-gradle-plugins/build.gradle.kts index 11885ff..8e4d4d6 100644 --- a/kotlin-common-gradle-plugins/build.gradle.kts +++ b/kotlin-common-gradle-plugins/build.gradle.kts @@ -5,6 +5,11 @@ plugins { dependencies { //implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:0.4.9") + // TODO + //implementation(commonGradleClasspathDependencies.kotlinx.benchmark.pluginProject()) + implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") + testImplementation(kotlin("test")) } @@ -87,5 +92,19 @@ gradlePlugin { "JVM test common feature variant", "Adds a JVM test common feature variant with a source set that depends on `main`." ) + + run { + scriptConventionsPlugin( + "benchmark.kotlinx-benchmark-jvm-conventions", + "kotlinx-benchmark conventions for Kotlin JVM", + "Applies the kotlinx-benchmark and `allopen` plugins, adds the kotlinx-benchmark dependencies, " + + "and registers a separate `benchmarks` source set that depends on `main` by default." + ) + scriptConventionsPlugin( + "benchmark.kotlinx-benchmark-multiplatform-conventions", + "kotlinx-benchmark conventions for Kotlin Multiplatform", + "Applies the kotlinx-benchmark and `allopen` plugins and adds the koltinx-benchmark dependencies." + ) + } } } diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/Internal.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/Internal.kt new file mode 100644 index 0000000..cd5f39e --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/Internal.kt @@ -0,0 +1,6 @@ +package com.huanshankeji + +@RequiresOptIn +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.PROPERTY) +annotation class InternalApi \ No newline at end of file diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetConfig.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetConfig.kt new file mode 100644 index 0000000..1c29bf4 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetConfig.kt @@ -0,0 +1,17 @@ +package com.huanshankeji + +// TODO use or remove +// TODO How should dependents be added, the "implementation" way or "api" way? Does this make this class too complicated and unnecessary as one can just call the Gradle functions directly? +class SourceSetConfig(val type: Type, val name: String, val dependents: List) { + + /** + * @see SourceSetType + */ + enum class Type { + Existing, New + } + + companion object { + val main = SourceSetConfig(Type.Existing, "main", emptyList()) + } +} \ No newline at end of file diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetType.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetType.kt new file mode 100644 index 0000000..1f49801 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/SourceSetType.kt @@ -0,0 +1,5 @@ +package com.huanshankeji + +enum class SourceSetType { + Main, RegisterSeparate +} \ No newline at end of file diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt new file mode 100644 index 0000000..84ad43f --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/VersionsAndDependencies.kt @@ -0,0 +1,7 @@ +package com.huanshankeji + +@InternalApi +val commonVersions = CommonVersions() + +@InternalApi +val commonDependencies = CommonDependencies(commonVersions) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/KotlinxBenchmarkConventionsExtension.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/KotlinxBenchmarkConventionsExtension.kt new file mode 100644 index 0000000..4357701 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/KotlinxBenchmarkConventionsExtension.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.benchmark + +import com.huanshankeji.SourceSetType +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.create + +interface KotlinxBenchmarkConventionsExtension { + val sourceSetType: Property +} + +fun ExtensionContainer.createKotlinxBenchmarkConventionsExtension() = + create("kotlinxBenchmarkConventions") diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts new file mode 100644 index 0000000..2ddc9d7 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts @@ -0,0 +1,49 @@ +package com.huanshankeji.benchmark + +import com.huanshankeji.SourceSetType.Main +import com.huanshankeji.SourceSetType.RegisterSeparate +import com.huanshankeji.commonDependencies +import com.huanshankeji.sourceSets + +plugins { + kotlin("jvm") + id("org.jetbrains.kotlinx.benchmark") + kotlin("plugin.allopen") +} + +val extension = extensions.createKotlinxBenchmarkConventionsExtension() +val sourceSetType = extension.sourceSetType.getOrElse(RegisterSeparate) + +private val MAIN = "main" +private val BENCHMAKRS = "benchmarks" + +if (sourceSetType == RegisterSeparate) + sourceSets.create(BENCHMAKRS) + +dependencies { + val implementationString: String + when (sourceSetType) { + Main -> implementationString = "implementation" + RegisterSeparate -> { + implementationString = "benchmarksImplementation" + implementationString(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) + } + } + + implementationString(commonDependencies.kotlinx.benchmark.runtime()) +} + +benchmark { + targets { + register( + when (sourceSetType) { + Main -> MAIN + RegisterSeparate -> BENCHMAKRS + } + ) + } +} + +allOpen { + annotation("org.openjdk.jmh.annotations.State") +} diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-multiplatform-conventions.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-multiplatform-conventions.gradle.kts new file mode 100644 index 0000000..a896147 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-multiplatform-conventions.gradle.kts @@ -0,0 +1,39 @@ +package com.huanshankeji.benchmark + +import com.huanshankeji.commonDependencies +import org.gradle.kotlin.dsl.invoke + +plugins { + kotlin("multiplatform") + id("org.jetbrains.kotlinx.benchmark") + kotlin("plugin.allopen") +} + +/* +There are 2 reasons creating a benchmark(s) module is not supported: +1. I didn't find an official way to add a `commonBenchmarks` compilation and make it depend on `commonMain; +1. The benchmark targets are added in `afterEvaluate` so benchmark(s) module dependencies can't be added. + */ + +/* +val extension = extensions.createKotlinxBenchmarkConventionsExtension() +val sourceSetType = extension.sourceSetType.getOrElse(RegisterSeparate) +*/ + +kotlin.sourceSets.commonMain { + dependencies { + implementation(commonDependencies.kotlinx.benchmark.runtime()) + } +} + +afterEvaluate { + benchmark { + targets { + kotlin.targets.forEach { register(it.name) } + } + } +} + +allOpen { + annotation("org.openjdk.jmh.annotations.State") +} From 09368526df93d2047ee3f29965e916957853ecaa Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 30 Jan 2024 17:09:30 +0800 Subject: [PATCH 24/28] Fix a bug that the extension arguments are not set when read by reading it in `afterEvaluate` --- ...ault-supported-feature-variants.gradle.kts | 2 + ...tlinx-benchmark-jvm-conventions.gradle.kts | 57 ++++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts index a439482..877f60e 100644 --- a/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts @@ -10,6 +10,8 @@ interface Extension { val sourceSetType: Property } +// TODO put in `afterEvaluate`? + val extension = extensions.create("registerOsAndArchFeatureVariants") java.registerDefaultSupportedFeatureVariants(extension.sourceSetType.getOrElse(SourceSetType.Main)) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts index 2ddc9d7..d660e0c 100644 --- a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts @@ -12,38 +12,41 @@ plugins { } val extension = extensions.createKotlinxBenchmarkConventionsExtension() -val sourceSetType = extension.sourceSetType.getOrElse(RegisterSeparate) -private val MAIN = "main" -private val BENCHMAKRS = "benchmarks" +afterEvaluate { + val sourceSetType = extension.sourceSetType.getOrElse(RegisterSeparate) -if (sourceSetType == RegisterSeparate) - sourceSets.create(BENCHMAKRS) + val MAIN = "main" + val BENCHMAKRS = "benchmarks" -dependencies { - val implementationString: String - when (sourceSetType) { - Main -> implementationString = "implementation" - RegisterSeparate -> { - implementationString = "benchmarksImplementation" - implementationString(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) + if (sourceSetType == RegisterSeparate) + sourceSets.create(BENCHMAKRS) + + dependencies { + val implementationString: String + when (sourceSetType) { + Main -> implementationString = "implementation" + RegisterSeparate -> { + implementationString = "benchmarksImplementation" + implementationString(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) + } } - } - implementationString(commonDependencies.kotlinx.benchmark.runtime()) -} + implementationString(commonDependencies.kotlinx.benchmark.runtime()) + } -benchmark { - targets { - register( - when (sourceSetType) { - Main -> MAIN - RegisterSeparate -> BENCHMAKRS - } - ) + benchmark { + targets { + register( + when (sourceSetType) { + Main -> MAIN + RegisterSeparate -> BENCHMAKRS + } + ) + } } -} -allOpen { - annotation("org.openjdk.jmh.annotations.State") -} + allOpen { + annotation("org.openjdk.jmh.annotations.State") + } +} \ No newline at end of file From 6d9c5aa9cb2fa276fb6feb50dc3aeb6aee733a53 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 30 Jan 2024 17:15:53 +0800 Subject: [PATCH 25/28] Extract a common variable --- .../benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts index d660e0c..9c290d6 100644 --- a/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts @@ -28,7 +28,7 @@ afterEvaluate { Main -> implementationString = "implementation" RegisterSeparate -> { implementationString = "benchmarksImplementation" - implementationString(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) + implementationString(with(sourceSets.main.get()) { output + runtimeClasspath }) } } From eca744e3d2ee3ecca2745b6515198ba5a3322ea3 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 30 Jan 2024 20:59:33 +0800 Subject: [PATCH 26/28] Reformat some code --- buildSrc/build.gradle.kts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 767449c..be4857d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,12 +24,8 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0") } */ - implementation( - kotlin( - "gradle-plugin", - "1.9.20" - ) - ) // for `KotlinCompilationTask` and the version is for Compose 1.5.1 + // for `KotlinCompilationTask` and the version is for Compose 1.5.1 + implementation(kotlin("gradle-plugin", "1.9.20")) implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.1.2") // This version has to be used for Gradle 8.4. implementation("com.gradle.publish:plugin-publish-plugin:1.2.1") From 5c933f96e5e630c7560c591c08ed5e50474c17a1 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 30 Jan 2024 21:02:40 +0800 Subject: [PATCH 27/28] Use the kotlinx.benchmark dependency from `common-gradle-dependencies` --- buildSrc/build.gradle.kts | 2 +- kotlin-common-gradle-plugins/build.gradle.kts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index be4857d..1d6a916 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -33,5 +33,5 @@ dependencies { // This is a bootstrapping dependency (cross-version self-dependency). Try not to update its version unless necessary. implementation("com.huanshankeji.team:gradle-plugins:0.3.0") { exclude("org.jetbrains.kotlin") } // This is also a bootstrapping dependency. - implementation("com.huanshankeji:common-gradle-dependencies:0.7.0-20231111") { exclude("org.jetbrains.kotlin") } + implementation("com.huanshankeji:common-gradle-dependencies:0.7.1-20231111-SNAPSHOT") { exclude("org.jetbrains.kotlin") } // TODO } diff --git a/kotlin-common-gradle-plugins/build.gradle.kts b/kotlin-common-gradle-plugins/build.gradle.kts index 8e4d4d6..95564ef 100644 --- a/kotlin-common-gradle-plugins/build.gradle.kts +++ b/kotlin-common-gradle-plugins/build.gradle.kts @@ -6,8 +6,7 @@ dependencies { //implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0") implementation("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:0.4.9") - // TODO - //implementation(commonGradleClasspathDependencies.kotlinx.benchmark.pluginProject()) + implementation(commonGradleClasspathDependencies.kotlinx.benchmark.pluginProject()) implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") testImplementation(kotlin("test")) From 4583bfdfbfa4c511530a43924a05405b6efe0d1a Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 30 Jan 2024 21:13:52 +0800 Subject: [PATCH 28/28] Use the latest stable version of `common-gradle-dependencies` --- buildSrc/build.gradle.kts | 2 +- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- .../main/kotlin/aligned-version-plugin-conventions.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 1d6a916..6ef7b7f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -33,5 +33,5 @@ dependencies { // This is a bootstrapping dependency (cross-version self-dependency). Try not to update its version unless necessary. implementation("com.huanshankeji.team:gradle-plugins:0.3.0") { exclude("org.jetbrains.kotlin") } // This is also a bootstrapping dependency. - implementation("com.huanshankeji:common-gradle-dependencies:0.7.1-20231111-SNAPSHOT") { exclude("org.jetbrains.kotlin") } // TODO + implementation("com.huanshankeji:common-gradle-dependencies:0.7.1-20231111") { exclude("org.jetbrains.kotlin") } } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index f1c8bda..0440fdf 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -13,6 +13,6 @@ val alignedPluginVersion = "0.5.0-SNAPSHOT" val commonGradleDependenciesVersion = "0.7.1-20231111-SNAPSHOT" // This is the source dependency version. There is another build source dependency in "buildSrc/build.gradle.kts". -val pluginProjectDependentStableCommonGradleDependenciesVersion = "0.7.0-20231111".apply { +val pluginProjectSourceDependentStableCommonGradleDependenciesVersion = "0.7.1-20231111".apply { require(!endsWith("SNAPSHOT")) } diff --git a/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts b/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts index 1a6aee4..b8b0bbe 100644 --- a/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/aligned-version-plugin-conventions.gradle.kts @@ -7,7 +7,7 @@ plugins { version = alignedPluginVersion dependencies { - implementation("com.huanshankeji:common-gradle-dependencies:$pluginProjectDependentStableCommonGradleDependenciesVersion") + implementation("com.huanshankeji:common-gradle-dependencies:$pluginProjectSourceDependentStableCommonGradleDependenciesVersion") } tasks.named>("compileKotlin").configure {