From e63d396c3ee9e9179f4823e99d239d119102943c Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Nov 2024 15:27:39 -0500 Subject: [PATCH 01/18] Add Firebase Data Connect to CI --- .github/workflows/android.yml | 8 ++++++-- scripts/ci_remove_fdc.py | 8 -------- 2 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 scripts/ci_remove_fdc.py diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8a635beff6..0580d8ed45 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -16,11 +16,15 @@ jobs: java-version: 17 - name: Setup Gradle uses: gradle/gradle-build-action@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install Data Connect Toolkit + run: npm install -g firebase-tools@13.23.0 - name: Check Snippets run: python scripts/checksnippets.py # TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed - - name: Remove Firebase Data Connect from CI - run: python scripts/ci_remove_fdc.py - name: Copy mock google_services.json run: ./copy_mock_google_services_json.sh - name: Build with Gradle (Pull Request) diff --git a/scripts/ci_remove_fdc.py b/scripts/ci_remove_fdc.py deleted file mode 100644 index c425af615b..0000000000 --- a/scripts/ci_remove_fdc.py +++ /dev/null @@ -1,8 +0,0 @@ -# TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed -with open('settings.gradle.kts', 'r') as file: - filedata = file.read() - -filedata = filedata.replace('":dataconnect:app",', '') - -with open('settings.gradle.kts', 'w') as file: - file.write(filedata) From 9b004744f7aad0ca28afd9a9bbc4efd32e576677 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Nov 2024 15:34:58 -0500 Subject: [PATCH 02/18] .github/workflows/android.yml: actually generate the code --- .github/workflows/android.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 0580d8ed45..608e8bed64 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -22,6 +22,8 @@ jobs: node-version: 22 - name: Install Data Connect Toolkit run: npm install -g firebase-tools@13.23.0 + - name: Generate Data Connect Sources + run: cd dataconnect && firebase dataconnect:sdk:generate --project foo - name: Check Snippets run: python scripts/checksnippets.py # TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed From 85b1e731f8304fd11ae3d202e158cf705517978a Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Nov 2024 16:56:43 -0500 Subject: [PATCH 03/18] add codegen to part of the workflow --- .github/workflows/android.yml | 2 - dataconnect/app/build.gradle.kts | 76 +++++++++++++++++++ dataconnect/dataconnect/.gitignore | 1 + .../movie-connector/connector.yaml | 6 +- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 dataconnect/dataconnect/.gitignore diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 608e8bed64..0580d8ed45 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -22,8 +22,6 @@ jobs: node-version: 22 - name: Install Data Connect Toolkit run: npm install -g firebase-tools@13.23.0 - - name: Generate Data Connect Sources - run: cd dataconnect && firebase dataconnect:sdk:generate --project foo - name: Check Snippets run: python scripts/checksnippets.py # TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index 2abc8043fc..6d183dfe43 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -1,3 +1,6 @@ +import com.android.build.api.variant.AndroidComponentsExtension +import java.util.Locale + plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) @@ -83,3 +86,76 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } + +abstract class GenerateDataConnectSourcesTask : DefaultTask() { + @get:InputFiles abstract val inputDirectory: DirectoryProperty + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty + @get:Internal abstract val workDirectory: DirectoryProperty + + @TaskAction + fun run() { + val inputDirectory = inputDirectory.get().asFile + val outputDirectory = outputDirectory.get().asFile + val workDirectory = workDirectory.get().asFile + + project.delete(outputDirectory) + project.delete(workDirectory) + + project.copy { + from(inputDirectory) + into(workDirectory) + } + + val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") + val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") + val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) + val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { + val matchResult = outputFileLineRegex.matchEntire(it) + if (matchResult === null) { + it + } else { + matchResult.groupValues[1] + outputDirectory.absolutePath + } + } + connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) + + val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") + val logFileStream = logFile?.outputStream() + try { + project.exec { + isIgnoreExitValue = false + if (logFileStream !== null) { + standardOutput = logFileStream + errorOutput = logFileStream + } + workingDir(workDirectory) + executable("firebase") + args("--debug") + args("dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + } + } catch (e: Exception) { + logFileStream?.close() + logFile?.forEachLine { logger.error(it.trimEnd()) } + } finally { + logFileStream?.close() + } + } +} + +val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) +androidComponents.onVariants { variant -> + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + val generateCodeTaskName = "generate${variantNameTitleCase}DataConnectSources" + val generateCodeTask = tasks.register(generateCodeTaskName) { + inputDirectory.set(layout.projectDirectory.dir("../dataconnect")) + outputDirectory.set(layout.buildDirectory.dir("generated/dataconnect/${variant.name}")) + workDirectory.set(layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) + } + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateDataConnectSourcesTask::outputDirectory, + ) +} diff --git a/dataconnect/dataconnect/.gitignore b/dataconnect/dataconnect/.gitignore new file mode 100644 index 0000000000..ecb842d705 --- /dev/null +++ b/dataconnect/dataconnect/.gitignore @@ -0,0 +1 @@ +.generated/ diff --git a/dataconnect/dataconnect/movie-connector/connector.yaml b/dataconnect/dataconnect/movie-connector/connector.yaml index 2ba51749dc..eb00af0c66 100644 --- a/dataconnect/dataconnect/movie-connector/connector.yaml +++ b/dataconnect/dataconnect/movie-connector/connector.yaml @@ -8,6 +8,6 @@ generate: kotlinSdk: # Create a custom package name for your generated SDK package: com.google.firebase.dataconnect.movies - # Specify where to store the generated SDK - # We're using the build/ directory so that generated code doesn't get checked into git - outputDir: ../../app/build/generated/sources/com/google/firebase/dataconnect/movies + # Use an arbitrary directory for generating the code, as the custom task in + # app/build.gradle.kts will generate into the appropriate directory. + outputDir: ../.generated From c12f7ca585c867fac345f267a9bc67a6549fcfb9 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Nov 2024 17:29:02 -0500 Subject: [PATCH 04/18] buildSrc added --- dataconnect/buildSrc/build.gradle.kts | 32 +++++++++++++++++++ dataconnect/buildSrc/settings.gradle.kts | 6 ++++ .../gradle/DataConnectGradlePlugin.kt | 27 ++++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 dataconnect/buildSrc/build.gradle.kts create mode 100644 dataconnect/buildSrc/settings.gradle.kts create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts new file mode 100644 index 0000000000..a307281663 --- /dev/null +++ b/dataconnect/buildSrc/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +plugins { + `kotlin-dsl` +} + +dependencies { + implementation(gradleKotlinDsl()) +} + +gradlePlugin { + plugins { + create("dataconnect") { + id = "com.google.firebase.example.dataconnect.gradle" + implementationClass = "com.google.firebase.example.dataconnect.gradle.DataConnectGradlePlugin" + } + } +} diff --git a/dataconnect/buildSrc/settings.gradle.kts b/dataconnect/buildSrc/settings.gradle.kts new file mode 100644 index 0000000000..c7c3c5d5a6 --- /dev/null +++ b/dataconnect/buildSrc/settings.gradle.kts @@ -0,0 +1,6 @@ +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt new file mode 100644 index 0000000000..3ff49d8969 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ +@file:Suppress("UnstableApiUsage") + +package com.google.firebase.example.dataconnect.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project + +@Suppress("unused") +abstract class DataConnectGradlePlugin : Plugin { + override fun apply(project: Project) { + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c44c2304cf..c229d658d9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-all.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 63dfadeba6d781d18bf5005194fe430395881542 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Nov 2024 17:35:18 -0500 Subject: [PATCH 05/18] GenerateDataConnectSourcesTask.kt added --- dataconnect/app/build.gradle.kts | 60 +------------ .../gradle/DataConnectGradlePlugin.kt | 1 - .../gradle/GenerateDataConnectSourcesTask.kt | 88 +++++++++++++++++++ 3 files changed, 90 insertions(+), 59 deletions(-) create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index 6d183dfe43..aa30efff59 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -1,12 +1,13 @@ import com.android.build.api.variant.AndroidComponentsExtension import java.util.Locale - +import com.google.firebase.example.dataconnect.gradle.GenerateDataConnectSourcesTask plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.kotlin.serialization) alias(libs.plugins.google.services) alias(libs.plugins.compose.compiler) + id("com.google.firebase.example.dataconnect.gradle") } android { @@ -87,63 +88,6 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) } -abstract class GenerateDataConnectSourcesTask : DefaultTask() { - @get:InputFiles abstract val inputDirectory: DirectoryProperty - @get:OutputDirectory abstract val outputDirectory: DirectoryProperty - @get:Internal abstract val workDirectory: DirectoryProperty - - @TaskAction - fun run() { - val inputDirectory = inputDirectory.get().asFile - val outputDirectory = outputDirectory.get().asFile - val workDirectory = workDirectory.get().asFile - - project.delete(outputDirectory) - project.delete(workDirectory) - - project.copy { - from(inputDirectory) - into(workDirectory) - } - - val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") - val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") - val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) - val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { - val matchResult = outputFileLineRegex.matchEntire(it) - if (matchResult === null) { - it - } else { - matchResult.groupValues[1] + outputDirectory.absolutePath - } - } - connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) - - val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") - val logFileStream = logFile?.outputStream() - try { - project.exec { - isIgnoreExitValue = false - if (logFileStream !== null) { - standardOutput = logFileStream - errorOutput = logFileStream - } - workingDir(workDirectory) - executable("firebase") - args("--debug") - args("dataconnect:sdk:generate") - // Specify a fake project because dataconnect:sdk:generate unnecessarily - // requires one. The actual value does not matter. - args("--project", "zzyzx") - } - } catch (e: Exception) { - logFileStream?.close() - logFile?.forEachLine { logger.error(it.trimEnd()) } - } finally { - logFileStream?.close() - } - } -} val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) androidComponents.onVariants { variant -> diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 3ff49d8969..0771934a70 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("UnstableApiUsage") package com.google.firebase.example.dataconnect.gradle diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt new file mode 100644 index 0000000000..d1f8a7fcbd --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +abstract class GenerateDataConnectSourcesTask : DefaultTask() { + + @get:InputFiles + abstract val inputDirectory: DirectoryProperty + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @get:Internal + abstract val workDirectory: DirectoryProperty + + @TaskAction + fun run() { + val inputDirectory = inputDirectory.get().asFile + val outputDirectory = outputDirectory.get().asFile + val workDirectory = workDirectory.get().asFile + + project.delete(outputDirectory) + project.delete(workDirectory) + + project.copy { + from(inputDirectory) + into(workDirectory) + } + + val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") + val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") + val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) + val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { + val matchResult = outputFileLineRegex.matchEntire(it) + if (matchResult === null) { + it + } else { + matchResult.groupValues[1] + outputDirectory.absolutePath + } + } + connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) + + val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") + val logFileStream = logFile?.outputStream() + try { + project.exec { + isIgnoreExitValue = false + if (logFileStream !== null) { + standardOutput = logFileStream + errorOutput = logFileStream + } + workingDir(workDirectory) + executable("firebase") + args("--debug") + args("dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + } + } catch (e: Exception) { + logFileStream?.close() + logFile?.forEachLine { logger.error(it.trimEnd()) } + } finally { + logFileStream?.close() + } + } +} From 922d04b2bc4b8048e1e412dbaddf353555f2231d Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Sat, 16 Nov 2024 03:46:30 +0000 Subject: [PATCH 06/18] fix generateDataConnectSources tasks --- dataconnect/app/build.gradle.kts | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index aa30efff59..867f107f25 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -1,6 +1,7 @@ import com.android.build.api.variant.AndroidComponentsExtension -import java.util.Locale import com.google.firebase.example.dataconnect.gradle.GenerateDataConnectSourcesTask +import java.util.Locale + plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) @@ -88,18 +89,24 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) } - val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) +val generateDataConnectSourcesVariantTasks = mutableListOf>() androidComponents.onVariants { variant -> - val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val generateCodeTaskName = "generate${variantNameTitleCase}DataConnectSources" - val generateCodeTask = tasks.register(generateCodeTaskName) { - inputDirectory.set(layout.projectDirectory.dir("../dataconnect")) - outputDirectory.set(layout.buildDirectory.dir("generated/dataconnect/${variant.name}")) - workDirectory.set(layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) - } - variant.sources.java!!.addGeneratedSourceDirectory( - generateCodeTask, - GenerateDataConnectSourcesTask::outputDirectory, - ) + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" + val generateCodeTask = tasks.register(generateCodeTaskName) { + inputDirectory.set(layout.projectDirectory.dir("../dataconnect")) + workDirectory.set(layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) + } + generateDataConnectSourcesVariantTasks.add(generateCodeTask) + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateDataConnectSourcesTask::outputDirectory, + ) +} + +tasks.register("generateDataConnectSources") { + generateDataConnectSourcesVariantTasks.forEach { + dependsOn(it) + } } From 880ee53519230cecc7f2bd65fe5abe28b018eae1 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Sat, 16 Nov 2024 06:24:41 +0000 Subject: [PATCH 07/18] work --- dataconnect/app/build.gradle.kts | 30 ------ dataconnect/buildSrc/build.gradle.kts | 10 +- dataconnect/buildSrc/settings.gradle.kts | 20 +++- .../gradle/DataConnectGradlePlugin.kt | 43 +++++++- .../gradle/GenerateDataConnectSourcesTask.kt | 102 +++++++++--------- gradle/libs.versions.toml | 2 + 6 files changed, 122 insertions(+), 85 deletions(-) diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index 867f107f25..b445003600 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -1,7 +1,3 @@ -import com.android.build.api.variant.AndroidComponentsExtension -import com.google.firebase.example.dataconnect.gradle.GenerateDataConnectSourcesTask -import java.util.Locale - plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) @@ -55,13 +51,9 @@ android { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } - sourceSets.getByName("main") { - java.srcDirs("build/generated/sources") - } } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.viewmodel.compose) @@ -88,25 +80,3 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } - -val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) -val generateDataConnectSourcesVariantTasks = mutableListOf>() -androidComponents.onVariants { variant -> - val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" - val generateCodeTask = tasks.register(generateCodeTaskName) { - inputDirectory.set(layout.projectDirectory.dir("../dataconnect")) - workDirectory.set(layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) - } - generateDataConnectSourcesVariantTasks.add(generateCodeTask) - variant.sources.java!!.addGeneratedSourceDirectory( - generateCodeTask, - GenerateDataConnectSourcesTask::outputDirectory, - ) -} - -tasks.register("generateDataConnectSources") { - generateDataConnectSourcesVariantTasks.forEach { - dependsOn(it) - } -} diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts index a307281663..51aaf461f6 100644 --- a/dataconnect/buildSrc/build.gradle.kts +++ b/dataconnect/buildSrc/build.gradle.kts @@ -15,10 +15,18 @@ */ plugins { - `kotlin-dsl` + `java-gradle-plugin` + alias(libs.plugins.kotlin.jvm) +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } } dependencies { + compileOnly(libs.android.gradlePlugin.api) implementation(gradleKotlinDsl()) } diff --git a/dataconnect/buildSrc/settings.gradle.kts b/dataconnect/buildSrc/settings.gradle.kts index c7c3c5d5a6..ca995b1836 100644 --- a/dataconnect/buildSrc/settings.gradle.kts +++ b/dataconnect/buildSrc/settings.gradle.kts @@ -1,6 +1,24 @@ -dependencyResolutionManagement { +rootProject.name = "buildSrc" + +pluginManagement { repositories { + maven { url = uri("") } + gradlePluginPortal() google() mavenCentral() } } + +dependencyResolutionManagement { + repositories { + maven { url = uri("") } + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../../gradle/libs.versions.toml")) + } + } + +} \ No newline at end of file diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 0771934a70..3ff7b8555c 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -16,11 +16,50 @@ package com.google.firebase.example.dataconnect.gradle +import com.android.build.api.variant.AndroidComponentsExtension import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.logging.Logging +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.register +import java.util.Locale @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { - override fun apply(project: Project) { - } + + private val logger = Logging.getLogger(javaClass) + + override fun apply(project: Project) { + val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) + logger.info("Found AndroidComponentsExtension: ${androidComponents::class.qualifiedName}") + logger.info("Android Gradle Plugin (AGP) Version: ${androidComponents.pluginVersion.version}") + + val generateDataConnectSourcesVariantTasks = mutableListOf>() + androidComponents.onVariants { variant -> + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" + + logger.info("Registering Gradle task: $generateCodeTaskName") + val generateCodeTask = project.tasks.register(generateCodeTaskName) { + inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) + workDirectory.set(project.layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) + } + generateDataConnectSourcesVariantTasks.add(generateCodeTask) + + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateDataConnectSourcesTask::outputDirectory, + ) + } + + androidComponents.selector() + + val generateDataConnectSourcesTaskName = "generateDataConnectSources" + logger.info("Registering Gradle task: $generateDataConnectSourcesTaskName") + project.tasks.register(generateDataConnectSourcesTaskName) { task -> + generateDataConnectSourcesVariantTasks.forEach { + task.dependsOn(it) + } + } + } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt index d1f8a7fcbd..2df353df12 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt @@ -25,64 +25,64 @@ import org.gradle.api.tasks.TaskAction abstract class GenerateDataConnectSourcesTask : DefaultTask() { - @get:InputFiles - abstract val inputDirectory: DirectoryProperty + @get:InputFiles + abstract val inputDirectory: DirectoryProperty - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty - @get:Internal - abstract val workDirectory: DirectoryProperty + @get:Internal + abstract val workDirectory: DirectoryProperty - @TaskAction - fun run() { - val inputDirectory = inputDirectory.get().asFile - val outputDirectory = outputDirectory.get().asFile - val workDirectory = workDirectory.get().asFile + @TaskAction + fun run() { + val inputDirectory = inputDirectory.get().asFile + val outputDirectory = outputDirectory.get().asFile + val workDirectory = workDirectory.get().asFile - project.delete(outputDirectory) - project.delete(workDirectory) + project.delete(outputDirectory) + project.delete(workDirectory) - project.copy { - from(inputDirectory) - into(workDirectory) - } + project.copy { + it.from(inputDirectory) + it.into(workDirectory) + } - val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") - val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") - val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) - val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { - val matchResult = outputFileLineRegex.matchEntire(it) - if (matchResult === null) { - it - } else { - matchResult.groupValues[1] + outputDirectory.absolutePath - } - } - connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) + val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") + val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") + val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) + val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { + val matchResult = outputFileLineRegex.matchEntire(it) + if (matchResult === null) { + it + } else { + matchResult.groupValues[1] + outputDirectory.absolutePath + } + } + connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) - val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") - val logFileStream = logFile?.outputStream() - try { - project.exec { - isIgnoreExitValue = false - if (logFileStream !== null) { - standardOutput = logFileStream - errorOutput = logFileStream + val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") + val logFileStream = logFile?.outputStream() + try { + project.exec { execSpec -> + execSpec.isIgnoreExitValue = false + if (logFileStream !== null) { + execSpec.standardOutput = logFileStream + execSpec.errorOutput = logFileStream + } + execSpec.workingDir(workDirectory) + execSpec.executable("firebase") + execSpec.args("--debug") + execSpec.args("dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + execSpec.args("--project", "zzyzx") + } + } catch (e: Exception) { + logFileStream?.close() + logFile?.forEachLine { logger.error(it.trimEnd()) } + } finally { + logFileStream?.close() } - workingDir(workDirectory) - executable("firebase") - args("--debug") - args("dataconnect:sdk:generate") - // Specify a fake project because dataconnect:sdk:generate unnecessarily - // requires one. The actual value does not matter. - args("--project", "zzyzx") - } - } catch (e: Exception) { - logFileStream?.close() - logFile?.forEachLine { logger.error(it.trimEnd()) } - } finally { - logFileStream?.close() } - } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 00284342f5..6df8b2b70f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ googleServices = "4.4.2" composeNavigation = "2.8.4" [libraries] +android-gradlePlugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "agp" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-lifecycle-runtime-compose-android = { module = "androidx.lifecycle:lifecycle-runtime-compose-android", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" } @@ -44,3 +45,4 @@ jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } From ccb65541acba9a5e818f2d1e657bf2cf73b2e472 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Sat, 16 Nov 2024 07:48:25 +0000 Subject: [PATCH 08/18] it works! --- dataconnect/buildSrc/build.gradle.kts | 6 +-- dataconnect/buildSrc/settings.gradle.kts | 4 -- .../gradle/DataConnectGradlePlugin.kt | 50 +++++++------------ .../gradle/GenerateDataConnectSourcesTask.kt | 24 +++++---- 4 files changed, 33 insertions(+), 51 deletions(-) diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts index 51aaf461f6..0ce3bee03b 100644 --- a/dataconnect/buildSrc/build.gradle.kts +++ b/dataconnect/buildSrc/build.gradle.kts @@ -15,8 +15,7 @@ */ plugins { - `java-gradle-plugin` - alias(libs.plugins.kotlin.jvm) + `kotlin-dsl` // See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin } java { @@ -26,8 +25,7 @@ java { } dependencies { - compileOnly(libs.android.gradlePlugin.api) - implementation(gradleKotlinDsl()) + implementation(libs.android.gradlePlugin.api) } gradlePlugin { diff --git a/dataconnect/buildSrc/settings.gradle.kts b/dataconnect/buildSrc/settings.gradle.kts index ca995b1836..79a581188b 100644 --- a/dataconnect/buildSrc/settings.gradle.kts +++ b/dataconnect/buildSrc/settings.gradle.kts @@ -1,8 +1,5 @@ -rootProject.name = "buildSrc" - pluginManagement { repositories { - maven { url = uri("") } gradlePluginPortal() google() mavenCentral() @@ -11,7 +8,6 @@ pluginManagement { dependencyResolutionManagement { repositories { - maven { url = uri("") } google() mavenCentral() } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 3ff7b8555c..50b39a3687 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -16,50 +16,36 @@ package com.google.firebase.example.dataconnect.gradle -import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.ApplicationVariant import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.logging.Logging -import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.register import java.util.Locale @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { - private val logger = Logging.getLogger(javaClass) - override fun apply(project: Project) { - val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) - logger.info("Found AndroidComponentsExtension: ${androidComponents::class.qualifiedName}") - logger.info("Android Gradle Plugin (AGP) Version: ${androidComponents.pluginVersion.version}") - - val generateDataConnectSourcesVariantTasks = mutableListOf>() - androidComponents.onVariants { variant -> - val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" - - logger.info("Registering Gradle task: $generateCodeTaskName") - val generateCodeTask = project.tasks.register(generateCodeTaskName) { - inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) - workDirectory.set(project.layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) - } - generateDataConnectSourcesVariantTasks.add(generateCodeTask) - - variant.sources.java!!.addGeneratedSourceDirectory( - generateCodeTask, - GenerateDataConnectSourcesTask::outputDirectory, - ) + val androidComponents = project.extensions.getByType() + androidComponents.onVariants { + this@DataConnectGradlePlugin.registerTasks(project, it) } + } - androidComponents.selector() + private fun registerTasks(project: Project, variant: ApplicationVariant) { + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" - val generateDataConnectSourcesTaskName = "generateDataConnectSources" - logger.info("Registering Gradle task: $generateDataConnectSourcesTaskName") - project.tasks.register(generateDataConnectSourcesTaskName) { task -> - generateDataConnectSourcesVariantTasks.forEach { - task.dependsOn(it) - } + val generateCodeTask = project.tasks.register(generateCodeTaskName) { + inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) + workDirectory.set(project.layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) } + + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateDataConnectSourcesTask::outputDirectory, + ) } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt index 2df353df12..8851c0203e 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt @@ -21,6 +21,8 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory +import org.gradle.kotlin.dsl.* + import org.gradle.api.tasks.TaskAction abstract class GenerateDataConnectSourcesTask : DefaultTask() { @@ -44,8 +46,8 @@ abstract class GenerateDataConnectSourcesTask : DefaultTask() { project.delete(workDirectory) project.copy { - it.from(inputDirectory) - it.into(workDirectory) + from(inputDirectory) + into(workDirectory) } val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") @@ -64,19 +66,19 @@ abstract class GenerateDataConnectSourcesTask : DefaultTask() { val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") val logFileStream = logFile?.outputStream() try { - project.exec { execSpec -> - execSpec.isIgnoreExitValue = false + project.exec { + isIgnoreExitValue = false if (logFileStream !== null) { - execSpec.standardOutput = logFileStream - execSpec.errorOutput = logFileStream + standardOutput = logFileStream + errorOutput = logFileStream } - execSpec.workingDir(workDirectory) - execSpec.executable("firebase") - execSpec.args("--debug") - execSpec.args("dataconnect:sdk:generate") + workingDir(workDirectory) + executable("firebase") + args("--debug") + args("dataconnect:sdk:generate") // Specify a fake project because dataconnect:sdk:generate unnecessarily // requires one. The actual value does not matter. - execSpec.args("--project", "zzyzx") + args("--project", "zzyzx") } } catch (e: Exception) { logFileStream?.close() From 4dc4b2a24e8dce88cbad0384673c0969060c2e68 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 01:50:06 +0000 Subject: [PATCH 09/18] GetFirebaseToolsTask.kt added --- .../gradle/DataConnectGradlePlugin.kt | 11 ++- .../gradle/GenerateDataConnectSourcesTask.kt | 41 ++++++----- .../gradle/GetFirebaseToolsTask.kt | 68 +++++++++++++++++++ 3 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 50b39a3687..c214583e26 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -23,18 +23,25 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.register import java.util.Locale +import org.gradle.api.tasks.TaskProvider @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { override fun apply(project: Project) { + val buildDirectory = project.layout.buildDirectory.dir("dataconnect") + val getFirebaseToolsTask = project.tasks.register("getFirebaseTools") { + version.set("13.23.0") + outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) + } + val androidComponents = project.extensions.getByType() androidComponents.onVariants { - this@DataConnectGradlePlugin.registerTasks(project, it) + this@DataConnectGradlePlugin.registerTasks(project, it, getFirebaseToolsTask) } } - private fun registerTasks(project: Project, variant: ApplicationVariant) { + private fun registerTasks(project: Project, variant: ApplicationVariant, getFirebaseToolsTask: TaskProvider) { val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt index 8851c0203e..18feb48fec 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt @@ -16,13 +16,12 @@ package com.google.firebase.example.dataconnect.gradle +import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory -import org.gradle.kotlin.dsl.* - import org.gradle.api.tasks.TaskAction abstract class GenerateDataConnectSourcesTask : DefaultTask() { @@ -63,28 +62,26 @@ abstract class GenerateDataConnectSourcesTask : DefaultTask() { } connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) - val logFile = if (logger.isInfoEnabled) null else workDirectory.resolve("generate.log.txt") - val logFileStream = logFile?.outputStream() - try { - project.exec { - isIgnoreExitValue = false - if (logFileStream !== null) { - standardOutput = logFileStream - errorOutput = logFileStream + val logFile = if (logger.isInfoEnabled) null else File(outputDirectory, "generate.log.txt") + val result = logFile?.outputStream().use { logStream -> + project.runCatching { + exec { + commandLine("firebase", "--debug", "dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + workingDir(outputDirectory) + isIgnoreExitValue = false + if (logStream !== null) { + standardOutput = logStream + errorOutput = logStream + } } - workingDir(workDirectory) - executable("firebase") - args("--debug") - args("dataconnect:sdk:generate") - // Specify a fake project because dataconnect:sdk:generate unnecessarily - // requires one. The actual value does not matter. - args("--project", "zzyzx") } - } catch (e: Exception) { - logFileStream?.close() - logFile?.forEachLine { logger.error(it.trimEnd()) } - } finally { - logFileStream?.close() + } + result.onFailure { exception -> + logFile?.let { logger.warn("{}", it.readText()) } + throw exception } } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt new file mode 100644 index 0000000000..4e28d41ef5 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +import java.io.File +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +abstract class GetFirebaseToolsTask : DefaultTask() { + + @get:Input + abstract val version: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun run() { + val version: String = version.get() + val outputDirectory: File = outputDirectory.get().asFile + + logger.info("version: {}", version) + logger.info("outputDirectory: {}", outputDirectory.absolutePath) + + project.delete(outputDirectory) + project.mkdir(outputDirectory) + + val packageJsonFile = File(outputDirectory, "package.json") + packageJsonFile.writeText("{}", Charsets.UTF_8) + + val npmInstallLogFile = if (logger.isInfoEnabled) null else File(outputDirectory, "install.log.txt") + val npmInstallResult = npmInstallLogFile?.outputStream().use { logStream -> + project.runCatching { + exec { + commandLine("npm", "install", "firebase-tools@$version") + workingDir(outputDirectory) + isIgnoreExitValue = false + if (logStream !== null) { + standardOutput = logStream + errorOutput = logStream + } + } + } + } + npmInstallResult.onFailure { exception -> + npmInstallLogFile?.let { logger.warn("{}", it.readText()) } + throw exception + } + } +} From b42e7cf5516dbaf7a52f75fa00f68456a47cade0 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 03:07:29 +0000 Subject: [PATCH 10/18] work --- .../gradle/DataConnectGradlePlugin.kt | 25 +++++++++---------- ...nectSourcesTask.kt => GenerateCodeTask.kt} | 17 ++++++++++--- ...ToolsTask.kt => SetupFirebaseToolsTask.kt} | 20 ++++++++++++++- 3 files changed, 45 insertions(+), 17 deletions(-) rename dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/{GenerateDataConnectSourcesTask.kt => GenerateCodeTask.kt} (81%) rename dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/{GetFirebaseToolsTask.kt => SetupFirebaseToolsTask.kt} (73%) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index c214583e26..8908a9bf56 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -23,36 +23,35 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.register import java.util.Locale -import org.gradle.api.tasks.TaskProvider @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { override fun apply(project: Project) { - val buildDirectory = project.layout.buildDirectory.dir("dataconnect") - val getFirebaseToolsTask = project.tasks.register("getFirebaseTools") { - version.set("13.23.0") - outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) - } - val androidComponents = project.extensions.getByType() androidComponents.onVariants { - this@DataConnectGradlePlugin.registerTasks(project, it, getFirebaseToolsTask) + this@DataConnectGradlePlugin.registerTasks(project, it) } } - private fun registerTasks(project: Project, variant: ApplicationVariant, getFirebaseToolsTask: TaskProvider) { + private fun registerTasks(project: Project, variant: ApplicationVariant) { val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val generateCodeTaskName = "generateDataConnectSources$variantNameTitleCase" + val buildDirectory = project.layout.buildDirectory.dir("dataconnect/${variant.name}") + + val setupFirebaseToolsTask = project.tasks.register("setupFirebaseTools$variantNameTitleCase") { + version.set("13.23.0") + outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) + } - val generateCodeTask = project.tasks.register(generateCodeTaskName) { + val generateCodeTask = project.tasks.register("generateDataConnectSources${variantNameTitleCase}") { inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) - workDirectory.set(project.layout.buildDirectory.dir("intermediates/dataconnect/${variant.name}")) + firebaseExecutable.set(setupFirebaseToolsTask.flatMap { it.firebaseExecutable }) + workDirectory.set(buildDirectory.map { it.dir("generateCodeTaskWorkDir") }) } variant.sources.java!!.addGeneratedSourceDirectory( generateCodeTask, - GenerateDataConnectSourcesTask::outputDirectory, + GenerateCodeTask::outputDirectory, ) } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt similarity index 81% rename from dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt rename to dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt index 18feb48fec..c4918d9843 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateDataConnectSourcesTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt @@ -19,16 +19,20 @@ package com.google.firebase.example.dataconnect.gradle import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -abstract class GenerateDataConnectSourcesTask : DefaultTask() { +abstract class GenerateCodeTask : DefaultTask() { @get:InputFiles abstract val inputDirectory: DirectoryProperty + @get:InputFiles + abstract val firebaseExecutable: RegularFileProperty + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @@ -38,11 +42,18 @@ abstract class GenerateDataConnectSourcesTask : DefaultTask() { @TaskAction fun run() { val inputDirectory = inputDirectory.get().asFile + val firebaseExecutable = firebaseExecutable.get().asFile val outputDirectory = outputDirectory.get().asFile val workDirectory = workDirectory.get().asFile + logger.info("inputDirectory: {}", inputDirectory) + logger.info("firebaseExecutable: {}", firebaseExecutable) + logger.info("outputDirectory: {}", outputDirectory) + logger.info("workDirectory: {}", workDirectory) + project.delete(outputDirectory) project.delete(workDirectory) + project.mkdir(workDirectory) project.copy { from(inputDirectory) @@ -66,11 +77,11 @@ abstract class GenerateDataConnectSourcesTask : DefaultTask() { val result = logFile?.outputStream().use { logStream -> project.runCatching { exec { - commandLine("firebase", "--debug", "dataconnect:sdk:generate") + commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") // Specify a fake project because dataconnect:sdk:generate unnecessarily // requires one. The actual value does not matter. args("--project", "zzyzx") - workingDir(outputDirectory) + workingDir(workDirectory) isIgnoreExitValue = false if (logStream !== null) { standardOutput = logStream diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt similarity index 73% rename from dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt rename to dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt index 4e28d41ef5..6e55b06fab 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GetFirebaseToolsTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt @@ -19,12 +19,15 @@ package com.google.firebase.example.dataconnect.gradle import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -abstract class GetFirebaseToolsTask : DefaultTask() { +abstract class SetupFirebaseToolsTask : DefaultTask() { @get:Input abstract val version: Property @@ -32,13 +35,20 @@ abstract class GetFirebaseToolsTask : DefaultTask() { @get:OutputDirectory abstract val outputDirectory: DirectoryProperty + @get:OutputFile + val firebaseExecutable: Provider = project.providers.provider { + outputDirectory.file("node_modules/.bin/firebase").get() + } + @TaskAction fun run() { val version: String = version.get() val outputDirectory: File = outputDirectory.get().asFile + val firebaseExecutable: File = firebaseExecutable.get().asFile logger.info("version: {}", version) logger.info("outputDirectory: {}", outputDirectory.absolutePath) + logger.info("firebaseExecutable: {}", firebaseExecutable.absolutePath) project.delete(outputDirectory) project.mkdir(outputDirectory) @@ -64,5 +74,13 @@ abstract class GetFirebaseToolsTask : DefaultTask() { npmInstallLogFile?.let { logger.warn("{}", it.readText()) } throw exception } + + if (!firebaseExecutable.exists()) { + throw SetupFirebaseToolsTaskException("npm install was expected to create the file " + + "${firebaseExecutable.absolutePath}, but it does not exist " + + "(error code mr7dwmhwtv)") + } } + + class SetupFirebaseToolsTaskException(message: String): Exception(message) } From 6120515b855448c3f704fac516602221f7c9c38f Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 03:40:20 +0000 Subject: [PATCH 11/18] gradle plugin works (TM) --- .../gradle/DataConnectGradlePlugin.kt | 42 +++++++++++++------ .../dataconnect/gradle/GenerateCodeTask.kt | 23 +++++----- .../gradle/SetupFirebaseToolsTask.kt | 19 ++------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 8908a9bf56..77bcb40a14 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -18,35 +18,51 @@ package com.google.firebase.example.dataconnect.gradle import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationVariant +import java.util.Locale import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.register -import java.util.Locale @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { override fun apply(project: Project) { + val buildDirectory = project.layout.buildDirectory.dir("dataconnect") + + val setupFirebaseToolsTask = project.tasks.register("setupFirebaseToolsForDataConnect") { + version.set("13.23.0") + outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) + } + val firebaseExecutable = setupFirebaseToolsTask.flatMap { it.firebaseExecutable } + val androidComponents = project.extensions.getByType() - androidComponents.onVariants { - this@DataConnectGradlePlugin.registerTasks(project, it) + androidComponents.onVariants { variant -> + val variantBuildDirectory = buildDirectory.map { it.dir("variants/${variant.name}") } + this@DataConnectGradlePlugin.registerVariantTasks( + project=project, + variant=variant, + buildDirectoryProvider=variantBuildDirectory, + firebaseExecutableProvider=firebaseExecutable, + ) } } - private fun registerTasks(project: Project, variant: ApplicationVariant) { + private fun registerVariantTasks( + project: Project, + variant: ApplicationVariant, + buildDirectoryProvider: Provider, + firebaseExecutableProvider: Provider, + ) { val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val buildDirectory = project.layout.buildDirectory.dir("dataconnect/${variant.name}") - - val setupFirebaseToolsTask = project.tasks.register("setupFirebaseTools$variantNameTitleCase") { - version.set("13.23.0") - outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) - } - val generateCodeTask = project.tasks.register("generateDataConnectSources${variantNameTitleCase}") { + val generateCodeTask = project.tasks.register("generate${variantNameTitleCase}DataConnectSources") { inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) - firebaseExecutable.set(setupFirebaseToolsTask.flatMap { it.firebaseExecutable }) - workDirectory.set(buildDirectory.map { it.dir("generateCodeTaskWorkDir") }) + firebaseExecutable.set(firebaseExecutableProvider) + tweakedConnectorsDirectory.set(buildDirectoryProvider.map { it.dir("tweakedConnectors") }) } variant.sources.java!!.addGeneratedSourceDirectory( diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt index c4918d9843..f2b55945f5 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt @@ -20,47 +20,48 @@ import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction abstract class GenerateCodeTask : DefaultTask() { - @get:InputFiles + @get:InputDirectory abstract val inputDirectory: DirectoryProperty - @get:InputFiles + @get:InputFile abstract val firebaseExecutable: RegularFileProperty @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @get:Internal - abstract val workDirectory: DirectoryProperty + abstract val tweakedConnectorsDirectory: DirectoryProperty @TaskAction fun run() { val inputDirectory = inputDirectory.get().asFile val firebaseExecutable = firebaseExecutable.get().asFile val outputDirectory = outputDirectory.get().asFile - val workDirectory = workDirectory.get().asFile + val tweakedConnectorsDirectory = tweakedConnectorsDirectory.get().asFile logger.info("inputDirectory: {}", inputDirectory) logger.info("firebaseExecutable: {}", firebaseExecutable) logger.info("outputDirectory: {}", outputDirectory) - logger.info("workDirectory: {}", workDirectory) + logger.info("tweakedConnectorsDirectory: {}", tweakedConnectorsDirectory) project.delete(outputDirectory) - project.delete(workDirectory) - project.mkdir(workDirectory) + project.delete(tweakedConnectorsDirectory) + project.mkdir(tweakedConnectorsDirectory) project.copy { from(inputDirectory) - into(workDirectory) + into(tweakedConnectorsDirectory) } - val connectorYamlFile = workDirectory.resolve("movie-connector/connector.yaml") + val connectorYamlFile = tweakedConnectorsDirectory.resolve("movie-connector/connector.yaml") val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { @@ -81,7 +82,7 @@ abstract class GenerateCodeTask : DefaultTask() { // Specify a fake project because dataconnect:sdk:generate unnecessarily // requires one. The actual value does not matter. args("--project", "zzyzx") - workingDir(workDirectory) + workingDir(tweakedConnectorsDirectory) isIgnoreExitValue = false if (logStream !== null) { standardOutput = logStream diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt index 6e55b06fab..5119503bab 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt @@ -23,8 +23,8 @@ import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction abstract class SetupFirebaseToolsTask : DefaultTask() { @@ -35,20 +35,17 @@ abstract class SetupFirebaseToolsTask : DefaultTask() { @get:OutputDirectory abstract val outputDirectory: DirectoryProperty - @get:OutputFile - val firebaseExecutable: Provider = project.providers.provider { - outputDirectory.file("node_modules/.bin/firebase").get() - } + @get:Internal + val firebaseExecutable: Provider get() = + outputDirectory.map { it.file("node_modules/.bin/firebase") } @TaskAction fun run() { val version: String = version.get() val outputDirectory: File = outputDirectory.get().asFile - val firebaseExecutable: File = firebaseExecutable.get().asFile logger.info("version: {}", version) logger.info("outputDirectory: {}", outputDirectory.absolutePath) - logger.info("firebaseExecutable: {}", firebaseExecutable.absolutePath) project.delete(outputDirectory) project.mkdir(outputDirectory) @@ -74,13 +71,5 @@ abstract class SetupFirebaseToolsTask : DefaultTask() { npmInstallLogFile?.let { logger.warn("{}", it.readText()) } throw exception } - - if (!firebaseExecutable.exists()) { - throw SetupFirebaseToolsTaskException("npm install was expected to create the file " + - "${firebaseExecutable.absolutePath}, but it does not exist " + - "(error code mr7dwmhwtv)") - } } - - class SetupFirebaseToolsTaskException(message: String): Exception(message) } From 6e4e925f460bd0e71b954231ecab5f3a1fc66ba9 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 06:33:48 +0000 Subject: [PATCH 12/18] it works again --- dataconnect/buildSrc/build.gradle.kts | 2 + .../dataconnect/gradle/GenerateCodeTask.kt | 77 +++++++++++++++---- gradle/libs.versions.toml | 1 + 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts index 0ce3bee03b..154c2123c4 100644 --- a/dataconnect/buildSrc/build.gradle.kts +++ b/dataconnect/buildSrc/build.gradle.kts @@ -16,6 +16,7 @@ plugins { `kotlin-dsl` // See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin + kotlin("plugin.serialization") version embeddedKotlinVersion } java { @@ -26,6 +27,7 @@ java { dependencies { implementation(libs.android.gradlePlugin.api) + implementation(libs.snakeyaml) } gradlePlugin { diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt index f2b55945f5..55d406e913 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt @@ -25,6 +25,7 @@ import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction +import org.yaml.snakeyaml.Yaml abstract class GenerateCodeTask : DefaultTask() { @@ -61,20 +62,9 @@ abstract class GenerateCodeTask : DefaultTask() { into(tweakedConnectorsDirectory) } - val connectorYamlFile = tweakedConnectorsDirectory.resolve("movie-connector/connector.yaml") - val outputFileLineRegex = Regex("""(\s*outputDir:\s*).*""") - val connectorYamlOriginalLines = connectorYamlFile.readLines(Charsets.UTF_8) - val connectorYamlUpdatedLines = connectorYamlOriginalLines.map { - val matchResult = outputFileLineRegex.matchEntire(it) - if (matchResult === null) { - it - } else { - matchResult.groupValues[1] + outputDirectory.absolutePath - } - } - connectorYamlFile.writeText(connectorYamlUpdatedLines.joinToString("") { it + "\n" }, Charsets.UTF_8) + tweakConnectorYamlFiles(tweakedConnectorsDirectory, outputDirectory.absolutePath) - val logFile = if (logger.isInfoEnabled) null else File(outputDirectory, "generate.log.txt") + val logFile = if (logger.isInfoEnabled) null else File(tweakedConnectorsDirectory, "generate.log.txt") val result = logFile?.outputStream().use { logStream -> project.runCatching { exec { @@ -96,4 +86,65 @@ abstract class GenerateCodeTask : DefaultTask() { throw exception } } + + private fun tweakConnectorYamlFiles(dir: File, newOutputDir: String) { + logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) + dir.walk().forEach { file -> + if (file.isFile && file.name == "connector.yaml") { + tweakConnectorYamlFile(file, newOutputDir) + } else { + logger.debug("skipping file: {}", file.absolutePath) + } + } + } + + private fun tweakConnectorYamlFile(file: File, newOutputDir: String) { + logger.info("Tweaking connector.yaml file: {}", file.absolutePath) + + fun Map<*,*>.withTweakedKotlinSdk() = filterKeys { it == "kotlinSdk" } + .mapValues { (_, value) -> + val kotlinSdkMap = value as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code m697s27yxn)") + kotlinSdkMap.mapValues { (key, value) -> + if (key == "outputDir") { + newOutputDir + } else { + value + } + } + } + + fun Map<*,*>.withTweakedGenerateNode() = mapValues { (key, value) -> + if (key != "generate") { + value + } else { + val generateMap = value as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: \"generate\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 9c2p857gq6)") + generateMap.withTweakedKotlinSdk() + } + } + + val yaml = Yaml() + val rootObject = file.reader(Charsets.UTF_8).use { reader -> + yaml.load(reader) + } + + val rootMap = rootObject as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: root is " + + (if (rootObject === null) "null" else rootObject::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 45dw8jx8jd)") + + val newRootMap = rootMap.withTweakedGenerateNode() + + file.writer(Charsets.UTF_8).use { writer -> + yaml.dump(newRootMap, writer) + } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6df8b2b70f..dd740b961b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,6 +38,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit androidx-material3 = { group = "androidx.compose.material3", name = "material3" } compose-navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "composeNavigation"} kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationCore" } +snakeyaml = { module = "org.yaml:snakeyaml", version = "2.3" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 1fb21ff60e92ff977e619bd6911fbd4807cb1585 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 06:47:00 +0000 Subject: [PATCH 13/18] minor refactor --- .../example/dataconnect/gradle/Commands.kt | 41 +++++++++ .../gradle/ConnectorYamlTweaking.kt | 82 +++++++++++++++++ .../dataconnect/gradle/GenerateCodeTask.kt | 88 ++----------------- .../gradle/SetupFirebaseToolsTask.kt | 20 +---- 4 files changed, 132 insertions(+), 99 deletions(-) create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt new file mode 100644 index 0000000000..efaaa8a8f3 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +import java.io.File +import org.gradle.api.Task +import org.gradle.process.ExecSpec + +fun Task.runCommand(logFile: File, configure: ExecSpec.() -> Unit) { + val effectiveLogFile = if (logger.isInfoEnabled) null else logFile + val result = effectiveLogFile?.outputStream().use { logStream -> + project.runCatching { + exec { + isIgnoreExitValue = false + if (logStream !== null) { + standardOutput = logStream + errorOutput = logStream + } + configure(this) + } + } + } + result.onFailure { exception -> + effectiveLogFile?.let { logger.warn("{}", it.readText()) } + throw exception + } +} \ No newline at end of file diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt new file mode 100644 index 0000000000..ab2fa62a32 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +import java.io.File +import org.gradle.api.Task +import org.yaml.snakeyaml.Yaml + +fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) { + logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) + dir.walk().forEach { file -> + if (file.isFile && file.name == "connector.yaml") { + tweakConnectorYamlFile(file, newOutputDir) + } else { + logger.debug("skipping file: {}", file.absolutePath) + } + } +} + +fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { + logger.info("Tweaking connector.yaml file: {}", file.absolutePath) + + fun Map<*,*>.withTweakedKotlinSdk() = filterKeys { it == "kotlinSdk" } + .mapValues { (_, value) -> + val kotlinSdkMap = value as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code m697s27yxn)") + kotlinSdkMap.mapValues { (key, value) -> + if (key == "outputDir") { + newOutputDir + } else { + value + } + } + } + + fun Map<*,*>.withTweakedGenerateNode() = mapValues { (key, value) -> + if (key != "generate") { + value + } else { + val generateMap = value as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: \"generate\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 9c2p857gq6)") + generateMap.withTweakedKotlinSdk() + } + } + + val yaml = Yaml() + val rootObject = file.reader(Charsets.UTF_8).use { reader -> + yaml.load(reader) + } + + val rootMap = rootObject as? Map<*,*> ?: + throw Exception("Parsing ${file.absolutePath} failed: root is " + + (if (rootObject === null) "null" else rootObject::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 45dw8jx8jd)") + + val newRootMap = rootMap.withTweakedGenerateNode() + + file.writer(Charsets.UTF_8).use { writer -> + yaml.dump(newRootMap, writer) + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt index 55d406e913..f3c4d3a47c 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt @@ -61,90 +61,14 @@ abstract class GenerateCodeTask : DefaultTask() { from(inputDirectory) into(tweakedConnectorsDirectory) } - tweakConnectorYamlFiles(tweakedConnectorsDirectory, outputDirectory.absolutePath) - val logFile = if (logger.isInfoEnabled) null else File(tweakedConnectorsDirectory, "generate.log.txt") - val result = logFile?.outputStream().use { logStream -> - project.runCatching { - exec { - commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") - // Specify a fake project because dataconnect:sdk:generate unnecessarily - // requires one. The actual value does not matter. - args("--project", "zzyzx") - workingDir(tweakedConnectorsDirectory) - isIgnoreExitValue = false - if (logStream !== null) { - standardOutput = logStream - errorOutput = logStream - } - } - } - } - result.onFailure { exception -> - logFile?.let { logger.warn("{}", it.readText()) } - throw exception - } - } - - private fun tweakConnectorYamlFiles(dir: File, newOutputDir: String) { - logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) - dir.walk().forEach { file -> - if (file.isFile && file.name == "connector.yaml") { - tweakConnectorYamlFile(file, newOutputDir) - } else { - logger.debug("skipping file: {}", file.absolutePath) - } - } - } - - private fun tweakConnectorYamlFile(file: File, newOutputDir: String) { - logger.info("Tweaking connector.yaml file: {}", file.absolutePath) - - fun Map<*,*>.withTweakedKotlinSdk() = filterKeys { it == "kotlinSdk" } - .mapValues { (_, value) -> - val kotlinSdkMap = value as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + - (if (value === null) "null" else value::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code m697s27yxn)") - kotlinSdkMap.mapValues { (key, value) -> - if (key == "outputDir") { - newOutputDir - } else { - value - } - } - } - - fun Map<*,*>.withTweakedGenerateNode() = mapValues { (key, value) -> - if (key != "generate") { - value - } else { - val generateMap = value as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: \"generate\" is " + - (if (value === null) "null" else value::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code 9c2p857gq6)") - generateMap.withTweakedKotlinSdk() - } - } - - val yaml = Yaml() - val rootObject = file.reader(Charsets.UTF_8).use { reader -> - yaml.load(reader) - } - - val rootMap = rootObject as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: root is " + - (if (rootObject === null) "null" else rootObject::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code 45dw8jx8jd)") - - val newRootMap = rootMap.withTweakedGenerateNode() - - file.writer(Charsets.UTF_8).use { writer -> - yaml.dump(newRootMap, writer) + runCommand(File(tweakedConnectorsDirectory, "generate.log.txt")) { + commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + workingDir(tweakedConnectorsDirectory) } } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt index 5119503bab..c6ff61b462 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt @@ -53,23 +53,9 @@ abstract class SetupFirebaseToolsTask : DefaultTask() { val packageJsonFile = File(outputDirectory, "package.json") packageJsonFile.writeText("{}", Charsets.UTF_8) - val npmInstallLogFile = if (logger.isInfoEnabled) null else File(outputDirectory, "install.log.txt") - val npmInstallResult = npmInstallLogFile?.outputStream().use { logStream -> - project.runCatching { - exec { - commandLine("npm", "install", "firebase-tools@$version") - workingDir(outputDirectory) - isIgnoreExitValue = false - if (logStream !== null) { - standardOutput = logStream - errorOutput = logStream - } - } - } - } - npmInstallResult.onFailure { exception -> - npmInstallLogFile?.let { logger.warn("{}", it.readText()) } - throw exception + runCommand(File(outputDirectory, "install.log.txt")) { + commandLine("npm", "install", "firebase-tools@$version") + workingDir(outputDirectory) } } } From 2644a79e061f7efcb0fff5745fdaf15621d50d6e Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 07:04:38 +0000 Subject: [PATCH 14/18] ktfmt buildSrc --- dataconnect/buildSrc/build.gradle.kts | 19 ++-- dataconnect/buildSrc/settings.gradle.kts | 27 +++-- .../example/dataconnect/gradle/Commands.kt | 33 ++++--- .../gradle/ConnectorYamlTweaking.kt | 98 ++++++++++--------- .../gradle/DataConnectGradlePlugin.kt | 72 +++++++------- .../dataconnect/gradle/GenerateCodeTask.kt | 63 ++++++------ .../gradle/SetupFirebaseToolsTask.kt | 40 ++++---- gradle/libs.versions.toml | 2 + 8 files changed, 180 insertions(+), 174 deletions(-) diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts index 154c2123c4..36b9fb605d 100644 --- a/dataconnect/buildSrc/build.gradle.kts +++ b/dataconnect/buildSrc/build.gradle.kts @@ -15,15 +15,12 @@ */ plugins { - `kotlin-dsl` // See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin - kotlin("plugin.serialization") version embeddedKotlinVersion + // See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin + `kotlin-dsl` + alias(libs.plugins.spotless) } -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} +java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } dependencies { implementation(libs.android.gradlePlugin.api) @@ -38,3 +35,11 @@ gradlePlugin { } } } + +spotless { + kotlin { ktfmt(libs.versions.ktfmt.get()).googleStyle() } + kotlinGradle { + target("*.gradle.kts") + ktfmt(libs.versions.ktfmt.get()).googleStyle() + } +} diff --git a/dataconnect/buildSrc/settings.gradle.kts b/dataconnect/buildSrc/settings.gradle.kts index 79a581188b..89a5d455e0 100644 --- a/dataconnect/buildSrc/settings.gradle.kts +++ b/dataconnect/buildSrc/settings.gradle.kts @@ -1,20 +1,15 @@ pluginManagement { - repositories { - gradlePluginPortal() - google() - mavenCentral() - } + repositories { + gradlePluginPortal() + google() + mavenCentral() + } } dependencyResolutionManagement { - repositories { - google() - mavenCentral() - } - versionCatalogs { - create("libs") { - from(files("../../gradle/libs.versions.toml")) - } - } - -} \ No newline at end of file + repositories { + google() + mavenCentral() + } + versionCatalogs { create("libs") { from(files("../../gradle/libs.versions.toml")) } } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt index efaaa8a8f3..48a17b4fd6 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt @@ -21,21 +21,22 @@ import org.gradle.api.Task import org.gradle.process.ExecSpec fun Task.runCommand(logFile: File, configure: ExecSpec.() -> Unit) { - val effectiveLogFile = if (logger.isInfoEnabled) null else logFile - val result = effectiveLogFile?.outputStream().use { logStream -> - project.runCatching { - exec { - isIgnoreExitValue = false - if (logStream !== null) { - standardOutput = logStream - errorOutput = logStream - } - configure(this) - } + val effectiveLogFile = if (logger.isInfoEnabled) null else logFile + val result = + effectiveLogFile?.outputStream().use { logStream -> + project.runCatching { + exec { + isIgnoreExitValue = false + if (logStream !== null) { + standardOutput = logStream + errorOutput = logStream + } + configure(this) } + } } - result.onFailure { exception -> - effectiveLogFile?.let { logger.warn("{}", it.readText()) } - throw exception - } -} \ No newline at end of file + result.onFailure { exception -> + effectiveLogFile?.let { logger.warn("{}", it.readText()) } + throw exception + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt index ab2fa62a32..0f1c1acc0a 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt @@ -21,62 +21,68 @@ import org.gradle.api.Task import org.yaml.snakeyaml.Yaml fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) { - logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) - dir.walk().forEach { file -> - if (file.isFile && file.name == "connector.yaml") { - tweakConnectorYamlFile(file, newOutputDir) - } else { - logger.debug("skipping file: {}", file.absolutePath) - } + logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) + dir.walk().forEach { file -> + if (file.isFile && file.name == "connector.yaml") { + tweakConnectorYamlFile(file, newOutputDir) + } else { + logger.debug("skipping file: {}", file.absolutePath) } + } } fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { - logger.info("Tweaking connector.yaml file: {}", file.absolutePath) - - fun Map<*,*>.withTweakedKotlinSdk() = filterKeys { it == "kotlinSdk" } - .mapValues { (_, value) -> - val kotlinSdkMap = value as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + - (if (value === null) "null" else value::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code m697s27yxn)") - kotlinSdkMap.mapValues { (key, value) -> - if (key == "outputDir") { - newOutputDir - } else { - value - } - } - } + logger.info("Tweaking connector.yaml file: {}", file.absolutePath) - fun Map<*,*>.withTweakedGenerateNode() = mapValues { (key, value) -> - if (key != "generate") { + fun Map<*, *>.withTweakedKotlinSdk() = + filterKeys { it == "kotlinSdk" } + .mapValues { (_, value) -> + val kotlinSdkMap = + value as? Map<*, *> + ?: throw Exception( + "Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code m697s27yxn)" + ) + kotlinSdkMap.mapValues { (key, value) -> + if (key == "outputDir") { + newOutputDir + } else { value - } else { - val generateMap = value as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: \"generate\" is " + - (if (value === null) "null" else value::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code 9c2p857gq6)") - generateMap.withTweakedKotlinSdk() + } } - } + } - val yaml = Yaml() - val rootObject = file.reader(Charsets.UTF_8).use { reader -> - yaml.load(reader) + fun Map<*, *>.withTweakedGenerateNode() = mapValues { (key, value) -> + if (key != "generate") { + value + } else { + val generateMap = + value as? Map<*, *> + ?: throw Exception( + "Parsing ${file.absolutePath} failed: \"generate\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 9c2p857gq6)" + ) + generateMap.withTweakedKotlinSdk() } + } - val rootMap = rootObject as? Map<*,*> ?: - throw Exception("Parsing ${file.absolutePath} failed: root is " + - (if (rootObject === null) "null" else rootObject::class.qualifiedName) + - ", but expected ${Map::class.qualifiedName} " + - "(error code 45dw8jx8jd)") + val yaml = Yaml() + val rootObject = file.reader(Charsets.UTF_8).use { reader -> yaml.load(reader) } - val newRootMap = rootMap.withTweakedGenerateNode() + val rootMap = + rootObject as? Map<*, *> + ?: throw Exception( + "Parsing ${file.absolutePath} failed: root is " + + (if (rootObject === null) "null" else rootObject::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 45dw8jx8jd)" + ) - file.writer(Charsets.UTF_8).use { writer -> - yaml.dump(newRootMap, writer) - } + val newRootMap = rootMap.withTweakedGenerateNode() + + file.writer(Charsets.UTF_8).use { writer -> yaml.dump(newRootMap, writer) } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 77bcb40a14..5ea465a930 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -30,44 +30,48 @@ import org.gradle.kotlin.dsl.register @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { - override fun apply(project: Project) { - val buildDirectory = project.layout.buildDirectory.dir("dataconnect") + override fun apply(project: Project) { + val buildDirectory = project.layout.buildDirectory.dir("dataconnect") - val setupFirebaseToolsTask = project.tasks.register("setupFirebaseToolsForDataConnect") { - version.set("13.23.0") - outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) - } - val firebaseExecutable = setupFirebaseToolsTask.flatMap { it.firebaseExecutable } + val setupFirebaseToolsTask = + project.tasks.register("setupFirebaseToolsForDataConnect") { + version.set("13.23.0") + outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) + } + val firebaseExecutable = setupFirebaseToolsTask.flatMap { it.firebaseExecutable } - val androidComponents = project.extensions.getByType() - androidComponents.onVariants { variant -> - val variantBuildDirectory = buildDirectory.map { it.dir("variants/${variant.name}") } - this@DataConnectGradlePlugin.registerVariantTasks( - project=project, - variant=variant, - buildDirectoryProvider=variantBuildDirectory, - firebaseExecutableProvider=firebaseExecutable, - ) - } + val androidComponents = project.extensions.getByType() + androidComponents.onVariants { variant -> + val variantBuildDirectory = buildDirectory.map { it.dir("variants/${variant.name}") } + this@DataConnectGradlePlugin.registerVariantTasks( + project = project, + variant = variant, + buildDirectoryProvider = variantBuildDirectory, + firebaseExecutableProvider = firebaseExecutable, + ) } + } - private fun registerVariantTasks( - project: Project, - variant: ApplicationVariant, - buildDirectoryProvider: Provider, - firebaseExecutableProvider: Provider, - ) { - val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + private fun registerVariantTasks( + project: Project, + variant: ApplicationVariant, + buildDirectoryProvider: Provider, + firebaseExecutableProvider: Provider, + ) { + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } - val generateCodeTask = project.tasks.register("generate${variantNameTitleCase}DataConnectSources") { - inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) - firebaseExecutable.set(firebaseExecutableProvider) - tweakedConnectorsDirectory.set(buildDirectoryProvider.map { it.dir("tweakedConnectors") }) - } + val generateCodeTask = + project.tasks.register( + "generate${variantNameTitleCase}DataConnectSources" + ) { + inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) + firebaseExecutable.set(firebaseExecutableProvider) + tweakedConnectorsDirectory.set(buildDirectoryProvider.map { it.dir("tweakedConnectors") }) + } - variant.sources.java!!.addGeneratedSourceDirectory( - generateCodeTask, - GenerateCodeTask::outputDirectory, - ) - } + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateCodeTask::outputDirectory, + ) + } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt index f3c4d3a47c..ec4767b8e8 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt @@ -25,50 +25,45 @@ import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import org.yaml.snakeyaml.Yaml abstract class GenerateCodeTask : DefaultTask() { - @get:InputDirectory - abstract val inputDirectory: DirectoryProperty + @get:InputDirectory abstract val inputDirectory: DirectoryProperty - @get:InputFile - abstract val firebaseExecutable: RegularFileProperty + @get:InputFile abstract val firebaseExecutable: RegularFileProperty - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty - @get:Internal - abstract val tweakedConnectorsDirectory: DirectoryProperty + @get:Internal abstract val tweakedConnectorsDirectory: DirectoryProperty - @TaskAction - fun run() { - val inputDirectory = inputDirectory.get().asFile - val firebaseExecutable = firebaseExecutable.get().asFile - val outputDirectory = outputDirectory.get().asFile - val tweakedConnectorsDirectory = tweakedConnectorsDirectory.get().asFile + @TaskAction + fun run() { + val inputDirectory = inputDirectory.get().asFile + val firebaseExecutable = firebaseExecutable.get().asFile + val outputDirectory = outputDirectory.get().asFile + val tweakedConnectorsDirectory = tweakedConnectorsDirectory.get().asFile - logger.info("inputDirectory: {}", inputDirectory) - logger.info("firebaseExecutable: {}", firebaseExecutable) - logger.info("outputDirectory: {}", outputDirectory) - logger.info("tweakedConnectorsDirectory: {}", tweakedConnectorsDirectory) + logger.info("inputDirectory: {}", inputDirectory) + logger.info("firebaseExecutable: {}", firebaseExecutable) + logger.info("outputDirectory: {}", outputDirectory) + logger.info("tweakedConnectorsDirectory: {}", tweakedConnectorsDirectory) - project.delete(outputDirectory) - project.delete(tweakedConnectorsDirectory) - project.mkdir(tweakedConnectorsDirectory) + project.delete(outputDirectory) + project.delete(tweakedConnectorsDirectory) + project.mkdir(tweakedConnectorsDirectory) - project.copy { - from(inputDirectory) - into(tweakedConnectorsDirectory) - } - tweakConnectorYamlFiles(tweakedConnectorsDirectory, outputDirectory.absolutePath) + project.copy { + from(inputDirectory) + into(tweakedConnectorsDirectory) + } + tweakConnectorYamlFiles(tweakedConnectorsDirectory, outputDirectory.absolutePath) - runCommand(File(tweakedConnectorsDirectory, "generate.log.txt")) { - commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") - // Specify a fake project because dataconnect:sdk:generate unnecessarily - // requires one. The actual value does not matter. - args("--project", "zzyzx") - workingDir(tweakedConnectorsDirectory) - } + runCommand(File(tweakedConnectorsDirectory, "generate.log.txt")) { + commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + workingDir(tweakedConnectorsDirectory) } + } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt index c6ff61b462..79ecd7c7d0 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt @@ -29,33 +29,31 @@ import org.gradle.api.tasks.TaskAction abstract class SetupFirebaseToolsTask : DefaultTask() { - @get:Input - abstract val version: Property + @get:Input abstract val version: Property - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty - @get:Internal - val firebaseExecutable: Provider get() = - outputDirectory.map { it.file("node_modules/.bin/firebase") } + @get:Internal + val firebaseExecutable: Provider + get() = outputDirectory.map { it.file("node_modules/.bin/firebase") } - @TaskAction - fun run() { - val version: String = version.get() - val outputDirectory: File = outputDirectory.get().asFile + @TaskAction + fun run() { + val version: String = version.get() + val outputDirectory: File = outputDirectory.get().asFile - logger.info("version: {}", version) - logger.info("outputDirectory: {}", outputDirectory.absolutePath) + logger.info("version: {}", version) + logger.info("outputDirectory: {}", outputDirectory.absolutePath) - project.delete(outputDirectory) - project.mkdir(outputDirectory) + project.delete(outputDirectory) + project.mkdir(outputDirectory) - val packageJsonFile = File(outputDirectory, "package.json") - packageJsonFile.writeText("{}", Charsets.UTF_8) + val packageJsonFile = File(outputDirectory, "package.json") + packageJsonFile.writeText("{}", Charsets.UTF_8) - runCommand(File(outputDirectory, "install.log.txt")) { - commandLine("npm", "install", "firebase-tools@$version") - workingDir(outputDirectory) - } + runCommand(File(outputDirectory, "install.log.txt")) { + commandLine("npm", "install", "firebase-tools@$version") + workingDir(outputDirectory) } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dd740b961b..6adec269a7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ activityCompose = "1.9.3" composeBom = "2024.11.00" googleServices = "4.4.2" composeNavigation = "2.8.4" +ktfmt = "0.43" [libraries] android-gradlePlugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "agp" } @@ -47,3 +48,4 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +spotless = { id = "com.diffplug.spotless", version = "7.0.0.BETA3" } From 81db20f2d914bf5b58fb2fa9ab9b3f55e1c22621 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 07:37:16 +0000 Subject: [PATCH 15/18] specify firebase tools version in build.gradle.kts --- dataconnect/app/build.gradle.kts | 6 ++++++ .../gradle/DataConnectExtension.kt | 21 +++++++++++++++++++ .../gradle/DataConnectGradlePlugin.kt | 14 ++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index b445003600..544f237f04 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -80,3 +80,9 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } + +dataconnect { + // The version of https://www.npmjs.com/package/firebase-tools to use to perform the + // Data Connect code generation. + firebaseToolsVersion = "13.23.0" +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt new file mode 100644 index 0000000000..f6a12ff1d4 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +interface DataConnectExtension { + var firebaseToolsVersion: String? +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 5ea465a930..15afee4b29 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -19,6 +19,7 @@ package com.google.firebase.example.dataconnect.gradle import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationVariant import java.util.Locale +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.Directory @@ -33,9 +34,20 @@ abstract class DataConnectGradlePlugin : Plugin { override fun apply(project: Project) { val buildDirectory = project.layout.buildDirectory.dir("dataconnect") + val dataConnectExtension = + project.extensions.create("dataconnect", DataConnectExtension::class.java) + val firebaseToolsVersion: Provider = + project.provider { + dataConnectExtension.firebaseToolsVersion + ?: throw GradleException( + "dataconnect.firebaseToolsVersion must be set in your build.gradle or build.gradle.kts " + + "(error code xbmvkc3mtr)" + ) + } + val setupFirebaseToolsTask = project.tasks.register("setupFirebaseToolsForDataConnect") { - version.set("13.23.0") + version.set(firebaseToolsVersion) outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) } val firebaseExecutable = setupFirebaseToolsTask.flatMap { it.firebaseExecutable } From 12ab778746001b74d41152556d5aa304fceef280 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 09:28:33 +0000 Subject: [PATCH 16/18] works --- dataconnect/app/build.gradle.kts | 4 + .../{GenerateCodeTask.kt => CodegenTask.kt} | 34 +++--- .../example/dataconnect/gradle/Commands.kt | 2 +- .../gradle/ConnectorYamlTweaking.kt | 11 +- .../gradle/DataConnectExtension.kt | 3 + .../gradle/DataConnectGradlePlugin.kt | 50 ++------ ...ToolsTask.kt => FirebaseToolsSetupTask.kt} | 7 +- .../example/dataconnect/gradle/Providers.kt | 107 ++++++++++++++++++ 8 files changed, 159 insertions(+), 59 deletions(-) rename dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/{GenerateCodeTask.kt => CodegenTask.kt} (61%) rename dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/{SetupFirebaseToolsTask.kt => FirebaseToolsSetupTask.kt} (88%) create mode 100644 dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Providers.kt diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index 544f237f04..a5cc12e775 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -85,4 +85,8 @@ dataconnect { // The version of https://www.npmjs.com/package/firebase-tools to use to perform the // Data Connect code generation. firebaseToolsVersion = "13.23.0" + + // The directory that contains dataconnect.yaml to use as input when performing + // the Data Connect code generation. + dataConnectConfigDir = file("../dataconnect") } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/CodegenTask.kt similarity index 61% rename from dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt rename to dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/CodegenTask.kt index ec4767b8e8..90dedc587f 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/GenerateCodeTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/CodegenTask.kt @@ -26,44 +26,50 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -abstract class GenerateCodeTask : DefaultTask() { +abstract class CodegenTask : DefaultTask() { - @get:InputDirectory abstract val inputDirectory: DirectoryProperty + @get:InputDirectory abstract val dataConnectConfigDir: DirectoryProperty @get:InputFile abstract val firebaseExecutable: RegularFileProperty @get:OutputDirectory abstract val outputDirectory: DirectoryProperty - @get:Internal abstract val tweakedConnectorsDirectory: DirectoryProperty + @get:Internal abstract val tweakedDataConnectConfigDir: DirectoryProperty @TaskAction fun run() { - val inputDirectory = inputDirectory.get().asFile + val dataConnectConfigDir = dataConnectConfigDir.get().asFile val firebaseExecutable = firebaseExecutable.get().asFile val outputDirectory = outputDirectory.get().asFile - val tweakedConnectorsDirectory = tweakedConnectorsDirectory.get().asFile + val tweakedDataConnectConfigDir = tweakedDataConnectConfigDir.get().asFile - logger.info("inputDirectory: {}", inputDirectory) + logger.info("dataConnectConfigDir: {}", dataConnectConfigDir) logger.info("firebaseExecutable: {}", firebaseExecutable) logger.info("outputDirectory: {}", outputDirectory) - logger.info("tweakedConnectorsDirectory: {}", tweakedConnectorsDirectory) + logger.info("tweakedDataConnectConfigDir: {}", tweakedDataConnectConfigDir) project.delete(outputDirectory) - project.delete(tweakedConnectorsDirectory) - project.mkdir(tweakedConnectorsDirectory) + project.delete(tweakedDataConnectConfigDir) + project.mkdir(tweakedDataConnectConfigDir) project.copy { - from(inputDirectory) - into(tweakedConnectorsDirectory) + from(dataConnectConfigDir) + into(tweakedDataConnectConfigDir) } - tweakConnectorYamlFiles(tweakedConnectorsDirectory, outputDirectory.absolutePath) + tweakConnectorYamlFiles(tweakedDataConnectConfigDir, outputDirectory.absolutePath) - runCommand(File(tweakedConnectorsDirectory, "generate.log.txt")) { + runCommand(File(tweakedDataConnectConfigDir, "generate.log.txt")) { commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") // Specify a fake project because dataconnect:sdk:generate unnecessarily // requires one. The actual value does not matter. args("--project", "zzyzx") - workingDir(tweakedConnectorsDirectory) + workingDir(tweakedDataConnectConfigDir) } } + + internal fun configureFrom(providers: MyVariantProviders) { + dataConnectConfigDir.set(providers.dataConnectConfigDir) + firebaseExecutable.set(providers.firebaseExecutable) + tweakedDataConnectConfigDir.set(providers.buildDirectory.map { it.dir("config") }) + } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt index 48a17b4fd6..0a53b06ae8 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Commands.kt @@ -20,7 +20,7 @@ import java.io.File import org.gradle.api.Task import org.gradle.process.ExecSpec -fun Task.runCommand(logFile: File, configure: ExecSpec.() -> Unit) { +internal fun Task.runCommand(logFile: File, configure: ExecSpec.() -> Unit) { val effectiveLogFile = if (logger.isInfoEnabled) null else logFile val result = effectiveLogFile?.outputStream().use { logStream -> diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt index 0f1c1acc0a..fbd9dcf09a 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/ConnectorYamlTweaking.kt @@ -17,10 +17,11 @@ package com.google.firebase.example.dataconnect.gradle import java.io.File +import org.gradle.api.GradleException import org.gradle.api.Task import org.yaml.snakeyaml.Yaml -fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) { +internal fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) { logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) dir.walk().forEach { file -> if (file.isFile && file.name == "connector.yaml") { @@ -31,7 +32,7 @@ fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) { } } -fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { +internal fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { logger.info("Tweaking connector.yaml file: {}", file.absolutePath) fun Map<*, *>.withTweakedKotlinSdk() = @@ -39,7 +40,7 @@ fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { .mapValues { (_, value) -> val kotlinSdkMap = value as? Map<*, *> - ?: throw Exception( + ?: throw GradleException( "Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + (if (value === null) "null" else value::class.qualifiedName) + ", but expected ${Map::class.qualifiedName} " + @@ -60,7 +61,7 @@ fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { } else { val generateMap = value as? Map<*, *> - ?: throw Exception( + ?: throw GradleException( "Parsing ${file.absolutePath} failed: \"generate\" is " + (if (value === null) "null" else value::class.qualifiedName) + ", but expected ${Map::class.qualifiedName} " + @@ -75,7 +76,7 @@ fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) { val rootMap = rootObject as? Map<*, *> - ?: throw Exception( + ?: throw GradleException( "Parsing ${file.absolutePath} failed: root is " + (if (rootObject === null) "null" else rootObject::class.qualifiedName) + ", but expected ${Map::class.qualifiedName} " + diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt index f6a12ff1d4..7dadff6d7c 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt @@ -16,6 +16,9 @@ package com.google.firebase.example.dataconnect.gradle +import java.io.File + interface DataConnectExtension { var firebaseToolsVersion: String? + var dataConnectConfigDir: File? } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt index 15afee4b29..57403344ca 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -19,71 +19,45 @@ package com.google.firebase.example.dataconnect.gradle import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationVariant import java.util.Locale -import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.file.Directory -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.newInstance import org.gradle.kotlin.dsl.register @Suppress("unused") abstract class DataConnectGradlePlugin : Plugin { override fun apply(project: Project) { - val buildDirectory = project.layout.buildDirectory.dir("dataconnect") + project.extensions.create("dataconnect", DataConnectExtension::class.java) + val providers = project.objects.newInstance() - val dataConnectExtension = - project.extensions.create("dataconnect", DataConnectExtension::class.java) - val firebaseToolsVersion: Provider = - project.provider { - dataConnectExtension.firebaseToolsVersion - ?: throw GradleException( - "dataconnect.firebaseToolsVersion must be set in your build.gradle or build.gradle.kts " + - "(error code xbmvkc3mtr)" - ) - } - - val setupFirebaseToolsTask = - project.tasks.register("setupFirebaseToolsForDataConnect") { - version.set(firebaseToolsVersion) - outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) - } - val firebaseExecutable = setupFirebaseToolsTask.flatMap { it.firebaseExecutable } + project.tasks.register("setupFirebaseToolsForDataConnect") { + configureFrom(providers) + } val androidComponents = project.extensions.getByType() androidComponents.onVariants { variant -> - val variantBuildDirectory = buildDirectory.map { it.dir("variants/${variant.name}") } - this@DataConnectGradlePlugin.registerVariantTasks( - project = project, - variant = variant, - buildDirectoryProvider = variantBuildDirectory, - firebaseExecutableProvider = firebaseExecutable, - ) + val variantProviders = project.objects.newInstance(variant) + registerVariantTasks(project, variant, variantProviders) } } private fun registerVariantTasks( project: Project, variant: ApplicationVariant, - buildDirectoryProvider: Provider, - firebaseExecutableProvider: Provider, + providers: MyVariantProviders, ) { val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } val generateCodeTask = - project.tasks.register( - "generate${variantNameTitleCase}DataConnectSources" - ) { - inputDirectory.set(project.layout.projectDirectory.dir("../dataconnect")) - firebaseExecutable.set(firebaseExecutableProvider) - tweakedConnectorsDirectory.set(buildDirectoryProvider.map { it.dir("tweakedConnectors") }) + project.tasks.register("generate${variantNameTitleCase}DataConnectSources") { + configureFrom(providers) } variant.sources.java!!.addGeneratedSourceDirectory( generateCodeTask, - GenerateCodeTask::outputDirectory, + CodegenTask::outputDirectory, ) } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/FirebaseToolsSetupTask.kt similarity index 88% rename from dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt rename to dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/FirebaseToolsSetupTask.kt index 79ecd7c7d0..48828f6318 100644 --- a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/SetupFirebaseToolsTask.kt +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/FirebaseToolsSetupTask.kt @@ -27,7 +27,7 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -abstract class SetupFirebaseToolsTask : DefaultTask() { +abstract class FirebaseToolsSetupTask : DefaultTask() { @get:Input abstract val version: Property @@ -56,4 +56,9 @@ abstract class SetupFirebaseToolsTask : DefaultTask() { workingDir(outputDirectory) } } + + internal fun configureFrom(providers: MyProjectProviders) { + version.set(providers.firebaseToolsVersion) + outputDirectory.set(providers.buildDirectory.map { it.dir("firebase-tools") }) + } } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Providers.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Providers.kt new file mode 100644 index 0000000000..dda65d47e0 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/Providers.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2024 Google LLC + * + * 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 com.google.firebase.example.dataconnect.gradle + +import com.android.build.api.variant.ApplicationVariant +import javax.inject.Inject +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.newInstance + +internal open class MyProjectProviders( + projectBuildDirectory: DirectoryProperty, + providerFactory: ProviderFactory, + ext: DataConnectExtension, +) { + + @Suppress("unused") + @Inject + constructor( + project: Project, + ) : this( + projectBuildDirectory = project.layout.buildDirectory, + providerFactory = project.providers, + ext = project.extensions.getByType(), + ) + + val buildDirectory: Provider = projectBuildDirectory.map { it.dir("dataconnect") } + + val firebaseToolsVersion: Provider = + providerFactory.provider { + ext.firebaseToolsVersion + ?: throw GradleException( + "dataconnect.firebaseToolsVersion must be set in your build.gradle or build.gradle.kts " + + "(error code xbmvkc3mtr)" + ) + } +} + +internal open class MyVariantProviders( + variant: ApplicationVariant, + myProjectProviders: MyProjectProviders, + ext: DataConnectExtension, + firebaseToolsSetupTask: FirebaseToolsSetupTask, + objectFactory: ObjectFactory, +) { + + @Suppress("unused") + @Inject + constructor( + variant: ApplicationVariant, + project: Project + ) : this( + variant = variant, + myProjectProviders = project.objects.newInstance(), + ext = project.extensions.getByType(), + firebaseToolsSetupTask = project.firebaseToolsSetupTask, + objectFactory = project.objects, + ) + + val buildDirectory: Provider = + myProjectProviders.buildDirectory.map { it.dir("variants/${variant.name}") } + + val dataConnectConfigDir: Provider = run { + val dir = + ext.dataConnectConfigDir + ?: throw GradleException( + "dataconnect.dataConnectConfigDir must be set in your build.gradle or build.gradle.kts " + + "(error code xbmvkc3mtr)" + ) + objectFactory.directoryProperty().also { property -> property.set(dir) } + } + + val firebaseExecutable: Provider = firebaseToolsSetupTask.firebaseExecutable +} + +private val Project.firebaseToolsSetupTask: FirebaseToolsSetupTask + get() { + val tasks = tasks.filterIsInstance() + if (tasks.size != 1) { + throw GradleException( + "expected exactly 1 FirebaseToolsSetupTask task to be registered, but found " + + "${tasks.size}: [${tasks.map { it.name }.sorted().joinToString(", ")}]" + ) + } + return tasks.single() + } From 91fcb67892c3b3ce6791bbc69fb0f94adc84a830 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 09:43:01 +0000 Subject: [PATCH 17/18] update workflow --- .github/workflows/android.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 0580d8ed45..f89439480c 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -20,11 +20,8 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 - - name: Install Data Connect Toolkit - run: npm install -g firebase-tools@13.23.0 - name: Check Snippets run: python scripts/checksnippets.py - # TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed - name: Copy mock google_services.json run: ./copy_mock_google_services_json.sh - name: Build with Gradle (Pull Request) From 59ec3aca70f6c92d6c8a94cfc3f41707dbb7dea8 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 18 Nov 2024 10:02:22 +0000 Subject: [PATCH 18/18] fix root settings.gradle.kts --- settings.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index af004974bc..6962a01794 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,7 @@ pluginManagement { + includeBuild("dataconnect/buildSrc") { + name = "dataConnectBuildSrc" + } repositories { google() mavenCentral()