Skip to content

Commit 0176ee4

Browse files
daymxnvkryachko
andauthored
Add a new plugin for Dackka (#4023)
* Moved the CI repo to artifacts only This causes no issues on our part, and makes for an easy fix as far as grabbing the fat jar of dackka. * Added missing suppress All `ComponentRegistrar` subclasses should be annotated with `@suppress` or `@hide`. * Added comment about bug Created a bug to track Timestamp being incorrectly under `firebase-firestore` instead of `firebase-common`. * Removed loose annotations. Dangling empty annotations were left in FirebaseInAppMessaging. This causes issues with certain javadoc systems. * Fixed CampaignMetadata docs Wrapped the pretex in a paragraph block, as dangling text causes issues with javadoc processing. b/243064949 * Fixed DocumentID docs Wrapped the pretex in a paragraph block, as dangling text causes issues with javadoc processing. b/243064949 * Added additional utility methods In preparation of dackka implementation, these util methods will help keep certain segments easier to read. * Add the DackkaPlugin Added a new plugin to replace our current dokka setup with dackka. This also includes various tasks to support the plugin. * Added the NDK back to subprojects. I removed this for macOS testing, and accidentally committed the change. * Fix minor javadoc grammar issue Instead of ran, use the present-tense of run. Co-authored-by: Vladimir Kryachko <[email protected]> * Added TODO to move links to task input b/243675474 * Added the NDK back to subprojects. I didn't mean to add it again. * Add link to bug for removing class/index headers b/243674302 * Add link to bug for fixing book paths b/243674303 * Add link to bug for fixing hyperlinks b/243674305 * Added link to bug for removing domains in links b/243673063 * Remove filter for annotation Not really sure why this was here to begin with. * Refactor Firesite task to draw from generate task The firesite transform task will now directly pull the output dir from the generate documentation task. This allows us to avoid hardcoding strings, and any weird edge cases that could end up happening as a result. * Refactor delete task to draw from generate task Similar to the firesite task, this avoids as many hardcoded strings as possible. * Refactor task dependency structure Instead of passing around the task explicitly, just pass what the task needs. This is easier to maintain, read, and will be easier to integrate with other tasks. * Refactor copy dackka task to use output dir input Falls inline with other tasks. * Fix javadoc todo link go/todont * Fix getjars bug link go/todont * Fix projectSpecificSources bug link go/todont * Fix projectSpecificSuppressedFiles bug link go/todont * Refactor bootclasspath impl for dackka Instead of having a separate extension method for it, just use the already fetched `LibraryExtension`. * Add metadatasources to tests/buildscript Hopefully this fixes the failing buildSrc tests. * Fix DocumentID Formatting * Fix formatting in CampaignMetadata Co-authored-by: Vladimir Kryachko <[email protected]>
1 parent 13083a2 commit 0176ee4

File tree

14 files changed

+488
-16
lines changed

14 files changed

+488
-16
lines changed

appcheck/firebase-appcheck/ktx/src/main/kotlin/com/google/firebase/appcheck/ktx/FirebaseAppCheck.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ operator fun AppCheckToken.component2() = expireTimeMillis
4646

4747
internal const val LIBRARY_NAME: String = "fire-app-check-ktx"
4848

49+
/** @suppress */
4950
class FirebaseAppCheckKtxRegistrar : ComponentRegistrar {
5051
override fun getComponents(): List<Component<*>> =
5152
listOf(LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME))

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ buildscript {
2525
}
2626
maven {
2727
url 'https://storage.googleapis.com/android-ci/mvn/'
28+
metadataSources {
29+
artifact()
30+
}
2831
}
2932

3033
}
@@ -77,6 +80,9 @@ configure(subprojects) {
7780
mavenCentral()
7881
maven {
7982
url 'https://storage.googleapis.com/android-ci/mvn/'
83+
metadataSources {
84+
artifact()
85+
}
8086
}
8187
}
8288

buildSrc/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ gradlePlugin {
7272
id = "LicenseResolverPlugin"
7373
implementationClass = "com.google.firebase.gradle.plugins.license.LicenseResolverPlugin"
7474
}
75+
register("dackkaPlugin") {
76+
id = "DackkaPlugin"
77+
implementationClass = "com.google.firebase.gradle.plugins.DackkaPlugin"
78+
}
7579
register("multiProjectReleasePlugin") {
7680
id = "MultiProjectReleasePlugin"
7781
implementationClass = "com.google.firebase.gradle.MultiProjectReleasePlugin"
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.google.firebase.gradle.plugins
2+
3+
import java.io.File
4+
import javax.inject.Inject
5+
import org.gradle.api.DefaultTask
6+
import org.gradle.api.provider.ListProperty
7+
import org.gradle.api.provider.Property
8+
import org.gradle.api.provider.SetProperty
9+
import org.gradle.api.tasks.Input
10+
import org.gradle.api.tasks.InputFile
11+
import org.gradle.api.tasks.InputFiles
12+
import org.gradle.api.tasks.OutputDirectory
13+
import org.gradle.api.tasks.TaskAction
14+
import org.gradle.process.ExecOperations
15+
import org.gradle.workers.WorkAction
16+
import org.gradle.workers.WorkParameters
17+
import org.gradle.workers.WorkerExecutor
18+
import org.json.JSONObject
19+
20+
/**
21+
* Extension class for [GenerateDocumentationTask].
22+
*
23+
* Provides public configurations for the task.
24+
*
25+
* @property dackkaJarFile a [File] of the Dackka fat jar
26+
* @property dependencies a list of all dependent jars (the classpath)
27+
* @property kotlinSources a list of kotlin source roots
28+
* @property javaSources a list of java source roots
29+
* @property suppressedFiles a list of files to exclude from documentation
30+
* @property outputDirectory where to store the generated files
31+
*/
32+
abstract class GenerateDocumentationTaskExtension : DefaultTask() {
33+
@get:InputFile
34+
abstract val dackkaJarFile: Property<File>
35+
36+
@get:Input
37+
abstract val dependencies: ListProperty<File>
38+
39+
@get:InputFiles
40+
abstract val kotlinSources: ListProperty<File>
41+
42+
@get:InputFiles
43+
abstract val javaSources: ListProperty<File>
44+
45+
@get:InputFiles
46+
abstract val suppressedFiles: ListProperty<File>
47+
48+
@get:OutputDirectory
49+
abstract val outputDirectory: Property<File>
50+
}
51+
52+
/**
53+
* Task to run Dackka on a project.
54+
*
55+
* Since dackka needs to be run on the command line, we have to organize the arguments for dackka into
56+
* a json file. We then pass that json file to dackka as an argument.
57+
*
58+
* @see GenerateDocumentationTaskExtension
59+
*/
60+
abstract class GenerateDocumentationTask @Inject constructor(
61+
private val workerExecutor: WorkerExecutor
62+
) : GenerateDocumentationTaskExtension() {
63+
64+
@TaskAction
65+
fun build() {
66+
val configFile = saveToJsonFile(constructArguments())
67+
launchDackka(configFile, workerExecutor)
68+
}
69+
70+
private fun constructArguments(): JSONObject {
71+
// TODO(b/243675474): Move these to a task input for caching purposes
72+
val linksMap = mapOf(
73+
"android" to "https://developer.android.com/reference/kotlin/",
74+
"google" to "https://developer.android.com/reference/",
75+
"firebase" to "https://firebase.google.com/docs/reference/kotlin/",
76+
"coroutines" to "https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/"
77+
)
78+
79+
val jsonMap = mapOf(
80+
"moduleName" to "",
81+
"outputDir" to outputDirectory.get().path,
82+
"globalLinks" to "",
83+
"sourceSets" to listOf(mutableMapOf(
84+
"sourceSetID" to mapOf(
85+
"scopeId" to "androidx",
86+
"sourceSetName" to "main"
87+
),
88+
"sourceRoots" to kotlinSources.get().map { it.path } + javaSources.get().map { it.path },
89+
"classpath" to dependencies.get().map { it.path },
90+
"documentedVisibilities" to listOf("PUBLIC", "PROTECTED"),
91+
"skipEmptyPackages" to "true",
92+
"suppressedFiles" to suppressedFiles.get().map { it.path },
93+
"externalDocumentationLinks" to linksMap.map { (name, url) -> mapOf(
94+
"url" to url,
95+
"packageListUrl" to "file://${project.rootDir.absolutePath}/kotlindoc/package-lists/$name/package-list"
96+
) }
97+
)),
98+
"offlineMode" to "true",
99+
"noJdkLink" to "true"
100+
)
101+
102+
return JSONObject(jsonMap)
103+
}
104+
105+
private fun saveToJsonFile(jsonObject: JSONObject): File {
106+
val outputFile = File.createTempFile("dackkaArgs", ".json")
107+
108+
outputFile.deleteOnExit()
109+
outputFile.writeText(jsonObject.toString(2))
110+
111+
return outputFile
112+
}
113+
114+
private fun launchDackka(argsFile: File, workerExecutor: WorkerExecutor) {
115+
val workQueue = workerExecutor.noIsolation()
116+
117+
workQueue.submit(DackkaWorkAction::class.java) {
118+
args.set(listOf(argsFile.path, "-loggingLevel", "WARN"))
119+
classpath.set(setOf(dackkaJarFile.get()))
120+
projectName.set(project.name)
121+
}
122+
}
123+
}
124+
125+
/**
126+
* Parameters needs to launch the Dackka fat jar on the command line.
127+
*
128+
* @property args a list of arguments to pass to Dackka- should include the json arguments file
129+
* @property classpath the classpath to use during execution of the jar file
130+
* @property projectName name of the calling project, used for the devsite tenant (output directory)
131+
*/
132+
interface DackkaParams : WorkParameters {
133+
val args: ListProperty<String>
134+
val classpath: SetProperty<File>
135+
val projectName: Property<String>
136+
}
137+
138+
/**
139+
* Work action to launch dackka with a [DackkaParams].
140+
*
141+
* Work actions are organized sections of work, offered by gradle.
142+
*/
143+
abstract class DackkaWorkAction @Inject constructor(
144+
private val execOperations: ExecOperations
145+
) : WorkAction<DackkaParams> {
146+
override fun execute() {
147+
execOperations.javaexec {
148+
mainClass.set("org.jetbrains.dokka.MainKt")
149+
args = parameters.args.get()
150+
classpath(parameters.classpath.get())
151+
152+
environment("DEVSITE_TENANT", "client/${parameters.projectName.get()}")
153+
}
154+
}
155+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.google.firebase.gradle.plugins
2+
3+
import com.android.build.api.attributes.BuildTypeAttr
4+
import com.android.build.gradle.LibraryExtension
5+
import java.io.File
6+
import org.gradle.api.Plugin
7+
import org.gradle.api.Project
8+
import org.gradle.api.provider.Provider
9+
import org.gradle.api.tasks.Copy
10+
import org.gradle.api.tasks.Delete
11+
import org.gradle.kotlin.dsl.getByType
12+
import org.gradle.kotlin.dsl.register
13+
14+
/**
15+
* Facilitates the creation of Firesite compliant reference documentation.
16+
*
17+
* This plugin handles a procedure of processes, all registered under the "kotlindoc" task.
18+
*
19+
* Those tasks are:
20+
* - Collect the arguments needed to run Dackka
21+
* - Run Dackka with [GenerateDocumentationTask] to create the initial reference docs
22+
* - Clean up the output with [FiresiteTransformTask] to fix minor inconsistencies
23+
* - Remove the java references generated from the task (we do not currently support them)
24+
* - Copies the output files to a common directory under the root project's build directory
25+
*
26+
* @see GenerateDocumentationTask
27+
* @see FiresiteTransformTask
28+
* @see JavadocPlugin
29+
*/
30+
abstract class DackkaPlugin : Plugin<Project> {
31+
override fun apply(project: Project) {
32+
prepareJavadocConfiguration(project)
33+
registerCleanDackkaDocumentation(project)
34+
project.afterEvaluate {
35+
if (shouldWePublish(project)) {
36+
val generateDocumentation = registerGenerateDackkaDocumentationTask(project)
37+
val outputDirectory = generateDocumentation.flatMap { it.outputDirectory }
38+
val firesiteTransform = registerFiresiteTransformTask(project, outputDirectory)
39+
val deleteJavaReferences = registerDeleteDackkaGeneratedJavaReferencesTask(project, outputDirectory)
40+
val copyOutputToCommonDirectory =
41+
registerCopyDackkaOutputToCommonDirectoryTask(project, outputDirectory)
42+
43+
project.tasks.register("kotlindoc") {
44+
group = "documentation"
45+
dependsOn(
46+
generateDocumentation,
47+
firesiteTransform,
48+
deleteJavaReferences,
49+
copyOutputToCommonDirectory
50+
)
51+
}
52+
} else {
53+
project.tasks.register("kotlindoc")
54+
}
55+
}
56+
}
57+
58+
private fun shouldWePublish(project: Project) =
59+
project.extensions.getByType<FirebaseLibraryExtension>().publishJavadoc
60+
61+
private fun prepareJavadocConfiguration(project: Project) {
62+
val javadocConfig = project.javadocConfig
63+
javadocConfig.dependencies += project.dependencies.create("com.google.code.findbugs:jsr305:3.0.2")
64+
javadocConfig.dependencies += project.dependencies.create("com.google.errorprone:error_prone_annotations:2.15.0")
65+
javadocConfig.attributes.attribute(
66+
BuildTypeAttr.ATTRIBUTE,
67+
project.objects.named(BuildTypeAttr::class.java, "release")
68+
)
69+
}
70+
71+
private fun registerGenerateDackkaDocumentationTask(project: Project) =
72+
project.tasks.register<GenerateDocumentationTask>("generateDackkaDocumentation") {
73+
with(project.extensions.getByType<LibraryExtension>()) {
74+
libraryVariants.all {
75+
if (name == "release") {
76+
mustRunAfter("createFullJarRelease")
77+
dependsOn("createFullJarRelease")
78+
79+
val classpath = project.provider {
80+
runtimeConfiguration.getJars() + project.javadocConfig.getJars() + bootClasspath
81+
}
82+
83+
val sourcesForJava = sourceSets.flatMap {
84+
it.javaDirectories.map { it.absoluteFile } + projectSpecificSources(project)
85+
}
86+
87+
// this will become useful with the agp upgrade, as they're separate in 7.x+
88+
val sourcesForKotlin = emptyList<File>()
89+
val excludedFiles = emptyList<File>() + projectSpecificSuppressedFiles(project)
90+
91+
dependencies.set(classpath)
92+
javaSources.set(sourcesForJava)
93+
kotlinSources.set(sourcesForKotlin)
94+
suppressedFiles.set(excludedFiles)
95+
96+
applyCommonConfigurations()
97+
}
98+
}
99+
}
100+
}
101+
102+
// TODO(b/243534168): Remove when fixed
103+
private fun projectSpecificSources(project: Project) =
104+
when (project.name) {
105+
"firebase-common" -> {
106+
project.project(":firebase-firestore").files("src/main/java/com/google/firebase").toList()
107+
}
108+
else -> emptyList()
109+
}
110+
111+
// TODO(b/243534168): Remove when fixed
112+
private fun projectSpecificSuppressedFiles(project: Project): List<File> =
113+
when (project.name) {
114+
"firebase-common" -> {
115+
val firestoreProject = project.project(":firebase-firestore")
116+
firestoreProject.files("src/main/java/com/google/firebase/firestore").toList()
117+
}
118+
"firebase-firestore" -> {
119+
project.files("src/main/java/com/google/firebase/Timestamp.java").toList()
120+
}
121+
else -> emptyList()
122+
}
123+
124+
private fun GenerateDocumentationTask.applyCommonConfigurations() {
125+
val dackkaFile = project.provider { project.dackkaConfig.singleFile }
126+
val dackkaOutputDirectory = File(project.buildDir, "dackkaDocumentation")
127+
128+
dackkaJarFile.set(dackkaFile)
129+
outputDirectory.set(dackkaOutputDirectory)
130+
}
131+
132+
private fun registerFiresiteTransformTask(project: Project, outputDirectory: Provider<File>) =
133+
project.tasks.register<FiresiteTransformTask>("firesiteTransform") {
134+
dackkaFiles.set(outputDirectory)
135+
}
136+
137+
// If we decide to publish java variants, we'll need to address the generated format as well
138+
private fun registerDeleteDackkaGeneratedJavaReferencesTask(project: Project, outputDirectory: Provider<File>) =
139+
project.tasks.register<Delete>("deleteDackkaGeneratedJavaReferences") {
140+
mustRunAfter("generateDackkaDocumentation")
141+
142+
val filesWeDoNotNeed = listOf(
143+
"reference/client",
144+
"reference/com"
145+
)
146+
val filesToDelete = outputDirectory.map { dir ->
147+
filesWeDoNotNeed.map {
148+
project.files("${dir.path}/$it")
149+
}
150+
}
151+
152+
delete(filesToDelete)
153+
}
154+
155+
private fun registerCopyDackkaOutputToCommonDirectoryTask(project: Project, outputDirectory: Provider<File>) =
156+
project.tasks.register<Copy>("copyDackkaOutputToCommonDirectory") {
157+
mustRunAfter("deleteDackkaGeneratedJavaReferences")
158+
mustRunAfter("firesiteTransform")
159+
160+
val referenceFolder = outputDirectory.map { project.file("${it.path}/reference") }
161+
val outputFolder = project.file("${project.rootProject.buildDir}/firebase-kotlindoc")
162+
163+
from(referenceFolder)
164+
destinationDir = outputFolder
165+
}
166+
167+
// Useful for local testing, but may not be desired for standard use (that's why it's not depended on)
168+
private fun registerCleanDackkaDocumentation(project: Project) =
169+
project.tasks.register<Delete>("cleanDackkaDocumentation") {
170+
group = "cleanup"
171+
172+
delete("${project.buildDir}/dackkaDocumentation")
173+
}
174+
}

buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void apply(Project project) {
109109
.setFreeCompilerArgs(
110110
ImmutableList.of("-module-name", kotlinModuleName(project))));
111111

112-
Dokka.configure(project, android, firebaseLibrary);
112+
project.getPluginManager().apply(DackkaPlugin.class);
113113
}
114114

115115
private static void setupApiInformationAnalysis(Project project, LibraryExtension android) {

0 commit comments

Comments
 (0)