|
| 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 | +import com.android.build.api.variant.ApplicationAndroidComponentsExtension |
| 18 | +import java.nio.charset.StandardCharsets |
| 19 | + |
| 20 | +plugins { |
| 21 | + // Use whichever versions of these dependencies suit your application. |
| 22 | + // The versions shown here were the latest versions as of December 03, 2024. |
| 23 | + // Note, however, that the version of kotlin("plugin.serialization") _must_, |
| 24 | + // in general, match the version of kotlin("android"). |
| 25 | + id("com.android.application") version "8.7.3" |
| 26 | + id("com.google.gms.google-services") version "4.4.2" |
| 27 | + val kotlinVersion = "2.1.0" |
| 28 | + kotlin("android") version kotlinVersion |
| 29 | + kotlin("plugin.serialization") version kotlinVersion |
| 30 | + |
| 31 | + // The following code in this "plugins" block can be omitted from customer |
| 32 | + // facing documentation as it is an implementation detail of this application. |
| 33 | + id("com.diffplug.spotless") version "7.0.0.BETA4" |
| 34 | +} |
| 35 | + |
| 36 | +dependencies { |
| 37 | + // Use whichever versions of these dependencies suit your application. |
| 38 | + // The versions shown here were the latest versions as of December 03, 2024. |
| 39 | + implementation("com.google.firebase:firebase-dataconnect:16.0.0-beta03") |
| 40 | + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") |
| 41 | + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") |
| 42 | + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3") |
| 43 | + implementation("androidx.appcompat:appcompat:1.7.0") |
| 44 | + implementation("androidx.activity:activity-ktx:1.9.3") |
| 45 | + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7") |
| 46 | + implementation("com.google.android.material:material:1.12.0") |
| 47 | + |
| 48 | + // The following code in this "dependencies" block can be omitted from customer |
| 49 | + // facing documentation as it is an implementation detail of this application. |
| 50 | + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.3") |
| 51 | + implementation("io.kotest:kotest-property:5.9.1") |
| 52 | + implementation("io.kotest.extensions:kotest-property-arbs:2.1.2") |
| 53 | +} |
| 54 | + |
| 55 | +// The remaining code in this file can be omitted from customer facing |
| 56 | +// documentation. It's here just to make things compile and/or configure |
| 57 | +// optional components of the build (e.g. spotless code formatting). |
| 58 | + |
| 59 | +android { |
| 60 | + namespace = "com.google.firebase.dataconnect.minimaldemo" |
| 61 | + compileSdk = 35 |
| 62 | + defaultConfig { |
| 63 | + minSdk = 21 |
| 64 | + targetSdk = 35 |
| 65 | + versionCode = 1 |
| 66 | + versionName = "1.0" |
| 67 | + } |
| 68 | + compileOptions { |
| 69 | + sourceCompatibility = JavaVersion.VERSION_1_8 |
| 70 | + targetCompatibility = JavaVersion.VERSION_1_8 |
| 71 | + isCoreLibraryDesugaringEnabled = true |
| 72 | + } |
| 73 | + buildFeatures.viewBinding = true |
| 74 | + kotlinOptions.jvmTarget = "1.8" |
| 75 | +} |
| 76 | + |
| 77 | +spotless { |
| 78 | + val ktfmtVersion = "0.53" |
| 79 | + kotlin { |
| 80 | + target("**/*.kt") |
| 81 | + targetExclude("build/") |
| 82 | + ktfmt(ktfmtVersion).googleStyle() |
| 83 | + } |
| 84 | + kotlinGradle { |
| 85 | + target("**/*.gradle.kts") |
| 86 | + targetExclude("build/") |
| 87 | + ktfmt(ktfmtVersion).googleStyle() |
| 88 | + } |
| 89 | + json { |
| 90 | + target("**/*.json") |
| 91 | + targetExclude("build/") |
| 92 | + simple().indentWithSpaces(2) |
| 93 | + } |
| 94 | + yaml { |
| 95 | + target("**/*.yaml") |
| 96 | + targetExclude("build/") |
| 97 | + jackson() |
| 98 | + .yamlFeature("INDENT_ARRAYS", true) |
| 99 | + .yamlFeature("MINIMIZE_QUOTES", true) |
| 100 | + .yamlFeature("WRITE_DOC_START_MARKER", false) |
| 101 | + } |
| 102 | + format("xml") { |
| 103 | + target("**/*.xml") |
| 104 | + targetExclude("build/") |
| 105 | + trimTrailingWhitespace() |
| 106 | + indentWithSpaces(2) |
| 107 | + endWithNewline() |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +abstract class DataConnectGenerateSourcesTask : DefaultTask() { |
| 112 | + |
| 113 | + @get:InputDirectory abstract val inputDirectory: DirectoryProperty |
| 114 | + |
| 115 | + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty |
| 116 | + |
| 117 | + @get:Internal abstract val nodeExecutableDirectory: DirectoryProperty |
| 118 | + |
| 119 | + @get:Internal abstract val firebaseCommand: Property<String> |
| 120 | + |
| 121 | + @get:Internal abstract val workDirectory: DirectoryProperty |
| 122 | + |
| 123 | + @get:Inject protected abstract val execOperations: ExecOperations |
| 124 | + |
| 125 | + @get:Inject protected abstract val providerFactory: ProviderFactory |
| 126 | + |
| 127 | + @get:Inject protected abstract val fileSystemOperations: FileSystemOperations |
| 128 | + |
| 129 | + @TaskAction |
| 130 | + fun run(inputChanges: InputChanges) { |
| 131 | + if (inputChanges.isIncremental) { |
| 132 | + val onlyLogFilesChanged = |
| 133 | + inputChanges.getFileChanges(inputDirectory).all { it.file.name.endsWith(".log") } |
| 134 | + if (onlyLogFilesChanged) { |
| 135 | + didWork = false |
| 136 | + return |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + val inputDirectory: File = inputDirectory.get().asFile |
| 141 | + val outputDirectory: File = outputDirectory.get().asFile |
| 142 | + val nodeExecutableDirectory: File? = nodeExecutableDirectory.orNull?.asFile |
| 143 | + val firebaseCommand: String? = firebaseCommand.orNull |
| 144 | + val workDirectory: File = workDirectory.get().asFile |
| 145 | + |
| 146 | + outputDirectory.deleteRecursively() |
| 147 | + outputDirectory.mkdirs() |
| 148 | + workDirectory.deleteRecursively() |
| 149 | + workDirectory.mkdirs() |
| 150 | + |
| 151 | + val newPath: String? = |
| 152 | + if (nodeExecutableDirectory === null) { |
| 153 | + null |
| 154 | + } else { |
| 155 | + val nodeExecutableDirectoryAbsolutePath = nodeExecutableDirectory.absolutePath |
| 156 | + val oldPath = providerFactory.environmentVariable("PATH").orNull |
| 157 | + if (oldPath === null) { |
| 158 | + nodeExecutableDirectoryAbsolutePath |
| 159 | + } else { |
| 160 | + nodeExecutableDirectoryAbsolutePath + File.pathSeparator + oldPath |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + val logFile = |
| 165 | + if (logger.isInfoEnabled) { |
| 166 | + null |
| 167 | + } else { |
| 168 | + File(workDirectory, "dataconnect.sdk.generate.log.txt") |
| 169 | + } |
| 170 | + |
| 171 | + val execResult = |
| 172 | + logFile?.outputStream().use { logStream -> |
| 173 | + execOperations.runCatching { |
| 174 | + exec { |
| 175 | + commandLine(firebaseCommand ?: "firebase", "--debug", "dataconnect:sdk:generate") |
| 176 | + workingDir(inputDirectory) |
| 177 | + isIgnoreExitValue = false |
| 178 | + if (newPath !== null) { |
| 179 | + environment("PATH", newPath) |
| 180 | + } |
| 181 | + if (logStream !== null) { |
| 182 | + standardOutput = logStream |
| 183 | + errorOutput = logStream |
| 184 | + } |
| 185 | + } |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + execResult.onFailure { exception -> |
| 190 | + logFile?.readText(StandardCharsets.UTF_8)?.lines()?.forEach { line -> |
| 191 | + logger.warn("{}", line.trimEnd()) |
| 192 | + } |
| 193 | + throw exception |
| 194 | + } |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | +abstract class CopyDirectoryTask : DefaultTask() { |
| 199 | + |
| 200 | + @get:InputDirectory abstract val srcDirectory: DirectoryProperty |
| 201 | + |
| 202 | + @get:OutputDirectory abstract val destDirectory: DirectoryProperty |
| 203 | + |
| 204 | + @get:Inject protected abstract val fileSystemOperations: FileSystemOperations |
| 205 | + |
| 206 | + @TaskAction |
| 207 | + fun run() { |
| 208 | + val srcDirectory: File = srcDirectory.get().asFile |
| 209 | + val destDirectory: File = destDirectory.get().asFile |
| 210 | + |
| 211 | + logger.info("srcDirectory: {}", srcDirectory.absolutePath) |
| 212 | + logger.info("destDirectory: {}", destDirectory.absolutePath) |
| 213 | + |
| 214 | + destDirectory.deleteRecursively() |
| 215 | + destDirectory.mkdirs() |
| 216 | + |
| 217 | + fileSystemOperations.copy { |
| 218 | + from(srcDirectory) |
| 219 | + into(destDirectory) |
| 220 | + } |
| 221 | + } |
| 222 | +} |
| 223 | + |
| 224 | +run { |
| 225 | + val dataConnectTaskGroupName = "Firebase Data Connect Minimal App" |
| 226 | + val projectDirectory = layout.projectDirectory |
| 227 | + |
| 228 | + val generateSourcesTask = |
| 229 | + tasks.register<DataConnectGenerateSourcesTask>("dataConnectGenerateSources") { |
| 230 | + group = dataConnectTaskGroupName |
| 231 | + description = |
| 232 | + "Run firebase dataconnect:sdk:generate to generate the Data Connect Kotlin SDK sources" |
| 233 | + |
| 234 | + inputDirectory = projectDirectory.dir("firebase") |
| 235 | + outputDirectory = projectDirectory.dir("dataConnectGeneratedSources") |
| 236 | + |
| 237 | + nodeExecutableDirectory = |
| 238 | + project.providers.gradleProperty("dataConnect.minimalApp.nodeExecutableDirectory").map { |
| 239 | + projectDirectory.dir(it) |
| 240 | + } |
| 241 | + firebaseCommand = project.providers.gradleProperty("dataConnect.minimalApp.firebaseCommand") |
| 242 | + |
| 243 | + workDirectory = layout.buildDirectory.dir(name) |
| 244 | + } |
| 245 | + |
| 246 | + val androidComponents = extensions.getByType<ApplicationAndroidComponentsExtension>() |
| 247 | + androidComponents.onVariants { variant -> |
| 248 | + val variantNameTitleCase = variant.name[0].uppercase() + variant.name.substring(1) |
| 249 | + val copyTaskName = "dataConnectCopy${variantNameTitleCase}GeneratedSources" |
| 250 | + val copyTask = |
| 251 | + tasks.register<CopyDirectoryTask>(copyTaskName) { |
| 252 | + group = dataConnectTaskGroupName |
| 253 | + description = |
| 254 | + "Copy the generated Data Connect Kotlin SDK sources into the " + |
| 255 | + "generated code directory for the \"${variant.name}\" variant." |
| 256 | + srcDirectory = generateSourcesTask.flatMap { it.outputDirectory } |
| 257 | + } |
| 258 | + |
| 259 | + variant.sources.java!!.addGeneratedSourceDirectory(copyTask, CopyDirectoryTask::destDirectory) |
| 260 | + } |
| 261 | +} |
0 commit comments