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 b010bd8..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()) } @@ -61,12 +60,20 @@ gradlePlugin { "(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" + } } + + scriptConventionsPlugin( + "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/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/default-web-frontend-conventions.gradle.kts b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/default-web-frontend-conventions.gradle.kts index ec175b8..198ab45 100644 --- 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 @@ -5,6 +5,12 @@ plugins { id("org.jetbrains.compose") } +interface Extension { + val htmlGenerationProjectPath: Property +} + +val extension = extensions.create("defaultWebFrontendConventions") + repositories { google() mavenCentral() @@ -28,7 +34,7 @@ kotlin { binaries.executable() } sourceSets { - val jsMain by getting { + jsMain { dependencies { implementation(compose.web.core) implementation(compose.runtime) @@ -41,7 +47,9 @@ kotlin { val generatedResourcesFile = buildDir.resolve("generatedResources") tasks.named("jsProcessResources") { - val htmlGenerationRun = tasks.getByPath(project.path + ":html-generation:run") as JavaExec + val htmlGenerationRun = tasks.getByPath( + extension.htmlGenerationProjectPath.getOrElse(project.path + ":html-generation") + ":run" + ) as JavaExec htmlGenerationRun.args(generatedResourcesFile.absolutePath) dependsOn(htmlGenerationRun) } 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/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..0c031c4 --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/FeatureVariantsAndDependencies.kt @@ -0,0 +1,150 @@ +package com.huanshankeji.jvm.native.osandarch + +import com.huanshankeji.* +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 +import org.gradle.kotlin.dsl.get + +val OsAndArch.featureVariantName get() = camelCaseIdentifier + + +fun JavaPluginExtension.registerDefaultSupportedFeatureVariants(sourceSetType: SourceSetType) { + when (sourceSetType) { + Main -> { + val mainSourceSet = sourceSets["main"] + for (osAndArch in DefaultSupported.OsAndArchs.all) + registerFeatureVariantWithSourceSet(osAndArch.featureVariantName, mainSourceSet) + } + + RegisterSeparate -> + for (osAndArch in DefaultSupported.OsAndArchs.all) + registerFeatureVariantWithNewSourceSet(osAndArch.featureVariantName) + } +} + + +/** + * @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 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.addDependenciesToFeatureVariantsWithIdentifiersInNameSuffixes( + osAndArchs: List, + targetConfigurationType: String, + group: String, namePrefix: String, version: String? = null +) { + for ((osAndArch, dependencyIdentifier) in osAndArchs) + addExternalModuleDependencyTo( + this, + 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 [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.addDependenciesToFeatureVariantsWithIdentifiersInClassifiers( + configs: List, + targetConfigurationType: String, + group: String, name: String, version: String? = null +) { + for ((osAndArch, dependencyIdentifier) in configs) + addExternalModuleDependencyTo( + this, + 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()}" + +private 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)) + } + } +} + +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 + ) 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..9e2468a --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/OsAndArch.kt @@ -0,0 +1,39 @@ +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 + +// TODO possibly use feature variant name +enum class Os(val identifier: String) { + Linux("linux"), Windows("windows"), Macos("osx") +} + +enum class CpuArchitecture(val identifier: String) { + X8664("x8664"), Aarch64("aarch64") +} + +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() + 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-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 new file mode 100644 index 0000000..877f60e --- /dev/null +++ b/architecture-common-gradle-plugins/src/main/kotlin/com/huanshankeji/jvm/native/osandarch/register-default-supported-feature-variants.gradle.kts @@ -0,0 +1,17 @@ +package com.huanshankeji.jvm.native.osandarch + +import com.huanshankeji.SourceSetType + +plugins { + java +} + +interface Extension { + val sourceSetType: Property +} + +// TODO put in `afterEvaluate`? + +val extension = extensions.create("registerOsAndArchFeatureVariants") + +java.registerDefaultSupportedFeatureVariants(extension.sourceSetType.getOrElse(SourceSetType.Main)) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bb27cda..6ef7b7f 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" } @@ -24,13 +24,14 @@ 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("org.gradle.kotlin:gradle-kotlin-dsl-plugins:4.0.6") // This version has to be used for Gradle 8.0.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.1.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") } // 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.1-20231111") { exclude("org.jetbrains.kotlin") } } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 6d43b9f..de8eb28 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -4,15 +4,15 @@ import com.huanshankeji.CommonVersions val commonVersions = CommonVersions() val commonGradleClasspathDependencies = CommonGradleClasspathDependencies(commonVersions) -val kotlinVersion = "1.8.10" // for Compose 1.3.1 +val kotlinVersion = "1.9.20" // for Compose 1.4.0 // TODO remove this comment -val alignedPluginVersion = "0.4.1" +val alignedPluginVersion = "0.5.0" // "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.7.1-20231111-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 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 7a20939..b8b0bbe 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:$pluginProjectSourceDependentStableCommonGradleDependenciesVersion") +} + +tasks.named>("compileKotlin").configure { + compilerOptions.freeCompilerArgs.add("-opt-in=com.huanshankeji.InternalApi") +} 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..b13aae1 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) = @@ -75,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() @@ -210,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 5f5f86b..c44d290 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonGradleClasspathDependencies.kt @@ -1,17 +1,31 @@ package com.huanshankeji +import org.gradle.kotlin.dsl.kotlin import org.gradle.plugin.use.PluginDependenciesSpec 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 version 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.applyPluginWithVersion(version: String = defaultVersion) = + applyPluginWithoutVersion().version(version) + } + + val serialization = Serialization() } - val serializationPlugin = SerializationPlugin() + val plugin = Plugin() + + @Deprecated("Use `plugin.serialization` instead.", ReplaceWith("plugin.serialization")) + val serializationPlugin = plugin.serialization } val kotlin = Kotlin() @@ -34,4 +48,19 @@ 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) + + fun pluginProject(version: String = defaultVersion) = + "org.jetbrains.kotlinx:kotlinx-benchmark-plugin:$version" + } + + 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 88441bc..465db30 100644 --- a/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt +++ b/common-gradle-dependencies/src/main/kotlin/com/huanshankeji/CommonVersions.kt @@ -2,23 +2,25 @@ package com.huanshankeji import kotlinVersion -class CommonVersions( +class CommonVersions @JvmOverloads constructor( val kotlin: String = kotlinVersion, - val kotlinCommon: String = "0.2.1", + val kotlinCommon: String = "0.3.0", - val kotlinxCoroutines: String = "1.6.4", - val kotlinxHtml: String = "0.8.0", - val kotlinxSerialization: String = "1.4.0-RC", - 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 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.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.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", + 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.19.1" ) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 761b8f0..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.0.2-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" \ diff --git a/kotlin-common-gradle-plugins/build.gradle.kts b/kotlin-common-gradle-plugins/build.gradle.kts index 11885ff..95564ef 100644 --- a/kotlin-common-gradle-plugins/build.gradle.kts +++ b/kotlin-common-gradle-plugins/build.gradle.kts @@ -5,6 +5,10 @@ plugins { dependencies { //implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:0.4.9") + implementation(commonGradleClasspathDependencies.kotlinx.benchmark.pluginProject()) + implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") + testImplementation(kotlin("test")) } @@ -87,5 +91,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/FeatureVariants.kt b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt new file mode 100644 index 0000000..8178b04 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/FeatureVariants.kt @@ -0,0 +1,39 @@ +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( + 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() +} + + +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/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/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()}" } 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..9c290d6 --- /dev/null +++ b/kotlin-common-gradle-plugins/src/main/kotlin/com/huanshankeji/benchmark/kotlinx-benchmark-jvm-conventions.gradle.kts @@ -0,0 +1,52 @@ +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() + +afterEvaluate { + val sourceSetType = extension.sourceSetType.getOrElse(RegisterSeparate) + + val MAIN = "main" + val BENCHMAKRS = "benchmarks" + + if (sourceSetType == RegisterSeparate) + sourceSets.create(BENCHMAKRS) + + dependencies { + val implementationString: String + when (sourceSetType) { + Main -> implementationString = "implementation" + RegisterSeparate -> { + implementationString = "benchmarksImplementation" + implementationString(with(sourceSets.main.get()) { output + runtimeClasspath }) + } + } + + implementationString(commonDependencies.kotlinx.benchmark.runtime()) + } + + benchmark { + targets { + register( + when (sourceSetType) { + Main -> MAIN + RegisterSeparate -> BENCHMAKRS + } + ) + } + } + + allOpen { + annotation("org.openjdk.jmh.annotations.State") + } +} \ No newline at end of file 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") +} 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) 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." } }