Skip to content

Commit

Permalink
Add compiler version compatibility check.
Browse files Browse the repository at this point in the history
  • Loading branch information
FilipDolnik committed Oct 17, 2023
1 parent 88ae348 commit 8d949d8
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import javax.inject.Inject

abstract class SkieDebugConfiguration @Inject constructor(objects: ObjectFactory) {

val skipSupportedVersionsCheck: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
val dumpSwiftApiBeforeApiNotes: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
val dumpSwiftApiAfterApiNotes: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
val printSkiePerformanceLogs: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
Expand Down
11 changes: 10 additions & 1 deletion SKIE/skie-gradle/plugin-loader/gradle-plugin-loader.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import co.touchlab.skie.gradle.KotlinCompilerVersion
import co.touchlab.skie.gradle.publish.dependencyName
import co.touchlab.skie.gradle.util.enquoted
import co.touchlab.skie.gradle.util.stringListProperty
import co.touchlab.skie.gradle.version.gradleApiVersionDimension
import co.touchlab.skie.gradle.version.kotlinToolingVersionDimension

Expand All @@ -18,6 +20,13 @@ buildConfig {
val gradlePlugin = projects.gradle.gradlePlugin.dependencyProject
// TODO Rename to SKIE_GRADLE_PLUGIN
buildConfigField("String", "SKIE_GRADLE_PLUGIN_DEPENDENCY", "\"${gradlePlugin.dependencyName}\"")

// Workaround for problems with BuildConfig - it incorrectly wraps long strings, and it cannot easily generate a List
val supportedKotlinVersions = project.stringListProperty("versionSupport.supportedKotlinVersions")
.joinToString(", ") { it.enquoted() }
buildConfigField("kotlin.Any", "SUPPORTED_KOTLIN_VERSIONS", "listOf<String>($supportedKotlinVersions)")

buildConfigField("String", "SKIE_VERSION", "\"${project.version}\"")
}

kotlin {
Expand Down Expand Up @@ -53,7 +62,7 @@ tasks.named("compileKotlin").configure {
gradleApiVersions.components.forEach { gradleApiVersion ->
kotlinToolingVersions.components.forEach { kotlinToolingVersion ->
val shimConfiguration = configurations.detachedConfiguration(
projects.gradle.gradlePlugin
projects.gradle.gradlePlugin,
).apply {
attributes {
attribute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.plugin.GradlePluginApiVersion
import org.gradle.api.logging.Logging
import org.gradle.api.model.ObjectFactory
import org.gradle.configurationcache.extensions.serviceOf
import org.gradle.internal.classloader.HashingClassLoaderFactory
Expand All @@ -19,47 +18,107 @@ import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin

@Suppress("unused")
abstract class SkieLoaderPlugin: Plugin<Project> {
abstract class SkieLoaderPlugin : Plugin<Project> {

override fun apply(project: Project): Unit = with(project) {
override fun apply(project: Project) {
// We need to register the extension here, so that Gradle knows the type of it in the build script.
with(SkieExtension) {
createExtension()
val skieExtension = with(SkieExtension) {
project.createExtension()
}

val kotlinVersion = project.kotlinGradlePluginVersionOverride ?: project.kotlinGradlePluginVersion ?: rootProject.kotlinGradlePluginVersion
project.afterEvaluate {
val kotlinVersion = getValidKotlinVersion(skieExtension) ?: return@afterEvaluate

if (kotlinVersion == null) {
log.error("Couldn't find Kotlin plugin version. Make sure you have Kotlin plugin applied or set property 'skie.kgpVersion' in your gradle.properties.")
return
loadSkieGradlePlugin(kotlinVersion)
}
}

private fun Project.getValidKotlinVersion(skieExtension: SkieExtension): KotlinVersion? {
val kotlinVersion = getKotlinVersionString()?.let(::KotlinVersion)

val skipSupportedVersionsCheck = skieExtension.debug.skipSupportedVersionsCheck.get()
if (skipSupportedVersionsCheck) {
logger.error(
"""
Warning:
SKIE skips Kotlin compiler version compatibility check because 'skie.debug.skipSupportedVersionsCheck' is set to true.
Usage of this flag in production is highly discouraged as it may lead to non-obvious compiler errors caused by SKIE incompatibility with the given Kotlin compiler version.
""".trimIndent(),
)
}

val error = when {
kotlinVersion == null ->
"""
SKIE could not infer Kotlin plugin version.
Make sure you have Kotlin Multiplatform plugin applied in the same module as SKIE and that the plugin works - for example by calling the link task that produces the Obj-C framework.
If that is the case, then this problem is likely caused by a bug in SKIE - please report it to the SKIE developers.
You can try to workaround this issue by providing the Kotlin version manually via 'skie.kgpVersion' property in your gradle.properties.
""".trimIndent()
!kotlinVersion.isSupported && !skipSupportedVersionsCheck ->
"""
SKIE ${BuildConfig.SKIE_VERSION} does not support Kotlin $kotlinVersion.
Supported versions are ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS}.
Check if you have the most recent version of SKIE and if so, please wait for the SKIE developers to add support for this Kotlin version.
New Kotlin versions are usually supported within a few days after they are released.
Note that there are no plans for supporting early access versions like Beta, RC, etc.
""".trimIndent()
else -> null
}

if (error != null) {
reportSkieLoaderError(error)

return null
}

return kotlinVersion
}

private fun Project.reportSkieLoaderError(error: String) {
logger.error("Error:\n$error\nSKIE cannot not be used until this error is resolved.\n")

project.gradle.taskGraph.whenReady {
val hasLinkTask = allTasks.any { it.name.startsWith("link") && it.project == project }
val isSkieEnabled = extensions.findByType(SkieExtension::class.java)?.isEnabled?.get() == true

if (hasLinkTask && isSkieEnabled) {
error("$error\nTo proceed with the compilation, please remove or explicitly disable SKIE by adding 'skie { isEnabled.set(false) }' to your Gradle configuration.")
}
}
}

private fun Project.getKotlinVersionString(): String? =
(project.kotlinGradlePluginVersionOverride ?: project.kotlinGradlePluginVersion ?: project.rootProject.kotlinGradlePluginVersion)

private fun Project.loadSkieGradlePlugin(kotlinVersion: KotlinVersion) {
val gradleVersion = GradleVersion.current().version
log.info("Resolving SKIE gradle plugin for Kotlin plugin version $kotlinVersion and Gradle version $gradleVersion")
logger.info("Resolving SKIE gradle plugin for Kotlin plugin version $kotlinVersion and Gradle version $gradleVersion")

KotlinCompilerVersion.registerIn(project.dependencies)
KotlinCompilerVersion.registerIn(buildscript.dependencies)
val skieGradleConfiguration = buildscript.configurations.detachedConfiguration(
buildscript.dependencies.create(BuildConfig.SKIE_GRADLE_PLUGIN_DEPENDENCY)
buildscript.dependencies.create(BuildConfig.SKIE_GRADLE_PLUGIN_DEPENDENCY),
).apply {
this.isCanBeConsumed = false
this.isCanBeResolved = true

exclude(
mapOf(
"group" to "org.jetbrains.kotlin"
)
"group" to "org.jetbrains.kotlin",
),
)

attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(KotlinCompilerVersion.attribute, objects.named(kotlinVersion))
attribute(KotlinCompilerVersion.attribute, objects.named(kotlinVersion.value))
if (GradleVersion.current() >= GradleVersion.version("7.0")) {
attribute(
GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE,
objects.named(GradleVersion.current().version)
objects.named(GradleVersion.current().version),
)
}
}
Expand All @@ -77,7 +136,12 @@ abstract class SkieLoaderPlugin: Plugin<Project> {

val probablySkiePluginClass = skieGradleClassLoader.loadClass("co.touchlab.skie.plugin.SkieGradlePlugin")
if (!Plugin::class.java.isAssignableFrom(probablySkiePluginClass)) {
log.error("Loaded class ${probablySkiePluginClass.name} does not implement ${Plugin::class.java.name}! Please report this to the SKIE team.")
reportSkieLoaderError(
"""
Loaded class ${probablySkiePluginClass.name} does not implement ${Plugin::class.java.name}!
This is a bug in SKIE - please report it to the SKIE developers.
""".trimIndent(),
)
return
}

Expand Down Expand Up @@ -115,7 +179,12 @@ abstract class SkieLoaderPlugin: Plugin<Project> {
private inline fun <reified T : Named> ObjectFactory.named(name: String): T =
named(T::class.java, name)

companion object {
val log = Logging.getLogger(SkieLoaderPlugin::class.java)
@JvmInline
private value class KotlinVersion(val value: String) {

val isSupported: Boolean
get() = (BuildConfig.SUPPORTED_KOTLIN_VERSIONS as List<*>).contains(value)

override fun toString(): String = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ internal object SkieDirectoriesManager {
}
}

// TODO Decide if this should be public or internal

val KotlinNativeLink.skieDirectories: SkieDirectories
get() = SkieDirectories(
project.layout.buildDirectory.dir("skie/${binary.name}/${binary.target.targetName}").get().asFile,
Expand Down
1 change: 1 addition & 0 deletions common-gradle/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ pluginId=co.touchlab.skie

versionSupport.kotlinTooling=1.8.0, 1.8.20, 1.9.0
versionSupport.gradleApi=7.3, 8.0, 8.1
versionSupport.supportedKotlinVersions=1.8.0, 1.8.10, 1.8.20, 1.8.21, 1.8.22, 1.9.0, 1.9.10

touchlab.key=ABCDEFGHIJKLMNOPQRSTUVWXYZ

0 comments on commit 8d949d8

Please sign in to comment.