Skip to content

Commit

Permalink
Support custom version or coordinates for Compose compiler (#283)
Browse files Browse the repository at this point in the history
Since this project does not build a Kotlin compiler plugin, we should not be forced to do a release for new versions of Kotlin. This property will allow consumers to upgrade thier Kotlin version by specifying a newer JetBrains Compose compiler version, or a totally different set of Maven coordiantes for a custom Compose compiler aritfact.
  • Loading branch information
JakeWharton authored Jul 29, 2023
1 parent 4cd7c87 commit 391874b
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 11 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ subprojects {
checkDependencies true
checkReleaseBuilds false // Full lint runs as part of 'build' task.
}
android.composeOptions {
kotlinCompilerExtensionVersion = libs.androidx.compose.compiler.get().version
}
}

apply plugin: 'com.diffplug.spotless'
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ androidx-test-runner = { module = "androidx.test:runner", version = "1.5.2" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version = "1.7.2" }

androidx-compose-bom = { module = "androidx.compose:compose-bom", version = "2023.06.01" }
androidx-compose-compiler = "androidx.compose.compiler:compiler:1.5.1"
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }

coil-compose = "io.coil-kt:coil-compose:2.4.0"
Expand Down
2 changes: 0 additions & 2 deletions molecule-gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ buildConfig {
}

packageName('app.cash.molecule.gradle')
buildConfigField("String", "composeCompilerGroupId", "\"${libs.jetbrains.compose.compiler.get().module.group}\"")
buildConfigField("String", "composeCompilerArtifactId", "\"${libs.jetbrains.compose.compiler.get().module.name}\"")
buildConfigField("String", "composeCompilerVersion", "\"${libs.jetbrains.compose.compiler.get().version}\"")
buildConfigField("String", "moleculeVersion", "\"${project.version}\"")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.molecule.gradle

import org.gradle.api.provider.Property

interface MoleculeExtension {
/**
* The version of the JetBrains Compose compiler to use, or a Maven coordinate triple of
* the custom Compose compiler to use.
*
* Example: using a custom version of the JetBrains Compose compiler
* ```kotlin
* redwood {
* kotlinCompilerPlugin.set("1.4.8")
* }
* ```
*
* Example: using a custom Maven coordinate for the Compose compiler
* ```kotlin
* redwood {
* kotlinCompilerPlugin.set("com.example:custom-compose-compiler:1.0.0")
* }
* ```
*/
val kotlinCompilerPlugin: Property<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*/
package app.cash.molecule.gradle

import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
Expand All @@ -33,22 +36,28 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption

private const val extensionName = "molecule"
private const val moleculeRuntime = "app.cash.molecule:molecule-runtime:$moleculeVersion"

class MoleculePlugin : KotlinCompilerPluginSupportPlugin {
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true

override fun getCompilerPluginId(): String = "app.cash.molecule"
private abstract class MoleculeExtensionImpl
@Inject constructor(objectFactory: ObjectFactory) : MoleculeExtension {
override val kotlinCompilerPlugin: Property<String> =
objectFactory.property(String::class.java)
.convention(composeCompilerVersion)
}

override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact(
composeCompilerGroupId,
composeCompilerArtifactId,
composeCompilerVersion,
)
class MoleculePlugin : KotlinCompilerPluginSupportPlugin {
private lateinit var extension: MoleculeExtension

override fun apply(target: Project) {
super.apply(target)

extension = target.extensions.create(
MoleculeExtension::class.java,
extensionName,
MoleculeExtensionImpl::class.java,
)

if (target.isInternal() && target.path == ":molecule-runtime") {
// Being lazy and using our own plugin to configure the Compose compiler on our runtime.
// Bail out because otherwise we create a circular dependency reference on ourselves!
Expand Down Expand Up @@ -101,6 +110,26 @@ class MoleculePlugin : KotlinCompilerPluginSupportPlugin {
}
}

override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true

override fun getCompilerPluginId(): String = "app.cash.molecule"

override fun getPluginArtifact(): SubpluginArtifact {
val plugin = extension.kotlinCompilerPlugin.get()
val parts = plugin.split(":")
return when (parts.size) {
1 -> SubpluginArtifact("org.jetbrains.compose.compiler", "compiler", parts[0])
3 -> SubpluginArtifact(parts[0], parts[1], parts[2])
else -> error(
"""
|Illegal format of '$extensionName.${MoleculeExtension::kotlinCompilerPlugin.name}' property.
|Expected format: either '<VERSION>' or '<GROUP_ID>:<ARTIFACT_ID>:<VERSION>'
|Actual value: '$plugin'
""".trimMargin(),
)
}
}

private fun Project.isInternal(): Boolean {
return properties["app.cash.molecule.internal"].toString() == "true"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
buildscript {
dependencies {
classpath "app.cash.molecule:molecule-gradle-plugin:$moleculeVersion"
classpath libs.kotlin.plugin
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
}

apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'app.cash.molecule'

molecule {
// Use the AndroidX Compose compiler instead of JetBrains Compose compiler.
kotlinCompilerPlugin = libs.androidx.compose.compiler.get().toString()
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
libs {
from(files('../../../../../gradle/libs.versions.toml'))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
buildscript {
dependencies {
classpath "app.cash.molecule:molecule-gradle-plugin:$moleculeVersion"
classpath libs.kotlin.plugin
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
}

apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'app.cash.molecule'

molecule {
kotlinCompilerPlugin = 'wrong:format'
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
libs {
from(files('../../../../../gradle/libs.versions.toml'))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
buildscript {
ext.kotlinVersion = '1.8.20'

dependencies {
classpath "app.cash.molecule:molecule-gradle-plugin:$moleculeVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
}

if (kotlinVersion == libs.kotlin.plugin.get().version) {
throw RuntimeException("This test requires a different version of Kotlin then the Molecule build")
}

apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'app.cash.molecule'

molecule {
// Use the JetBrains Compose compiler version for the version of Kotlin used by this project.
kotlinCompilerPlugin = '1.4.8'
}

repositories {
maven {
url "file://${rootDir.absolutePath}/../../../../../build/localMaven"
}
mavenCentral()
google()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
libs {
from(files('../../../../../gradle/libs.versions.toml'))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ class MoleculePluginTest {
)
}

@Test fun customCompilerCoordinates() {
createRunner("custom-compiler-coordinates").build()
}

@Test fun customCompilerInvalid() {
val result = createRunner("custom-compiler-invalid").buildAndFail()
assertThat(result.output).contains(
"""
|Illegal format of 'molecule.kotlinCompilerPlugin' property.
|Expected format: either '<VERSION>' or '<GROUP_ID>:<ARTIFACT_ID>:<VERSION>'
|Actual value: 'wrong:format'
""".trimMargin(),
)
}

@Test fun customCompilerVersion() {
createRunner("custom-compiler-version").build()
}

private fun createRunner(fixture: String): GradleRunner {
val fixtureDir = File("src/test/fixtures", fixture)
val gradleRoot = File(fixtureDir, "gradle").also { it.mkdir() }
Expand Down

0 comments on commit 391874b

Please sign in to comment.