Skip to content
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

Refactor Vendor Plugin #6640

Merged
merged 9 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ jobs:
module: ${{ fromJSON(needs.determine_changed.outputs.modules) }}
exclude:
- module: :firebase-firestore
- module: :firebase-functions:ktx

steps:
- uses: actions/[email protected]
Expand Down
30 changes: 17 additions & 13 deletions plugins/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
// Copyright 2018 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.
/*
* Copyright 2018 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 {
alias(libs.plugins.kotlinx.serialization)
Expand All @@ -26,6 +28,8 @@ repositories {
maven(url = "https://plugins.gradle.org/m2/")
}

group = "com.google.firebase"

spotless {
java {
target("src/**/*.java")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@

package com.google.firebase.gradle.plugins

import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.android.build.api.variant.LibraryVariant
import java.io.BufferedOutputStream
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.plugins.PluginManager
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkQueue
Expand Down Expand Up @@ -165,7 +173,7 @@ inline fun <reified T> attributeFrom(name: String) = Attribute.of(name, T::class
* attribute(Attribute.of(name, T::class.java), value)
* ```
*/
inline fun <reified T> AttributeContainer.attribute(name: String, value: T) =
inline fun <reified T : Any> AttributeContainer.attribute(name: String, value: T) =
attribute(attributeFrom(name), value)

/**
Expand All @@ -174,8 +182,7 @@ inline fun <reified T> AttributeContainer.attribute(name: String, value: T) =
* pluginManager.apply(T::class)
* ```
*/
inline fun <reified T : Any> org.gradle.api.plugins.PluginManager.`apply`(): Unit =
`apply`(T::class)
inline fun <reified T : Any> PluginManager.apply(): Unit = apply(T::class)

/**
* The name provided to this artifact when published.
Expand All @@ -186,4 +193,54 @@ inline fun <reified T : Any> org.gradle.api.plugins.PluginManager.`apply`(): Uni
* ```
*/
val Dependency.artifactName: String
get() = listOf(group, name, version).filterNotNull().joinToString(":")
get() = listOfNotNull(group, name, version).joinToString(":")

/**
* Creates an archive of this directory at the [dest] file.
*
* Should only be ran within the context of a [Task], as outside of a [Task] so you should likely be
* using the `copy` or `sync` tasks instead.
*/
fun File.zipFilesTo(task: Task, dest: File): File {
val logger = task.logger

logger.info("Zipping '$absolutePath' to '${dest.absolutePath}'")

logger.debug("Ensuring parent directories are present for zip file")
dest.parentFile?.mkdirs()
daymxn marked this conversation as resolved.
Show resolved Hide resolved

logger.debug("Creating empty zip file to write to")
daymxn marked this conversation as resolved.
Show resolved Hide resolved
dest.createNewFile()

logger.debug("Packing file contents into zip")
ZipOutputStream(BufferedOutputStream(dest.outputStream())).use { zipFile ->
for (file in walk().filter { it.isFile }) {
val relativePath = file.relativeTo(this).unixPath
logger.debug("Adding file to zip: $relativePath")

zipFile.putNextEntry(ZipEntry(relativePath))
file.inputStream().use { it.copyTo(zipFile) }
zipFile.closeEntry()
}
}

return dest
}

/**
* Bind a callback to run whenever there are release variants for this android build.
*
* Syntax sugar for:
* ```
* components.onVariants(components.selector().withBuildType("release")) {
* // ...
* }
* ```
*
* @see LibraryAndroidComponentsExtension.onVariants
*/
fun LibraryAndroidComponentsExtension.onReleaseVariants(
callback: (variant: LibraryVariant) -> Unit
) {
onVariants(selector().withBuildType("release"), callback)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.firebase.gradle.plugins

import java.io.File
import org.w3c.dom.Element
import org.w3c.dom.NodeList

Expand All @@ -26,16 +27,14 @@ fun String.remove(regex: Regex) = replace(regex, "")
fun String.remove(str: String) = replace(str, "")

/**
* Returns a sequence containing all elements.
*
* The operation is _terminal_.
* Joins a variable amount of [strings][Any.toString] to a single [String] split by newlines (`\n`).
*
* Syntax sugar for:
* ```
* take(count())
* For example:
* ```kotlin
* println(multiLine("Hello", "World", "!")) // "Hello\nWorld\n!"
* ```
*/
public fun <T> Sequence<T>.takeAll(): Sequence<T> = take(count())
fun multiLine(vararg strings: Any?) = strings.joinToString("\n")
daymxn marked this conversation as resolved.
Show resolved Hide resolved

/**
* Converts an [Element] to an Artifact string.
Expand Down Expand Up @@ -77,19 +76,30 @@ fun Element.toArtifactString() =
* ```
*
* @throws NoSuchElementException if the [Element] does not have descendant [Element]s with tags
* that match the components of an Artifact string; groupId, artifactId, version.
* that match the components of an Artifact string; groupId and artifactId.
*/
fun Element.toMavenName() = "${textByTag("groupId")}:${textByTag("artifactId")}"

/**
* Finds a descendant [Element] by a given [tag], and returns the [textContent]
* [Element.getTextContent] of it.
* Finds a descendant [Element] by a [tag], and returns the [textContent][Element.getTextContent] of
* it.
*
* @param tag the XML tag to filter for (the special value "*" matches all tags)
* @throws NoSuchElementException if an [Element] with the given [tag] does not exist
* @see findElementsByTag
* @see textByTagOrNull
*/
fun Element.textByTag(tag: String) =
textByTagOrNull(tag) ?: throw RuntimeException("Element tag was missing: $tag")

/**
* Finds a descendant [Element] by a [tag], and returns the [textContent][Element.getTextContent] of
* it, or null if it couldn't be found.
*
* @param tag the XML tag to filter for (the special value "*" matches all tags)
* @see textByTag
*/
fun Element.textByTag(tag: String) = findElementsByTag(tag).first().textContent
fun Element.textByTagOrNull(tag: String) = findElementsByTag(tag).firstOrNull()?.textContent

/**
* Finds a descendant [Element] by a given [tag], or creates a new one.
Expand All @@ -99,7 +109,7 @@ fun Element.textByTag(tag: String) = findElementsByTag(tag).first().textContent
* @param tag the XML tag to filter for (the special value "*" matches all tags)
* @see findElementsByTag
*/
fun Element.findOrCreate(tag: String) =
fun Element.findOrCreate(tag: String): Element =
findElementsByTag(tag).firstOrNull() ?: ownerDocument.createElement(tag).also { appendChild(it) }

/**
Expand All @@ -118,27 +128,21 @@ fun Element.findElementsByTag(tag: String) =
* Yields the items of this [NodeList] as a [Sequence].
*
* [NodeList] does not typically offer an iterator. This extension method offers a means to loop
* through a NodeList's [item][NodeList.item] method, while also taking into account its [length]
* [NodeList.getLength] property to avoid an [IndexOutOfBoundsException].
* through a NodeList's [item][NodeList.item] method, while also taking into account the element's
* [length][NodeList.getLength] property to avoid an [IndexOutOfBoundsException].
*
* Additionally, this operation is _intermediate_ and _stateless_.
*/
fun NodeList.children() = sequence {
for (index in 0..length) {
yield(item(index))
fun NodeList.children(removeDOMSections: Boolean = true) = sequence {
for (index in 0 until length) {
val child = item(index)

if (!removeDOMSections || !child.nodeName.startsWith("#")) {
yield(child)
}
}
}

/**
* Joins a variable amount of [strings][Any.toString] to a single [String] split by newlines (`\n`).
*
* For example:
* ```kotlin
* println(multiLine("Hello", "World", "!")) // "Hello\nWorld\n!"
* ```
*/
fun multiLine(vararg strings: Any?) = strings.joinToString("\n")

/**
* Returns the first match of a regular expression in the [input], beginning at the specified
* [startIndex].
Expand All @@ -152,6 +156,26 @@ fun Regex.findOrThrow(input: CharSequence, startIndex: Int = 0) =
find(input, startIndex)
?: throw RuntimeException(multiLine("No match found for the given input:", input.toString()))

/**
* Returns the value of the first capture group.
*
* Intended to be used in [MatchResult] that are only supposed to capture a single entry.
*/
val MatchResult.firstCapturedValue: String
get() = groupValues[1]

/**
* Returns a sequence containing all elements.
*
* The operation is _terminal_.
*
* Syntax sugar for:
* ```
* take(count())
* ```
*/
fun <T> Sequence<T>.takeAll(): Sequence<T> = take(count())

/**
* Creates a [Pair] out of an [Iterable] with only two elements.
*
Expand Down Expand Up @@ -212,14 +236,6 @@ fun List<String>.replaceMatches(regex: Regex, transform: (MatchResult) -> String
}
}

/**
* Returns the value of the first capture group.
*
* Intended to be used in [MatchResult] that are only supposed to capture a single entry.
*/
val MatchResult.firstCapturedValue: String
get() = groupValues[1]

/**
* Creates a diff between two lists.
*
Expand Down Expand Up @@ -250,3 +266,23 @@ infix fun <T> List<T>.diff(other: List<T>): List<Pair<T?, T?>> {
* ```
*/
fun <T> List<T>.coerceToSize(targetSize: Int) = List(targetSize) { getOrNull(it) }

/**
* The [path][File.path] represented as a qualified unix path.
*
* Useful when a system expects a unix path, but you need to be able to run it on non unix systems.
*
* @see absoluteUnixPath
*/
val File.unixPath: String
get() = path.replace("\\", "/")

/**
* The [absolutePath][File.getAbsolutePath] represented as a qualified unix path.
*
* Useful when a system expects a unix path, but you need to be able to run it on non unix systems.
*
* @see unixPath
*/
val File.absoluteUnixPath: String
get() = absolutePath.replace("\\", "/")
Loading
Loading