Skip to content

Add a new plugin for Dackka #4023

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

Merged
merged 29 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b2e28cb
Moved the CI repo to artifacts only
daymxn Aug 23, 2022
6c890ab
Added missing suppress
daymxn Aug 23, 2022
488a2ec
Added comment about bug
daymxn Aug 23, 2022
5ded464
Removed loose annotations.
daymxn Aug 23, 2022
f3c5449
Fixed CampaignMetadata docs
daymxn Aug 23, 2022
127898f
Fixed DocumentID docs
daymxn Aug 23, 2022
6f7a181
Added additional utility methods
daymxn Aug 23, 2022
bc655e6
Add the DackkaPlugin
daymxn Aug 23, 2022
361cb78
Added the NDK back to subprojects.
daymxn Aug 23, 2022
54fc5ab
Fix minor javadoc grammar issue
daymxn Aug 24, 2022
f67ea2c
Added TODO to move links to task input
daymxn Aug 24, 2022
525819e
Added the NDK back to subprojects.
daymxn Aug 24, 2022
ea45f69
Add link to bug for removing class/index headers
daymxn Aug 24, 2022
7b241a9
Add link to bug for fixing book paths
daymxn Aug 24, 2022
b572eec
Add link to bug for fixing hyperlinks
daymxn Aug 24, 2022
ed7d2a4
Added link to bug for removing domains in links
daymxn Aug 24, 2022
f0bcab5
Remove filter for annotation
daymxn Aug 24, 2022
ab82741
Refactor Firesite task to draw from generate task
daymxn Aug 24, 2022
0bcb374
Refactor delete task to draw from generate task
daymxn Aug 24, 2022
817e9be
Refactor task dependency structure
daymxn Aug 24, 2022
53326b2
Refactor copy dackka task to use output dir input
daymxn Aug 24, 2022
e021ac7
Fix javadoc todo link
daymxn Aug 24, 2022
65d4fa3
Fix getjars bug link
daymxn Aug 24, 2022
c6e43ad
Fix projectSpecificSources bug link
daymxn Aug 24, 2022
706e275
Fix projectSpecificSuppressedFiles bug link
daymxn Aug 24, 2022
6560151
Refactor bootclasspath impl for dackka
daymxn Aug 24, 2022
9306449
Add metadatasources to tests/buildscript
daymxn Aug 24, 2022
505ce09
Fix DocumentID Formatting
daymxn Aug 24, 2022
c24efc7
Fix formatting in CampaignMetadata
daymxn Aug 24, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ operator fun AppCheckToken.component2() = expireTimeMillis

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

/** @suppress */
class FirebaseAppCheckKtxRegistrar : ComponentRegistrar {
override fun getComponents(): List<Component<*>> =
listOf(LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME))
Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ buildscript {
}
maven {
url 'https://storage.googleapis.com/android-ci/mvn/'
metadataSources {
artifact()
}
}

}
Expand Down Expand Up @@ -77,6 +80,9 @@ configure(subprojects) {
mavenCentral()
maven {
url 'https://storage.googleapis.com/android-ci/mvn/'
metadataSources {
artifact()
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ gradlePlugin {
id = "LicenseResolverPlugin"
implementationClass = "com.google.firebase.gradle.plugins.license.LicenseResolverPlugin"
}
register("dackkaPlugin") {
id = "DackkaPlugin"
implementationClass = "com.google.firebase.gradle.plugins.DackkaPlugin"
}
register("multiProjectReleasePlugin") {
id = "MultiProjectReleasePlugin"
implementationClass = "com.google.firebase.gradle.MultiProjectReleasePlugin"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.google.firebase.gradle.plugins

import java.io.File
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import org.json.JSONObject

/**
* Extension class for [GenerateDocumentationTask].
*
* Provides public configurations for the task.
*
* @property dackkaJarFile a [File] of the Dackka fat jar
* @property dependencies a list of all dependent jars (the classpath)
* @property kotlinSources a list of kotlin source roots
* @property javaSources a list of java source roots
* @property suppressedFiles a list of files to exclude from documentation
* @property outputDirectory where to store the generated files
*/
abstract class GenerateDocumentationTaskExtension : DefaultTask() {
@get:InputFile
abstract val dackkaJarFile: Property<File>

@get:Input
abstract val dependencies: ListProperty<File>

@get:InputFiles
abstract val kotlinSources: ListProperty<File>

@get:InputFiles
abstract val javaSources: ListProperty<File>

@get:InputFiles
abstract val suppressedFiles: ListProperty<File>

@get:OutputDirectory
abstract val outputDirectory: Property<File>
}

/**
* Task to run Dackka on a project.
*
* Since dackka needs to be run on the command line, we have to organize the arguments for dackka into
* a json file. We then pass that json file to dackka as an argument.
*
* @see GenerateDocumentationTaskExtension
*/
abstract class GenerateDocumentationTask @Inject constructor(
private val workerExecutor: WorkerExecutor
) : GenerateDocumentationTaskExtension() {

@TaskAction
fun build() {
val configFile = saveToJsonFile(constructArguments())
launchDackka(configFile, workerExecutor)
}

private fun constructArguments(): JSONObject {
// TODO(b/243675474): Move these to a task input for caching purposes
val linksMap = mapOf(
"android" to "https://developer.android.com/reference/kotlin/",
"google" to "https://developer.android.com/reference/",
"firebase" to "https://firebase.google.com/docs/reference/kotlin/",
"coroutines" to "https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/"
)

val jsonMap = mapOf(
"moduleName" to "",
"outputDir" to outputDirectory.get().path,
"globalLinks" to "",
"sourceSets" to listOf(mutableMapOf(
"sourceSetID" to mapOf(
"scopeId" to "androidx",
"sourceSetName" to "main"
),
"sourceRoots" to kotlinSources.get().map { it.path } + javaSources.get().map { it.path },
"classpath" to dependencies.get().map { it.path },
"documentedVisibilities" to listOf("PUBLIC", "PROTECTED"),
"skipEmptyPackages" to "true",
"suppressedFiles" to suppressedFiles.get().map { it.path },
"externalDocumentationLinks" to linksMap.map { (name, url) -> mapOf(
"url" to url,
"packageListUrl" to "file://${project.rootDir.absolutePath}/kotlindoc/package-lists/$name/package-list"
) }
)),
"offlineMode" to "true",
"noJdkLink" to "true"
)

return JSONObject(jsonMap)
}

private fun saveToJsonFile(jsonObject: JSONObject): File {
val outputFile = File.createTempFile("dackkaArgs", ".json")

outputFile.deleteOnExit()
outputFile.writeText(jsonObject.toString(2))

return outputFile
}

private fun launchDackka(argsFile: File, workerExecutor: WorkerExecutor) {
val workQueue = workerExecutor.noIsolation()

workQueue.submit(DackkaWorkAction::class.java) {
args.set(listOf(argsFile.path, "-loggingLevel", "WARN"))
classpath.set(setOf(dackkaJarFile.get()))
projectName.set(project.name)
}
}
}

/**
* Parameters needs to launch the Dackka fat jar on the command line.
*
* @property args a list of arguments to pass to Dackka- should include the json arguments file
* @property classpath the classpath to use during execution of the jar file
* @property projectName name of the calling project, used for the devsite tenant (output directory)
*/
interface DackkaParams : WorkParameters {
val args: ListProperty<String>
val classpath: SetProperty<File>
val projectName: Property<String>
}

/**
* Work action to launch dackka with a [DackkaParams].
*
* Work actions are organized sections of work, offered by gradle.
*/
abstract class DackkaWorkAction @Inject constructor(
private val execOperations: ExecOperations
) : WorkAction<DackkaParams> {
override fun execute() {
execOperations.javaexec {
mainClass.set("org.jetbrains.dokka.MainKt")
args = parameters.args.get()
classpath(parameters.classpath.get())

environment("DEVSITE_TENANT", "client/${parameters.projectName.get()}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.google.firebase.gradle.plugins

import com.android.build.api.attributes.BuildTypeAttr
import com.android.build.gradle.LibraryExtension
import java.io.File
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.register

/**
* Facilitates the creation of Firesite compliant reference documentation.
*
* This plugin handles a procedure of processes, all registered under the "kotlindoc" task.
*
* Those tasks are:
* - Collect the arguments needed to run Dackka
* - Run Dackka with [GenerateDocumentationTask] to create the initial reference docs
* - Clean up the output with [FiresiteTransformTask] to fix minor inconsistencies
* - Remove the java references generated from the task (we do not currently support them)
* - Copies the output files to a common directory under the root project's build directory
*
* @see GenerateDocumentationTask
* @see FiresiteTransformTask
* @see JavadocPlugin
*/
abstract class DackkaPlugin : Plugin<Project> {
override fun apply(project: Project) {
prepareJavadocConfiguration(project)
registerCleanDackkaDocumentation(project)
project.afterEvaluate {
if (shouldWePublish(project)) {
val generateDocumentation = registerGenerateDackkaDocumentationTask(project)
val outputDirectory = generateDocumentation.flatMap { it.outputDirectory }
val firesiteTransform = registerFiresiteTransformTask(project, outputDirectory)
val deleteJavaReferences = registerDeleteDackkaGeneratedJavaReferencesTask(project, outputDirectory)
val copyOutputToCommonDirectory =
registerCopyDackkaOutputToCommonDirectoryTask(project, outputDirectory)

project.tasks.register("kotlindoc") {
group = "documentation"
dependsOn(
generateDocumentation,
firesiteTransform,
deleteJavaReferences,
copyOutputToCommonDirectory
)
}
} else {
project.tasks.register("kotlindoc")
}
}
}

private fun shouldWePublish(project: Project) =
project.extensions.getByType<FirebaseLibraryExtension>().publishJavadoc

private fun prepareJavadocConfiguration(project: Project) {
val javadocConfig = project.javadocConfig
javadocConfig.dependencies += project.dependencies.create("com.google.code.findbugs:jsr305:3.0.2")
javadocConfig.dependencies += project.dependencies.create("com.google.errorprone:error_prone_annotations:2.15.0")
javadocConfig.attributes.attribute(
BuildTypeAttr.ATTRIBUTE,
project.objects.named(BuildTypeAttr::class.java, "release")
)
}

private fun registerGenerateDackkaDocumentationTask(project: Project) =
project.tasks.register<GenerateDocumentationTask>("generateDackkaDocumentation") {
with(project.extensions.getByType<LibraryExtension>()) {
libraryVariants.all {
if (name == "release") {
mustRunAfter("createFullJarRelease")
dependsOn("createFullJarRelease")

val classpath = project.provider {
runtimeConfiguration.getJars() + project.javadocConfig.getJars() + bootClasspath
}

val sourcesForJava = sourceSets.flatMap {
it.javaDirectories.map { it.absoluteFile } + projectSpecificSources(project)
}

// this will become useful with the agp upgrade, as they're separate in 7.x+
val sourcesForKotlin = emptyList<File>()
val excludedFiles = emptyList<File>() + projectSpecificSuppressedFiles(project)

dependencies.set(classpath)
javaSources.set(sourcesForJava)
kotlinSources.set(sourcesForKotlin)
suppressedFiles.set(excludedFiles)

applyCommonConfigurations()
}
}
}
}

// TODO(b/243534168): Remove when fixed
private fun projectSpecificSources(project: Project) =
when (project.name) {
"firebase-common" -> {
project.project(":firebase-firestore").files("src/main/java/com/google/firebase").toList()
}
else -> emptyList()
}

// TODO(b/243534168): Remove when fixed
private fun projectSpecificSuppressedFiles(project: Project): List<File> =
when (project.name) {
"firebase-common" -> {
val firestoreProject = project.project(":firebase-firestore")
firestoreProject.files("src/main/java/com/google/firebase/firestore").toList()
}
"firebase-firestore" -> {
project.files("src/main/java/com/google/firebase/Timestamp.java").toList()
}
else -> emptyList()
}

private fun GenerateDocumentationTask.applyCommonConfigurations() {
val dackkaFile = project.provider { project.dackkaConfig.singleFile }
val dackkaOutputDirectory = File(project.buildDir, "dackkaDocumentation")

dackkaJarFile.set(dackkaFile)
outputDirectory.set(dackkaOutputDirectory)
}

private fun registerFiresiteTransformTask(project: Project, outputDirectory: Provider<File>) =
project.tasks.register<FiresiteTransformTask>("firesiteTransform") {
dackkaFiles.set(outputDirectory)
}

// If we decide to publish java variants, we'll need to address the generated format as well
private fun registerDeleteDackkaGeneratedJavaReferencesTask(project: Project, outputDirectory: Provider<File>) =
project.tasks.register<Delete>("deleteDackkaGeneratedJavaReferences") {
mustRunAfter("generateDackkaDocumentation")

val filesWeDoNotNeed = listOf(
"reference/client",
"reference/com"
)
val filesToDelete = outputDirectory.map { dir ->
filesWeDoNotNeed.map {
project.files("${dir.path}/$it")
}
}

delete(filesToDelete)
}

private fun registerCopyDackkaOutputToCommonDirectoryTask(project: Project, outputDirectory: Provider<File>) =
project.tasks.register<Copy>("copyDackkaOutputToCommonDirectory") {
mustRunAfter("deleteDackkaGeneratedJavaReferences")
mustRunAfter("firesiteTransform")

val referenceFolder = outputDirectory.map { project.file("${it.path}/reference") }
val outputFolder = project.file("${project.rootProject.buildDir}/firebase-kotlindoc")

from(referenceFolder)
destinationDir = outputFolder
}

// Useful for local testing, but may not be desired for standard use (that's why it's not depended on)
private fun registerCleanDackkaDocumentation(project: Project) =
project.tasks.register<Delete>("cleanDackkaDocumentation") {
group = "cleanup"

delete("${project.buildDir}/dackkaDocumentation")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void apply(Project project) {
.setFreeCompilerArgs(
ImmutableList.of("-module-name", kotlinModuleName(project))));

Dokka.configure(project, android, firebaseLibrary);
project.getPluginManager().apply(DackkaPlugin.class);
}

private static void setupApiInformationAnalysis(Project project, LibraryExtension android) {
Expand Down
Loading