Skip to content

Commit 87c4832

Browse files
committed
dataconnect: integrate codegen into the gradle build
1 parent ae7bd5b commit 87c4832

File tree

16 files changed

+550
-18
lines changed

16 files changed

+550
-18
lines changed

.github/workflows/android.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ jobs:
1616
java-version: 17
1717
- name: Setup Gradle
1818
uses: gradle/gradle-build-action@v2
19+
- name: Setup Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: 22
1923
- name: Check Snippets
2024
run: python scripts/checksnippets.py
21-
# TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed
22-
- name: Remove Firebase Data Connect from CI
23-
run: python scripts/ci_remove_fdc.py
2425
- name: Copy mock google_services.json
2526
run: ./copy_mock_google_services_json.sh
2627
- name: Build with Gradle (Pull Request)

dataconnect/app/build.gradle.kts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
alias(libs.plugins.kotlin.serialization)
55
alias(libs.plugins.google.services)
66
alias(libs.plugins.compose.compiler)
7+
id("com.google.firebase.example.dataconnect.gradle")
78
}
89

910
android {
@@ -50,13 +51,9 @@ android {
5051
excludes += "/META-INF/{AL2.0,LGPL2.1}"
5152
}
5253
}
53-
sourceSets.getByName("main") {
54-
java.srcDirs("build/generated/sources")
55-
}
5654
}
5755

5856
dependencies {
59-
6057
implementation(libs.androidx.core.ktx)
6158
implementation(libs.androidx.lifecycle.runtime.ktx)
6259
implementation(libs.androidx.lifecycle.viewmodel.compose)
@@ -83,3 +80,13 @@ dependencies {
8380
debugImplementation(libs.androidx.ui.tooling)
8481
debugImplementation(libs.androidx.ui.test.manifest)
8582
}
83+
84+
dataconnect {
85+
// The version of https://www.npmjs.com/package/firebase-tools to use to perform the
86+
// Data Connect code generation.
87+
firebaseToolsVersion = "13.23.0"
88+
89+
// The directory that contains dataconnect.yaml to use as input when performing
90+
// the Data Connect code generation.
91+
dataConnectConfigDir = file("../dataconnect")
92+
}

dataconnect/buildSrc/build.gradle.kts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
// See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin
19+
`kotlin-dsl`
20+
alias(libs.plugins.spotless)
21+
}
22+
23+
java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } }
24+
25+
dependencies {
26+
implementation(libs.android.gradlePlugin.api)
27+
implementation(libs.snakeyaml)
28+
}
29+
30+
gradlePlugin {
31+
plugins {
32+
create("dataconnect") {
33+
id = "com.google.firebase.example.dataconnect.gradle"
34+
implementationClass = "com.google.firebase.example.dataconnect.gradle.DataConnectGradlePlugin"
35+
}
36+
}
37+
}
38+
39+
spotless {
40+
kotlin { ktfmt(libs.versions.ktfmt.get()).googleStyle() }
41+
kotlinGradle {
42+
target("*.gradle.kts")
43+
ktfmt(libs.versions.ktfmt.get()).googleStyle()
44+
}
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pluginManagement {
2+
repositories {
3+
gradlePluginPortal()
4+
google()
5+
mavenCentral()
6+
}
7+
}
8+
9+
dependencyResolutionManagement {
10+
repositories {
11+
google()
12+
mavenCentral()
13+
}
14+
versionCatalogs { create("libs") { from(files("../../gradle/libs.versions.toml")) } }
15+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.example.dataconnect.gradle
18+
19+
import java.io.File
20+
import org.gradle.api.DefaultTask
21+
import org.gradle.api.file.DirectoryProperty
22+
import org.gradle.api.file.RegularFileProperty
23+
import org.gradle.api.tasks.InputDirectory
24+
import org.gradle.api.tasks.InputFile
25+
import org.gradle.api.tasks.Internal
26+
import org.gradle.api.tasks.OutputDirectory
27+
import org.gradle.api.tasks.TaskAction
28+
29+
abstract class CodegenTask : DefaultTask() {
30+
31+
@get:InputDirectory abstract val dataConnectConfigDir: DirectoryProperty
32+
33+
@get:InputFile abstract val firebaseExecutable: RegularFileProperty
34+
35+
@get:OutputDirectory abstract val outputDirectory: DirectoryProperty
36+
37+
@get:Internal abstract val tweakedDataConnectConfigDir: DirectoryProperty
38+
39+
@TaskAction
40+
fun run() {
41+
val dataConnectConfigDir = dataConnectConfigDir.get().asFile
42+
val firebaseExecutable = firebaseExecutable.get().asFile
43+
val outputDirectory = outputDirectory.get().asFile
44+
val tweakedDataConnectConfigDir = tweakedDataConnectConfigDir.get().asFile
45+
46+
logger.info("dataConnectConfigDir: {}", dataConnectConfigDir)
47+
logger.info("firebaseExecutable: {}", firebaseExecutable)
48+
logger.info("outputDirectory: {}", outputDirectory)
49+
logger.info("tweakedDataConnectConfigDir: {}", tweakedDataConnectConfigDir)
50+
51+
project.delete(outputDirectory)
52+
project.delete(tweakedDataConnectConfigDir)
53+
project.mkdir(tweakedDataConnectConfigDir)
54+
55+
project.copy {
56+
from(dataConnectConfigDir)
57+
into(tweakedDataConnectConfigDir)
58+
}
59+
tweakConnectorYamlFiles(tweakedDataConnectConfigDir, outputDirectory.absolutePath)
60+
61+
runCommand(File(tweakedDataConnectConfigDir, "generate.log.txt")) {
62+
commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate")
63+
// Specify a fake project because dataconnect:sdk:generate unnecessarily
64+
// requires one. The actual value does not matter.
65+
args("--project", "zzyzx")
66+
workingDir(tweakedDataConnectConfigDir)
67+
}
68+
}
69+
70+
internal fun configureFrom(providers: MyVariantProviders) {
71+
dataConnectConfigDir.set(providers.dataConnectConfigDir)
72+
firebaseExecutable.set(providers.firebaseExecutable)
73+
tweakedDataConnectConfigDir.set(providers.buildDirectory.map { it.dir("config") })
74+
}
75+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.example.dataconnect.gradle
18+
19+
import java.io.File
20+
import org.gradle.api.Task
21+
import org.gradle.process.ExecSpec
22+
23+
internal fun Task.runCommand(logFile: File, configure: ExecSpec.() -> Unit) {
24+
val effectiveLogFile = if (logger.isInfoEnabled) null else logFile
25+
val result =
26+
effectiveLogFile?.outputStream().use { logStream ->
27+
project.runCatching {
28+
exec {
29+
isIgnoreExitValue = false
30+
if (logStream !== null) {
31+
standardOutput = logStream
32+
errorOutput = logStream
33+
}
34+
configure(this)
35+
}
36+
}
37+
}
38+
result.onFailure { exception ->
39+
effectiveLogFile?.let { logger.warn("{}", it.readText()) }
40+
throw exception
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.example.dataconnect.gradle
18+
19+
import java.io.File
20+
import org.gradle.api.GradleException
21+
import org.gradle.api.Task
22+
import org.yaml.snakeyaml.Yaml
23+
24+
internal fun Task.tweakConnectorYamlFiles(dir: File, newOutputDir: String) {
25+
logger.info("Tweaking connector.yaml files in {}", dir.absolutePath)
26+
dir.walk().forEach { file ->
27+
if (file.isFile && file.name == "connector.yaml") {
28+
tweakConnectorYamlFile(file, newOutputDir)
29+
} else {
30+
logger.debug("skipping file: {}", file.absolutePath)
31+
}
32+
}
33+
}
34+
35+
internal fun Task.tweakConnectorYamlFile(file: File, newOutputDir: String) {
36+
logger.info("Tweaking connector.yaml file: {}", file.absolutePath)
37+
38+
fun Map<*, *>.withTweakedKotlinSdk() =
39+
filterKeys { it == "kotlinSdk" }
40+
.mapValues { (_, value) ->
41+
val kotlinSdkMap =
42+
value as? Map<*, *>
43+
?: throw GradleException(
44+
"Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " +
45+
(if (value === null) "null" else value::class.qualifiedName) +
46+
", but expected ${Map::class.qualifiedName} " +
47+
"(error code m697s27yxn)"
48+
)
49+
kotlinSdkMap.mapValues { (key, value) ->
50+
if (key == "outputDir") {
51+
newOutputDir
52+
} else {
53+
value
54+
}
55+
}
56+
}
57+
58+
fun Map<*, *>.withTweakedGenerateNode() = mapValues { (key, value) ->
59+
if (key != "generate") {
60+
value
61+
} else {
62+
val generateMap =
63+
value as? Map<*, *>
64+
?: throw GradleException(
65+
"Parsing ${file.absolutePath} failed: \"generate\" is " +
66+
(if (value === null) "null" else value::class.qualifiedName) +
67+
", but expected ${Map::class.qualifiedName} " +
68+
"(error code 9c2p857gq6)"
69+
)
70+
generateMap.withTweakedKotlinSdk()
71+
}
72+
}
73+
74+
val yaml = Yaml()
75+
val rootObject = file.reader(Charsets.UTF_8).use { reader -> yaml.load<Any?>(reader) }
76+
77+
val rootMap =
78+
rootObject as? Map<*, *>
79+
?: throw GradleException(
80+
"Parsing ${file.absolutePath} failed: root is " +
81+
(if (rootObject === null) "null" else rootObject::class.qualifiedName) +
82+
", but expected ${Map::class.qualifiedName} " +
83+
"(error code 45dw8jx8jd)"
84+
)
85+
86+
val newRootMap = rootMap.withTweakedGenerateNode()
87+
88+
file.writer(Charsets.UTF_8).use { writer -> yaml.dump(newRootMap, writer) }
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.example.dataconnect.gradle
18+
19+
import java.io.File
20+
21+
interface DataConnectExtension {
22+
var firebaseToolsVersion: String?
23+
var dataConnectConfigDir: File?
24+
}

0 commit comments

Comments
 (0)