Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0

- name: Build modules
run: ./gradlew build koverXmlReportDebug -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace
run: ./gradlew build apiCheck koverXmlReportDebug -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace

- name: Run Screenshot Tests
run: ./gradlew :maps-app:validateDebugScreenshotTest
Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Shared Gradle conventions are in `build-logic/` (included build).
./gradlew :maps-compose:testDebugUnitTest # unit tests for one module
./gradlew koverXmlReportDebug # all unit tests and coverage reports
./gradlew lint # Android Lint across all modules
./gradlew apiCheck # public API matches the committed api/*.api files
./gradlew :maps-app:validateDebugScreenshotTest # validate screenshot tests
```

Expand Down Expand Up @@ -111,6 +112,11 @@ reviews on pull requests, so they stay specific to that tool.
- Avoid `!!`; use null-safe operators.
- Public API changes must be additive and backward compatible; deprecate
before removing.
- The public API of each library is committed in `<module>/api/<module>.api` and
checked by `apiCheck` in CI. After an intentional API change, run
`./gradlew apiDump` and commit the updated file, so the change is visible in
review. The check compares binary signatures, so also look out for renamed
parameters, which break callers using named arguments.

## Pull requests

Expand Down
1 change: 1 addition & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation(libs.dokka.plugin)
implementation(libs.kover.gradle.plugin)
implementation(libs.gradle.maven.publish.plugin)
implementation(libs.binary.compatibility.validator)

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,22 @@
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import kotlinx.validation.KotlinApiBuildTask
import kotlinx.validation.KotlinApiCompareTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Copy
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.register

class PublishingConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.run {
applyPlugins()
configureKover()
configureVanniktechPublishing()
configureBinaryCompatibilityValidator()
}
}

Expand Down Expand Up @@ -93,4 +98,70 @@ class PublishingConventionPlugin : Plugin<Project> {
}
}
}

/**
* Dumps the public API of the release classes to api/<module>.api and checks it on every build,
* so a change to the public API shows up in review and an accidental break fails CI. Run
* `./gradlew apiDump` after an intentional API change and commit the updated file.
*/
private fun Project.configureBinaryCompatibilityValidator() {
val projectName = name
val apiFile = layout.projectDirectory.file("api/$projectName.api")
val buildApiFile = layout.buildDirectory.file("api/$projectName.api")

afterEvaluate {
val bundleTask = tasks.findByName("bundleLibCompileToJarRelease") ?: return@afterEvaluate
val classesJar = layout.buildDirectory.file(
"intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar",
)

val apiBuild = tasks.register<KotlinApiBuildTask>("apiBuild") {
group = "verification"
description = "Builds public API declaration for $projectName."
inputJar.set(classesJar)
outputApiFile.set(buildApiFile)
// The generated R classes are public in bytecode but not part of the API.
val rClasses = listOf(
"com.google.maps.android.compose.R",
"com.google.maps.android.compose.utils.R",
"com.google.maps.android.compose.widgets.R",
)
ignoredClasses.addAll(rClasses)
ignoredClasses.addAll(rClasses.flatMap { r -> R_TYPES.map { type -> "$r$$type" } })
dependsOn(bundleTask)
}

val apiDump = tasks.register<Copy>("apiDump") {
group = "verification"
description = "Syncs public API declarations of $projectName to the project api/ directory."
from(apiBuild.flatMap { it.outputApiFile })
into(apiFile.asFile.parentFile)
dependsOn(apiBuild)
}

val apiCheck = tasks.register<KotlinApiCompareTask>("apiCheck") {
group = "verification"
description =
"Checks public API declarations of $projectName against the committed api/$projectName.api."
projectApiFile.set(apiFile)
generatedApiFile.set(apiBuild.flatMap { it.outputApiFile })
dependsOn(apiBuild)
}

tasks.findByName("check")?.dependsOn(apiCheck)

rootProject.tasks.matching { it.name == "apiDump" }.configureEach {
dependsOn(apiDump)
}
rootProject.tasks.matching { it.name == "apiCheck" }.configureEach {
dependsOn(apiCheck)
}
}
}
}

private val R_TYPES = listOf(
"anim", "animator", "array", "attr", "bool", "color", "dimen", "drawable", "font", "fraction", "id",
"integer", "interpolator", "layout", "menu", "mipmap", "plurals", "raw", "string", "style", "styleable",
"transition", "xml",
)
17 changes: 17 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.spotless)
alias(libs.plugins.binary.compatibility.validator)
}

spotless {
Expand All @@ -58,6 +59,22 @@ spotless {
}
}

// The public API of the library modules is checked against the committed api/<module>.api files.
// The per-module tasks are set up in PublishingConventionPlugin.
apiValidation {
ignoredProjects += listOf("maps-app", "docs")
}

val apiDump = tasks.register("apiDump") {
group = "verification"
description = "Dumps public API declarations for all library modules."
}

val apiCheck = tasks.register("apiCheck") {
group = "verification"
description = "Checks that public API declarations match the committed .api files."
}

val projectArtifactId by extra { project: Project ->
if (project.name in listOf("maps-compose", "maps-compose-widgets", "maps-compose-utils")) {
project.name
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ androidMinSdk = "23"
androidTargetSdk = "37"

agp = "9.4.1"
binaryCompatibilityValidator = "0.18.2"
dependencyAnalysis = "3.19.2"
dokka = "2.2.0"
gradleMavenPublishPlugin = "0.37.0"
Expand Down Expand Up @@ -50,6 +51,7 @@ truth = "1.4.5"
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
gradle-maven-publish-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "gradleMavenPublishPlugin" }
binary-compatibility-validator = { module = "org.jetbrains.kotlinx.binary-compatibility-validator:org.jetbrains.kotlinx.binary-compatibility-validator.gradle.plugin", version.ref = "binaryCompatibilityValidator" }
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
Expand Down Expand Up @@ -95,6 +97,7 @@ test-junit = { module = "junit:junit", version.ref = "junit" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }

[plugins]
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidator" }
android-application = { id = "com.android.application", version.ref = "agp" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
dependency-analysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" }
Expand Down
51 changes: 51 additions & 0 deletions maps-compose-utils/api/maps-compose-utils.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
public final class com/google/maps/android/compose/clustering/ClusteringKt {
public static final fun Clustering (Ljava/util/Collection;Lcom/google/maps/android/clustering/ClusterManager;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
public static final synthetic fun Clustering (Ljava/util/Collection;Lcom/google/maps/android/clustering/ClusterManager;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
public static final synthetic fun Clustering-1-qr28I (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final synthetic fun Clustering-9F6RVxY (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLcom/google/maps/android/clustering/view/ClusterRenderer;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final fun Clustering-9F6RVxY (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final synthetic fun Clustering-9F6RVxY (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;III)V
public static final fun Clustering-wbgrcbg (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLcom/google/maps/android/clustering/view/ClusterRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V
public static final fun Clustering-wbgrcbg (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;III)V
public static final fun ClusteringMarkerProperties-MqvHWfI (Landroidx/compose/ui/geometry/Offset;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
public static final synthetic fun ClusteringMarkerProperties-ueCybiQ (Landroidx/compose/ui/geometry/Offset;Ljava/lang/Float;Ljava/lang/Float;Landroidx/compose/runtime/Composer;II)V
public static final fun getLocalClusteringMarkerProperties ()Landroidx/compose/runtime/ProvidableCompositionLocal;
public static final fun rememberClusterManager (Landroidx/compose/runtime/Composer;I)Lcom/google/maps/android/clustering/ClusterManager;
public static final fun rememberClusterRenderer (Lcom/google/maps/android/clustering/ClusterManager;Landroidx/compose/runtime/Composer;I)Lcom/google/maps/android/clustering/view/ClusterRenderer;
public static final fun rememberClusterRenderer-Cq5_NK8 (Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;JJFFFFLcom/google/maps/android/clustering/ClusterManager;Landroidx/compose/runtime/Composer;II)Lcom/google/maps/android/clustering/view/ClusterRenderer;
}

public final class com/google/maps/android/compose/clustering/ClusteringMarkerProperties {
public static final field $stable I
public fun <init> ()V
public final fun getAnchor-_m7T9-E ()Landroidx/compose/ui/geometry/Offset;
public final fun getContentDescription ()Ljava/lang/String;
public final fun getRotation ()Ljava/lang/Float;
public final fun getZIndex ()Ljava/lang/Float;
}

public final class com/google/maps/android/compose/clustering/ComposableSingletons$ClusteringKt {
public static final field INSTANCE Lcom/google/maps/android/compose/clustering/ComposableSingletons$ClusteringKt;
public fun <init> ()V
public final fun getLambda$-1045318553$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$-260840354$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$-543144580$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$1030848660$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$1311340673$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$1784640720$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$1844422179$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$433743892$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
public final fun getLambda$773099471$maps_compose_utils ()Lkotlin/jvm/functions/Function3;
}

public final class com/google/maps/android/compose/wms/WmsTileOverlayKt {
public static final fun WmsTileOverlay (Lkotlin/jvm/functions/Function5;Lcom/google/maps/android/compose/TileOverlayState;ZFZFLkotlin/jvm/functions/Function1;IILandroidx/compose/runtime/Composer;II)V
}

public final class com/google/maps/android/compose/wms/WmsUrlTileProvider : com/google/android/gms/maps/model/UrlTileProvider {
public static final field $stable I
public fun <init> (IILkotlin/jvm/functions/Function5;)V
public synthetic fun <init> (IILkotlin/jvm/functions/Function5;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getTileUrl (III)Ljava/net/URL;
}

6 changes: 6 additions & 0 deletions maps-compose-widgets/api/maps-compose-widgets.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public final class com/google/maps/android/compose/widgets/ScaleBarKt {
public static final fun DisappearingScaleBar-JXI6SxA (Landroidx/compose/ui/Modifier;FFLcom/google/maps/android/compose/CameraPositionState;JJJILandroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Landroidx/compose/runtime/Composer;II)V
public static final fun ScaleBar-7pV8VcA (Landroidx/compose/ui/Modifier;FFLcom/google/maps/android/compose/CameraPositionState;JJJLandroidx/compose/runtime/Composer;II)V
public static final fun getDarkGray ()J
}

Loading
Loading