-
Notifications
You must be signed in to change notification settings - Fork 573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto transform #5221
base: main
Are you sure you want to change the base?
Auto transform #5221
Changes from 20 commits
e09b2a5
a6af717
e9c40f8
ccb83b9
5cce3b0
d54da7c
92c43b5
aeef091
0d291c9
f35e14e
df0b99a
c4d5fbe
8444cde
132d7d1
e737bac
4f34e82
59850f6
53b4c6d
9b066fb
43eb768
09ae515
1809cc3
6751b29
78e9f35
3b97320
c9cba8c
2d5a375
843f727
ac0611c
5b8b8b0
14f219e
cb28d6a
f2ce0ce
af9ca1d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
package com.google.firebase.gradle.plugins | ||
|
||
import java.io.File | ||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import java.nio.file.StandardCopyOption | ||
import javax.xml.parsers.DocumentBuilderFactory | ||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.provider.Property | ||
import org.gradle.api.tasks.Input | ||
import org.gradle.api.tasks.TaskAction | ||
import org.gradle.configurationcache.extensions.capitalized | ||
import org.gradle.kotlin.dsl.project | ||
import org.w3c.dom.Element | ||
|
||
val KTX_CONTENT = | ||
""" | ||
// Copyright 2023 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.ktx | ||
|
||
import androidx.annotation.Keep | ||
import com.google.firebase.components.Component | ||
import com.google.firebase.components.ComponentRegistrar | ||
import com.google.firebase.platforminfo.LibraryVersionComponent | ||
|
||
internal const val LIBRARY_NAME: String = #{LIBRARY_NAME} | ||
|
||
/** @suppress */ | ||
@Keep | ||
class #{PROJECT_NAME}LoggingRegistrar : ComponentRegistrar { | ||
override fun getComponents(): List<Component<*>> { | ||
return listOf(LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)) | ||
} | ||
} | ||
""" | ||
.trimIndent() | ||
|
||
abstract class PackageTransform : DefaultTask() { | ||
@get:Input abstract val artifactId: Property<String> | ||
@get:Input abstract val groupId: Property<String> | ||
@get:Input abstract val projectPath: Property<String> | ||
|
||
fun getSymbol(line: String): String { | ||
val parts = line.split(" ") | ||
if (parts.get(0) == "val" || parts.get(0) == "class") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use |
||
// Field or class | ||
return parts.get(1).replace(":", "") | ||
} else if (parts.get(0) == "object") { | ||
return parts.get(1) | ||
} else if (parts.get(0) == "fun") { | ||
// Method | ||
var output: String = "" | ||
var ignore = false | ||
for (c in line) { | ||
if (c == ':') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also |
||
ignore = true | ||
} | ||
if (c == ')' || c == ',') { | ||
ignore = false | ||
} | ||
if (c == '=') { | ||
break | ||
} | ||
if (!ignore) { | ||
output += c | ||
} | ||
} | ||
return output.replace("fun", "") | ||
} | ||
return "" | ||
} | ||
|
||
fun copyDir(src: Path, dest: Path) { | ||
Files.walk(src).forEach { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (!Files.isDirectory(it)) { | ||
val destination = dest.resolve(src.relativize(it)) | ||
Files.createDirectories(destination.parent) | ||
Files.copy(it, destination, StandardCopyOption.REPLACE_EXISTING) | ||
} | ||
} | ||
} | ||
|
||
fun deprecateKTX(src: String, pkgName: String) { | ||
File(src).walk().forEach { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
if (it.absolutePath.endsWith(".kt")) { | ||
val lines = File(it.absolutePath).readLines() | ||
val output = mutableListOf<String>() | ||
for (i in 0 until lines.size) { | ||
output.add(lines[i]) | ||
if (lines[i].contains("*/")) { | ||
var symbol = "" | ||
var ctr = i + 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it basically indicates the number of { - number of }.. This is basically to handle the exclude part in the dependencies There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably we can come up with a better naming... not sure what though :-) |
||
while (symbol.isEmpty()) { | ||
symbol = getSymbol(lines[ctr++]).trim() | ||
} | ||
output.add( | ||
"""@Deprecated("Use `${pkgName}${symbol}`", ReplaceWith("${pkgName}${symbol}"))""" | ||
) | ||
} | ||
} | ||
File(it.absolutePath).writeText(output.joinToString("\n")) | ||
} | ||
} | ||
} | ||
fun updateKTXReferences(src: String) { | ||
// Remove all .ktx suffixes essentially. | ||
File(src).walk().forEach { | ||
if (it.absolutePath.endsWith(".kt") && !it.absolutePath.contains("/ktx/")) { | ||
val lines = File(it.absolutePath).readLines().map { x -> x.replace(".ktx", "") } | ||
File(it.absolutePath).delete() | ||
val newFile = File(it.absolutePath) | ||
newFile.writeText(lines.joinToString("\n")) | ||
} | ||
} | ||
} | ||
|
||
fun updatePlatformLogging(src: String) { | ||
File(src).walk().forEach { | ||
if (it.absolutePath.endsWith(".kt")) { | ||
val lines = | ||
File(it.absolutePath).readLines().filter { x -> | ||
!x.contains("contains(LIBRARY_NAME)") && | ||
!x.contains("LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)") | ||
} | ||
File(it.absolutePath).delete() | ||
val newFile = File(it.absolutePath) | ||
newFile.writeText(lines.joinToString("\n")) | ||
} | ||
} | ||
} | ||
|
||
fun readDependencies(path: String): List<String> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can read the whole file in a string, then find There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel free to ignore since it's not going to be merged to master. |
||
val lines = File(path).readLines() | ||
var start = false | ||
var output = mutableListOf<String>() | ||
for (line in lines) { | ||
if (start == true) { | ||
if (line.contains("}")) { | ||
break | ||
} | ||
output.add(line.trim()) | ||
} | ||
|
||
if (line.contains("dependencies {")) { | ||
start = true | ||
} | ||
} | ||
return output.filter { x -> (x.trim().isNotEmpty() && !x.startsWith("//")) } | ||
} | ||
|
||
fun copyManifestComponent(source: String, dest: String) { | ||
val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() | ||
val ktxManifest = documentBuilder.parse(File(source)) | ||
ktxManifest.documentElement.normalize() | ||
val nodeList = ktxManifest.getElementsByTagName("*") | ||
val metadata = | ||
(0..nodeList.length - 1) | ||
.toList() | ||
.map { x -> nodeList.item(x) } | ||
.filter { it.nodeType == Element.ELEMENT_NODE } | ||
.map { x -> (x as Element) } | ||
.filter { it.nodeName == "meta-data" } | ||
.map { x -> | ||
"<meta-data android:name=\"${x.getAttribute("android:name").replace(".ktx.",".")}\" android:value=\"${x.getAttribute("android:value")}\"/>" | ||
} | ||
.joinToString("\n") | ||
|
||
val lines = File(dest).readLines() | ||
var output = mutableListOf<String>() | ||
var inside = false | ||
for (line in lines) { | ||
if ( | ||
line.contains("android:name=\"com.google.firebase.components.ComponentDiscoveryService\"") | ||
) { | ||
inside = true | ||
} | ||
if (inside == true) { | ||
if (line.contains("/>")) { | ||
output.add(line.replace("/>", "> ${metadata} </service>")) | ||
inside = false | ||
continue | ||
} else if (line.contains(">")) { | ||
output.add(line.replace(">", "> ${metadata}")) | ||
inside = false | ||
continue | ||
} | ||
} | ||
output.add(line) | ||
} | ||
File(dest).writeText(output.joinToString("\n")) | ||
} | ||
|
||
@TaskAction | ||
fun run() { | ||
var packageNamePath = "${groupId.get()}.${artifactId.get().split("-")[1]}".replace(".", "/") | ||
packageNamePath = packageNamePath.replace("common", "") | ||
val ktxArtifactPath = "${projectPath.get()}/ktx/src/main/kotlin/${packageNamePath}/ktx" | ||
val ktxPackagePath = "${projectPath.get()}/src/main/java/${packageNamePath}/ktx" | ||
val mainPackagePath = "${projectPath.get()}/src/main/java/${packageNamePath}" | ||
val ktxArtifactTestPath = "${projectPath.get()}/ktx/src/test/kotlin/${packageNamePath}/ktx" | ||
val ktxPackageTestPath = "${projectPath.get()}/src/test/java/${packageNamePath}/ktx" | ||
val mainPackageTestPath = "${projectPath.get()}/src/test/java/${packageNamePath}" | ||
val ktxArtifactAndroidTestPath = | ||
"${projectPath.get()}/ktx/src/androidTest/kotlin/${packageNamePath}/ktx" | ||
val ktxPackageAndroidTestPath = | ||
"${projectPath.get()}/src/androidTest/java/${packageNamePath}/ktx" | ||
val mainPackageAndroidTestPath = "${projectPath.get()}/src/androidTest/java/${packageNamePath}" | ||
copyDir(File(ktxArtifactPath).toPath(), File(ktxPackagePath).toPath()) | ||
copyDir(File(ktxArtifactPath).toPath(), File(mainPackagePath).toPath()) | ||
if (File(ktxArtifactTestPath).exists()) { | ||
copyDir(File(ktxArtifactTestPath).toPath(), File(mainPackageTestPath).toPath()) | ||
copyDir(File(ktxArtifactTestPath).toPath(), File(ktxPackageTestPath).toPath()) | ||
updateKTXReferences(mainPackageTestPath) | ||
} | ||
if (File(ktxArtifactAndroidTestPath).exists()) { | ||
copyDir(File(ktxArtifactAndroidTestPath).toPath(), File(mainPackageAndroidTestPath).toPath()) | ||
copyDir(File(ktxArtifactAndroidTestPath).toPath(), File(ktxPackageAndroidTestPath).toPath()) | ||
updateKTXReferences(mainPackageAndroidTestPath) | ||
} | ||
updateKTXReferences(mainPackagePath) | ||
updatePlatformLogging("${projectPath.get()}/src") | ||
copyManifestComponent( | ||
"${projectPath.get()}/ktx/src/main/AndroidManifest.xml", | ||
"${projectPath.get()}/src/main/AndroidManifest.xml" | ||
) | ||
deprecateKTX(ktxPackagePath, packageNamePath.replace("/", ".")) | ||
var gradlePath = "${projectPath.get()}/${artifactId.get()}.gradle.kts" | ||
var ktxGradlePath = "${projectPath.get()}/ktx/ktx.gradle.kts" | ||
if (!File(gradlePath).exists()) { | ||
gradlePath = "${projectPath.get()}/${artifactId.get()}.gradle" | ||
ktxGradlePath = "${projectPath.get()}/ktx/ktx.gradle" | ||
} | ||
val dependencies = readDependencies(gradlePath) | ||
var ktxDependencies = | ||
readDependencies(ktxGradlePath) | ||
.filter { !it.contains("project(\"${project.path}\")") } | ||
.toMutableList() | ||
val deps = (dependencies.toSet() + ktxDependencies.toSet()).toList() | ||
updateGradleFile(gradlePath, deps) | ||
// KTX changes | ||
updateCode( | ||
ktxArtifactPath, | ||
packageNamePath.replace("/", "."), | ||
"${projectPath.get()}/ktx/src/main/AndroidManifest.xml" | ||
) | ||
ktxDependencies = | ||
ktxDependencies | ||
.filter { | ||
!((it.contains("implementation") || it.contains("api")) && | ||
!it.contains("firebase-components:")) | ||
} | ||
.toMutableList() | ||
// KTX gradle changes | ||
ktxDependencies.add("api(project(\"${project.path}\"))") | ||
updateGradleFile(ktxGradlePath, ktxDependencies) | ||
} | ||
private fun updateGradleFile(gradlePath: String, depsArg: List<String>) { | ||
val deps = depsArg.sorted().map { x -> " " + x } | ||
val lines = File(gradlePath).readLines() | ||
val output = mutableListOf<String>() | ||
for (line in lines) { | ||
if (line.contains("id(\"kotlin-android\")")) { | ||
continue | ||
} | ||
output.add(line) | ||
if (line.contains("id(\"firebase-library\")")) { | ||
output.add(" id(\"kotlin-android\")") | ||
} | ||
if (line.contains("dependencies {")) { | ||
output += deps | ||
output.add("}") | ||
break | ||
} | ||
} | ||
File(gradlePath).writeText(output.joinToString("\n")) | ||
} | ||
private fun extractLibraryName(path: String): String = | ||
File(path) | ||
.readLines() | ||
.filter { x -> x.contains("LIBRARY_NAME:") } | ||
.map { x -> x.split(" ").last() } | ||
.get(0) | ||
private fun updateCode(path: String, pkgName: String, manifestPath: String) { | ||
File(path).walk().forEach { | ||
if (it.absolutePath.endsWith(".kt") && !it.absolutePath.endsWith("Logging.kt")) { | ||
val filePath = it.absolutePath | ||
val projectName = artifactId.get().split("-").map { x -> x.capitalized() }.joinToString("") | ||
val replaceClass: String = | ||
File(filePath) | ||
.readLines() | ||
.filter { it.contains("class") } | ||
.map { x -> x.split(" ").get(1).replace(":", "") } | ||
.get(0) | ||
val loggingPath: String = "${File(filePath).parent}/Logging.kt" | ||
File(loggingPath) | ||
.writeText( | ||
KTX_CONTENT.replace("#{LIBRARY_NAME}", extractLibraryName(filePath)) | ||
.replace("#{PROJECT_NAME}", projectName) | ||
) | ||
val lines = | ||
File(manifestPath).readLines().map { x -> | ||
x.replace(replaceClass, "${projectName}LoggingRegistrar") | ||
} | ||
File(manifestPath).writeText(lines.joinToString("\n")) | ||
File(filePath).delete() | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright