diff --git a/CHANGELOG.md b/CHANGELOG.md index 21266ed160..d937d44e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Change Log ========== +Unreleased +---------- + +### Gradle plugin + +* Fix: generate protos into commonMain again for Kotlin multiplatform projects with an Android target (#3688) + Version 7.0.0-alpha09 --------------------- diff --git a/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/WirePlugin.kt b/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/WirePlugin.kt index 250b7bee1c..a5df5abeb3 100644 --- a/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/WirePlugin.kt +++ b/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/WirePlugin.kt @@ -56,7 +56,16 @@ class WirePlugin : Plugin { // When `android.builtInKotlin` property is enabled, AGP provides Kotlin support for all projects without // requiring users to apply the `org.jetbrains.kotlin.android` plugin. project.extensions.findByName("kotlin")?.let { kotlin.set(true) } - applyWirePlugin() + if (project.extensions.findByType(KotlinMultiplatformExtension::class.java) != null) { + // Multiplatform project with an Android target. Wire generates into commonMain and reads + // the wire {} extension eagerly, so the setup must wait for the build script to be + // evaluated. This path does not use the Android variant API, so afterEvaluate is safe. + project.afterEvaluate { applyWirePlugin() } + } else { + // The Android setup must run now: the variant API used in forEachWireSource requires + // onVariants to be registered before AGP computes its variants. + applyWirePlugin() + } } project.plugins.withId("com.android.application", androidPluginHandler) project.plugins.withId("com.android.library", androidPluginHandler) diff --git a/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/kotlin/SourceRoots.kt b/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/kotlin/SourceRoots.kt index d25772d980..d561534bd9 100644 --- a/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/kotlin/SourceRoots.kt +++ b/wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/kotlin/SourceRoots.kt @@ -41,6 +41,13 @@ internal fun forEachWireSource( sourceHandler: (WireSource) -> Unit, ) { when { + // A multiplatform project can also have an Android target which applies an Android plugin. + // The Kotlin Multiplatform check has to come first: Wire generates once into commonMain, and + // the Android target consumes commonMain like every other target. + hasKotlin && project.extensions.findByType(KotlinMultiplatformExtension::class.java) != null -> { + val extension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) + extension.sourceRoots().forEach(sourceHandler) + } hasAndroid -> { val extension = project.extensions.getByType(AndroidComponentsExtension::class.java) extension.onVariants { variant -> @@ -57,10 +64,6 @@ internal fun forEachWireSource( sourceHandler(source) } } - hasKotlin && project.extensions.findByType(KotlinMultiplatformExtension::class.java) != null -> { - val extension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) - extension.sourceRoots().forEach(sourceHandler) - } hasKotlin -> { val kotlinSourceSets = project.extensions.findByType(KotlinProjectExtension::class.java)?.sourceSets val javaSourceSets = project.extensions.findByType(SourceSetContainer::class.java) diff --git a/wire-gradle-plugin/src/test/kotlin/com/squareup/wire/gradle/WirePluginTest.kt b/wire-gradle-plugin/src/test/kotlin/com/squareup/wire/gradle/WirePluginTest.kt index 7797cf43f5..770091e1d1 100644 --- a/wire-gradle-plugin/src/test/kotlin/com/squareup/wire/gradle/WirePluginTest.kt +++ b/wire-gradle-plugin/src/test/kotlin/com/squareup/wire/gradle/WirePluginTest.kt @@ -1057,6 +1057,31 @@ class WirePluginTest { assertThat(generatedProto2).exists() } + @Test + fun kotlinMultiplatformWithAndroidTarget() { + val fixtureRoot = File("src/test/projects/kotlin-multiplatform-android") + + val result = fixtureGradleRunner( + fixtureRoot, + "assemble", + "--info", + "--no-build-cache", + ).build() + + // Wire generates once into commonMain. The Android target consumes commonMain like every + // other target, so there must be no per-variant generation task. + assertThat(result.task(":generateCommonMainProtos")).isNotNull() + assertThat(result.task(":generateDebugProtos")).isNull() + assertThat(result.task(":generateReleaseProtos")).isNull() + + val generatedProto1 = + File(fixtureRoot, "build/generated/source/wire/com/squareup/dinosaurs/Dinosaur.kt") + val generatedProto2 = + File(fixtureRoot, "build/generated/source/wire/com/squareup/geology/Period.kt") + assertThat(generatedProto1).exists() + assertThat(generatedProto2).exists() + } + private fun fieldsFromProtoSource(generatedProtoSource: String): List { val protoFieldPattern = "@field:WireField.*?(val .*?):" val matchedFields = protoFieldPattern.toRegex(setOf(MULTILINE, DOT_MATCHES_ALL)) diff --git a/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/build.gradle b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/build.gradle new file mode 100644 index 0000000000..104ef7dc2d --- /dev/null +++ b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/build.gradle @@ -0,0 +1,55 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +buildscript { + dependencies { + classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion" + classpath libs.pluginz.kotlin + classpath libs.pluginz.android + } + + repositories { + maven { + url new File(rootDir, "../../../../../build/localMaven").toURI().toString() + } + mavenCentral() + google() + } +} + +apply plugin: 'org.jetbrains.kotlin.multiplatform' +apply plugin: 'com.android.library' +apply plugin: 'com.squareup.wire' + +repositories { + maven { + url new File(rootDir, "../../../../../build/localMaven").toURI().toString() + } + mavenCentral() + google() +} + +android { + namespace = 'com.squareup.wire.kmpandroid' + compileSdk = 36 +} + +kotlin { + jvm() + androidTarget() +} + +wire { + kotlin { + } +} + +tasks.withType(JavaCompile).configureEach { + sourceCompatibility = JavaVersion.VERSION_11.toString() + targetCompatibility = JavaVersion.VERSION_11.toString() +} + +tasks.withType(KotlinCompile).configureEach { + kotlinOptions { + jvmTarget = "11" + } +} diff --git a/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/gradle.properties b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/gradle.properties new file mode 100644 index 0000000000..63ae174737 --- /dev/null +++ b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/gradle.properties @@ -0,0 +1,2 @@ +android.builtInKotlin=false +android.newDsl=false diff --git a/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/settings.gradle b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/settings.gradle new file mode 100644 index 0000000000..7af53a5e7a --- /dev/null +++ b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/settings.gradle @@ -0,0 +1,19 @@ +pluginManagement { + repositories { + maven { + url new File(rootDir, "../../../../../build/localMaven").toURI().toString() + } + google() + mavenCentral() + } +} + +include ':' + +dependencyResolutionManagement { + versionCatalogs { + libs { + from(files('../../../../../gradle/libs.versions.toml')) + } + } +} diff --git a/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/dinosaurs/dinosaur.proto b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/dinosaurs/dinosaur.proto new file mode 100644 index 0000000000..9218b789bb --- /dev/null +++ b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/dinosaurs/dinosaur.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +package squareup.dinosaurs; + +option java_package = "com.squareup.dinosaurs"; + +import "squareup/geology/period.proto"; + +message Dinosaur { + /** Common name of this dinosaur, like "Stegosaurus". */ + optional string name = 1; + + /** URLs with images of this dinosaur. */ + repeated string picture_urls = 2; + + optional double length_meters = 3; + optional double mass_kilograms = 4; + optional squareup.geology.Period period = 5; +} diff --git a/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/geology/period.proto b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/geology/period.proto new file mode 100644 index 0000000000..fc96fbeb73 --- /dev/null +++ b/wire-gradle-plugin/src/test/projects/kotlin-multiplatform-android/src/commonMain/proto/squareup/geology/period.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +package squareup.geology; + +option java_package = "com.squareup.geology"; + +enum Period { + /** 145.5 million years ago — 66.0 million years ago. */ + CRETACEOUS = 1; + + /** 201.3 million years ago — 145.0 million years ago. */ + JURASSIC = 2; + + /** 252.17 million years ago — 201.3 million years ago. */ + TRIASSIC = 3; +}