Skip to content

Commit

Permalink
Replaced compiler-specific modules with version-specific source sets (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr3zee committed Jun 21, 2024
1 parent 1ab75db commit ad99de0
Show file tree
Hide file tree
Showing 42 changed files with 175 additions and 270 deletions.
19 changes: 9 additions & 10 deletions compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
*/

import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import util.configureMetaTasks

plugins {
alias(libs.plugins.conventions.jvm)
alias(libs.plugins.compiler.specific.module)
}

val kotlinVersion: String by extra
val rpcVersion: String = libs.versions.kotlinx.rpc.get()

Expand All @@ -14,17 +18,12 @@ allprojects {
version = "$kotlinVersion-$rpcVersion"
}

plugins {
alias(libs.plugins.conventions.jvm)
alias(libs.plugins.compiler.specific.module)
kotlin {
explicitApi = ExplicitApiMode.Disabled
}

subprojects {
afterEvaluate {
configure<KotlinProjectExtension> {
explicitApi = ExplicitApiMode.Disabled
}
}
dependencies {
compileOnly(libs.kotlin.compiler.embeddable)
}

configureMetaTasks("cleanTest", "test")
Expand Down
11 changes: 0 additions & 11 deletions compiler-plugin/compiler-plugin-1_7/build.gradle.kts

This file was deleted.

11 changes: 0 additions & 11 deletions compiler-plugin/compiler-plugin-1_7_2/build.gradle.kts

This file was deleted.

11 changes: 0 additions & 11 deletions compiler-plugin/compiler-plugin-1_8/build.gradle.kts

This file was deleted.

11 changes: 0 additions & 11 deletions compiler-plugin/compiler-plugin-1_9/build.gradle.kts

This file was deleted.

11 changes: 0 additions & 11 deletions compiler-plugin/compiler-plugin-core/build.gradle.kts

This file was deleted.

5 changes: 0 additions & 5 deletions compiler-plugin/gradle.properties

This file was deleted.

1 change: 0 additions & 1 deletion compiler-plugin/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pluginManagement {

plugins {
id("settings-conventions")
id("compiler-specific-modules")
}

includeRootAsPublic()
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,100 @@
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.nio.file.Files
import java.nio.file.Path

val kotlinVersion: String by extra

fun NamedDomainObjectContainer<KotlinSourceSet>.applyCompilerSpecificSourceSets() {
forEach { set ->
val sourceDirs = set.kotlin.sourceDirectories.toList()
val path = sourceDirs.firstOrNull() // one is ok in most cases because we need its parent directory
?: error(
"Expected at least one source set dir for '${set.name}' source set (kotlin dir). " +
"Review the case and adjust the script"
)

val sourceSetPath = path.toPath().parent
if (!Files.exists(sourceSetPath)) {
return@forEach
}

// ### Utils ###
val core = sourceSetPath.resolve(Const.CORE_SOURCE_SET).toFile()

// version-specific source sets
val vsSets = Files.newDirectoryStream(sourceSetPath).use { it.toList() }.filter {
Files.isDirectory(it) && it.name().matches(directoryNameRegex)
}.map { it.toFile() }

// choose 'latest' if there are no more specific ones
val mostSpecificApplicable = vsSets.mostSpecificByVersionOrNull(kotlinVersion)
?: vsSets.singleOrNull { it.name == Const.LATEST_SOURCE_SET }
?: run {
logger.info("No version specific sources sets, but '${core.name}'")
set.kotlin.setSrcDirs(listOf(core)) // 'core' source set instead of 'kotlin'
set.configureResources(sourceSetPath, core.name)
return@forEach
}

fun capitalize(string: String): String {
if (string.isEmpty()) {
return ""
}
val firstChar = string[0]
return string.replaceFirst(firstChar, Character.toTitleCase(firstChar))
}
logger.info(
"${project.name}: included version specific source sets: " +
"${core.name}, ${mostSpecificApplicable.name}"
)

object CSM {
const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform"
const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm"
}
set.kotlin.setSrcDirs(listOf(core, mostSpecificApplicable)) // 'core' source set instead of 'kotlin'
set.configureResources(sourceSetPath, core.name, mostSpecificApplicable.name)

val kotlinVersion = getKotlinPluginVersion()
val excluded = vsSets.filter { it != mostSpecificApplicable }
logger.info("${project.name}: excluded version specific source sets: [${excluded.joinToString { it.name }}]")
}
}

// ### Plugin Logic ###
// What happens here, is that root module is being made a complete compile specific module for current version of Kotlin
// We take -core submodule that contains compiler version independent code, add to it version specific module
// and the summary is presented to the outer world as a complete module,
// which is the root module for these two submodules.
// Root module takes its submodules as `api` gradle configurations, and root's jar will consist of
// two submodules' artifacts and will be presented to the world as it was always a one complete module.
fun KotlinSourceSet.configureResources(sourceSetPath: Path, vararg versionNames: String) {
val vsResources = sourceSetPath.resolve(Const.RESOURCES).toFile()
resources.setSrcDirs(
versionNames.map { vsResources.resolve(it) }
)

val rootProjectPrefix = "$name-"
val coreProjectName = "${rootProjectPrefix}core"
// 'resources' property does not work alone in gradle 7.5.1 with kotlin 1.7.0 (no idea why),
// so we adjust task contents as well
tasks.withType<ProcessResources>().configureEach {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE

// configurations are available only when kotlin plugin is applied, so we use lazy resolving
fun Project.lazyDependency(baseConfigurationName: String, notation: Any) {
val kmpConfigurationName = "commonMain${capitalize(baseConfigurationName)}"
this.configurations.matching { it.name == kmpConfigurationName }.all {
this@lazyDependency.dependencies.add(kmpConfigurationName, notation)
from(versionNames.map { vsResources.resolve(it) })
include { it.file.parentInAllowList(versionNames) }
}
}

this.configurations.matching { it.name == baseConfigurationName }.all {
this@lazyDependency.dependencies.add(baseConfigurationName, notation)
fun File.parentInAllowList(allowList: Array<out String>): Boolean {
val parent = toPath().parent?.toFile()
// will skip v_1_7 for 1.7.0, as it's parent is resources
// but will allow META-INF, as it's parent is v_1_7
if (parent?.name in allowList) {
return true
}
}

fun Project.lazyApi(notation: Any) {
lazyDependency("api", notation)
// allow META-INF contents
return untilAllowedParentOrNull(allowList) != null
}

val root = project

subprojects {
afterEvaluate {
println("Compiler-specific module $name, version: $version")
tailrec fun File.untilAllowedParentOrNull(allowList: Array<out String>): File? {
if (name in allowList) {
return null
}

when {
name == coreProjectName -> {
root.lazyApi(this)
}

name.startsWith(rootProjectPrefix) -> {
val semVer = name
.removePrefix(rootProjectPrefix)
.replace('_', '.')
val parent = toPath().parent?.toFile()
return if (parent?.name in allowList) this else parent?.untilAllowedParentOrNull(allowList)
}

// resolve compiler specific submodule, for example compiler-plugin-1_7 for Kotlin version 1.7.22
if (kotlinVersion.startsWith(semVer)) {
val coreProject = root.subprojects.singleOrNull { it.name == coreProjectName }
?: error("Expected to find subproject with name '$coreProjectName'")
lazyApi(coreProject)
plugins.withId(Const.KOTLIN_JVM_PLUGIN_ID) {
the<KotlinJvmProjectExtension>().sourceSets.applyCompilerSpecificSourceSets()
}

root.lazyApi(this)
}
}
}
plugins.withId(Const.KOTLIN_MULTIPLATFORM_PLUGIN_ID) {
the<KotlinMultiplatformExtension>().sourceSets.applyCompilerSpecificSourceSets()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet

/**
* Use this to add a specific dependency to a source set, depending on a Kotlin version
*/
fun KotlinSourceSet.vsDependencies(
vsSourceSetDir: String,
configure: KotlinDependencyHandler.() -> Unit,
) {
kotlin.srcDirs.find { it.name == vsSourceSetDir }?.apply {
dependencies(configure)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import java.nio.file.Path

object Const {
const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform"
const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm"

const val CORE_SOURCE_SET = "core"
const val LATEST_SOURCE_SET = "latest"

const val RESOURCES = "resources"
}

fun capitalize(string: String): String {
if (string.isEmpty()) {
return ""
}
val firstChar = string[0]
return string.replaceFirst(firstChar, Character.toTitleCase(firstChar))
}

fun Path.name() = fileName?.toString().orEmpty()
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import java.io.File

// Versioning is used to sort version-specific source sets in the 'first comes more specific' order
// By 'more specific' we mean that '1.7.10' is more specific than '1.7'.
// So [1.7, 1.7.10, 1.9.10, 1.7.22, 1.9, 1, 1.7.0, latest, 1.8]
// will be sorted as [1.7.0, 1.7.10, 1.7.22, 1.7, 1.8, 1.9.10, 1.9, 1, latest]
// It's ok to have version '1'.
// For example, we may have '1.7' and '1' specific source sets.
// That would mean that all 1.7.* versions we compile with the '1.7' source set,
// and 1.8.+ up to 1.9.24 will be with the '1' source set
class CompilerModuleVersion(fullName: String, prefix: String) : Comparable<CompilerModuleVersion> {
// For example, "v_1_7_10" -> "1.7.10"
val version = fullName
.removePrefix(prefix)
.replace('_', '.')

override fun compareTo(other: CompilerModuleVersion): Int {
return when {
version.length == other.version.length -> version.compareTo(other.version)
version.length < other.version.length -> 1
else -> -1
}
}
}

fun Collection<File>.mostSpecificByVersionOrNull(kotlinVersion: String): File? {
return map { it to CompilerModuleVersion(it.name, "v_") }
.sortedBy { (_, semVer) -> semVer }
.firstOrNull { (_, semVer) ->
kotlinVersion.startsWith(semVer.version)
}?.first
}

// matches:
// - latest
// - v_1
// - v_1_9
// - v_1_9_2
// - v_1_9_24
val directoryNameRegex = "^(latest|v(_\\d){1,3}\\d?)$".toRegex()
Loading

0 comments on commit ad99de0

Please sign in to comment.