122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets/* 2526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikas * Copyright 2018 The Android Open Source Project 322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * 422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Licensed under the Apache License, Version 2.0 (the "License"); 522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * you may not use this file except in compliance with the License. 622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * You may obtain a copy of the License at 722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * 822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * http://www.apache.org/licenses/LICENSE-2.0 922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * 1022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Unless required by applicable law or agreed to in writing, software 1122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * distributed under the License is distributed on an "AS IS" BASIS, 1222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * See the License for the specific language governing permissions and 1422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * limitations under the License. 1522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets */ 1622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 17526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikaspackage androidx.build 1822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 19f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinetsimport androidx.build.Strategy.Prebuilts 20f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinetsimport androidx.build.Strategy.TipOfTree 21526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.checkapi.ApiXmlConversionTask 22526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.checkapi.CheckApiTask 23526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.checkapi.UpdateApiTask 24526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.doclava.DoclavaTask 25a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsimport androidx.build.doclava.DEFAULT_DOCLAVA_CONFIG 26a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsimport androidx.build.doclava.CHECK_API_CONFIG_DEVELOP 27a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsimport androidx.build.doclava.CHECK_API_CONFIG_RELEASE 28a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsimport androidx.build.doclava.CHECK_API_CONFIG_PATCH 29a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsimport androidx.build.doclava.ChecksConfig 30526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.docs.GenerateDocsTask 31526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikasimport androidx.build.jdiff.JDiffTask 32cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport com.android.build.gradle.AppExtension 3322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport com.android.build.gradle.LibraryExtension 34cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport com.android.build.gradle.api.BaseVariant 3522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport com.android.build.gradle.api.LibraryVariant 3622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.GradleException 3722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.Project 3822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.Task 3922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.artifacts.Configuration 40f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinetsimport org.gradle.api.artifacts.ResolveException 4122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.file.FileCollection 42cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport org.gradle.api.file.FileTree 4322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.plugins.JavaBasePlugin 4422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.tasks.TaskContainer 4522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.tasks.bundling.Zip 4622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.tasks.compile.JavaCompile 4722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport org.gradle.api.tasks.javadoc.Javadoc 4822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsimport java.io.File 49cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.Collection 50cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.List 51cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.MutableMap 52cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.emptyList 53cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.filter 54cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.find 55cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.forEach 56cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.listOf 57cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.mapNotNull 58cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.minus 59cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.mutableMapOf 60cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.plus 61cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.set 62cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsimport kotlin.collections.toSet 6322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 6422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsdata class DacOptions(val libraryroot: String, val dataname: String) 6522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 6622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsobject DiffAndDocs { 67a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets private lateinit var anchorTask: Task 68cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets private var docsProject: Project? = null 69cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 70c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets private lateinit var rules: List<PublishDocsRules> 71c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets private val docsTasks: MutableMap<String, GenerateDocsTask> = mutableMapOf() 725e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets 7322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets @JvmStatic 74c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets fun configureDiffAndDocs( 75c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets root: Project, 76c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets supportRootFolder: File, 77c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets dacOptions: DacOptions, 78c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets additionalRules: List<PublishDocsRules> = emptyList() 79c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets ): Task { 80c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets rules = additionalRules + TIP_OF_TREE 81cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets docsProject = root.findProject(":docs-fake") 82a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets anchorTask = root.tasks.create("anchorDocsTask") 835e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets val doclavaConfiguration = root.configurations.getByName("doclava") 845e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets val generateSdkApiTask = createGenerateSdkApiTask(root, doclavaConfiguration) 85cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets rules.forEach { 86cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val task = createGenerateDocsTask( 87cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets project = root, generateSdkApiTask = generateSdkApiTask, 88cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets doclavaConfig = doclavaConfiguration, 89cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets supportRootFolder = supportRootFolder, dacOptions = dacOptions, 90cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets destDir = File(root.docsDir(), it.name), 91cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets taskName = "${it.name}DocsTask") 92cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets docsTasks[it.name] = task 93a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets anchorTask.dependsOn(createDistDocsTask(root, task, it.name)) 94cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 95cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 96c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets root.tasks.create("generateDocs").dependsOn(docsTasks[TIP_OF_TREE.name]) 97c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets 98cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets setupDocsProject() 99a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets return anchorTask 1005e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 1015e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets 102f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets private fun prebuiltSources( 103f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets root: Project, 104f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets mavenId: String, 105f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets originName: String, 106f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets originRule: DocsRule 107f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets ): FileTree { 108cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val configName = "docs-temp_$mavenId" 109cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val configuration = root.configurations.create(configName) 110cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets root.dependencies.add(configName, mavenId) 111cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 112f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets val artifacts = try { 113f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets configuration.resolvedConfiguration.resolvedArtifacts 114f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets } catch (e: ResolveException) { 115f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets throw GradleException("Failed to find prebuilts for $mavenId. " + 116f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets "A matching rule $originRule in docsRules(\"$originName\") " + 117f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets "in PublishDocsRules.kt requires it. You should either add a prebuilt, " + 118f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets "or add overriding \"ignore\" or \"tipOfTree\" rules", e) 119f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets } 120f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets 121cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val artifact = artifacts.find { it.moduleVersion.id.toString() == mavenId } 122f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets ?: throw GradleException() 123cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 124cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val folder = artifact.file.parentFile 125cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val tree = root.zipTree(File(folder, "${artifact.file.nameWithoutExtension}-sources.jar")) 126cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets .matching { 127cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets it.exclude("**/*.MF") 128cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets it.exclude("**/*.aidl") 129cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets it.exclude("**/*.html") 130cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets it.exclude("**/*.kt") 131cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 132cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets root.configurations.remove(configuration) 133cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets return tree 134cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 135cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 136cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets private fun setupDocsProject() { 137cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets docsProject?.afterEvaluate { docs -> 138cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val appExtension = docs.extensions.findByType(AppExtension::class.java) 139cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets ?: throw GradleException("Android app plugin is missing on docsProject") 140cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 141cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets rules.forEach { rule -> 142cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets appExtension.productFlavors.create(rule.name) { 143cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets it.dimension = "library-group" 144cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 145cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 146cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets appExtension.applicationVariants.all { v -> 147cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val task = docsTasks[v.flavorName] 148cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets if (v.buildType.name == "release" && task != null) { 149cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets registerAndroidProjectForDocsTask(task, v) 150cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets task.exclude { fileTreeElement -> 151cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets fileTreeElement.path.endsWith(v.rFile()) 152cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 153cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 154cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 155cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 156cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 157cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets docsProject?.rootProject?.subprojects 158cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets ?.filter { docsProject != it } 159cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets ?.forEach { docsProject?.evaluationDependsOn(it.path) } 160cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 161cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 162c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets private fun registerPrebuilts(extension: SupportLibraryExtension) = 163c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docsProject?.afterEvaluate { docs -> 164cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val depHandler = docs.dependencies 165cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets val root = docs.rootProject 166f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets rules.forEach { rule -> 167f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets val resolvedRule = rule.resolve(extension) 168f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets val strategy = resolvedRule.strategy 169f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets if (strategy is Prebuilts) { 170f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets val dependency = strategy.dependency(extension) 171f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets depHandler.add("${rule.name}Implementation", dependency) 172f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets strategy.stubs?.forEach { path -> 173f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets depHandler.add("${rule.name}CompileOnly", root.files(path)) 174cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 175f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets docsTasks[rule.name]!!.source(prebuiltSources(root, dependency, 176f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets rule.name, resolvedRule)) 177f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets } 178f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets } 179cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 180cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 181cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets private fun tipOfTreeTasks(extension: SupportLibraryExtension, setup: (DoclavaTask) -> Unit) { 182f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets rules.filter { rule -> rule.resolve(extension).strategy == TipOfTree } 183cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets .mapNotNull { rule -> docsTasks[rule.name] } 184cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets .forEach(setup) 185cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 186cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 187c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette /** 188c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Registers a Java project for global docs generation, local API file generation, and 189c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * local API diff generation tasks. 190c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 1915e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets fun registerJavaProject(project: Project, extension: SupportLibraryExtension) { 1925e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets if (!hasApiTasks(project, extension)) { 1935e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return 1945e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 1955e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets val compileJava = project.properties["compileJava"] as JavaCompile 196cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 197cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets registerPrebuilts(extension) 198cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 199cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets tipOfTreeTasks(extension) { task -> 200cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets registerJavaProjectForDocsTask(task, compileJava) 201cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 202cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 20378d760ec0aebf990265ae61b955f617e2a0b7474Jake Wharton if (!project.hasApiFolder()) { 2045e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets project.logger.info("Project ${project.name} doesn't have an api folder, " + 2055e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets "ignoring API tasks.") 2065e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return 2075e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 208c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val tasks = initializeApiChecksForProject(project) 2095e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets registerJavaProjectForDocsTask(tasks.generateApi, compileJava) 2105e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets registerJavaProjectForDocsTask(tasks.generateDiffs, compileJava) 211c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets setupDocsTasks(project, tasks) 212a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets anchorTask.dependsOn(tasks.checkApiTask) 2135e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 2145e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets 215c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette /** 216c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Registers an Android project for global docs generation, local API file generation, and 217c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * local API diff generation tasks. 218c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 219c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette fun registerAndroidProject( 220c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets project: Project, 221c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets library: LibraryExtension, 222c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets extension: SupportLibraryExtension 223c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette ) { 2245e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets if (!hasApiTasks(project, extension)) { 2255e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return 2265e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 227cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 228cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets registerPrebuilts(extension) 229cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 2305e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets library.libraryVariants.all { variant -> 2315e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets if (variant.name == "release") { 232cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets // include R.file generated for prebuilts 233f38083e1a800b64d0b2ee00e75aa1b945640fc60Sergey Vasilinets rules.filter { it.resolve(extension).strategy is Prebuilts }.forEach { rule -> 234cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets docsTasks[rule.name]?.include { fileTreeElement -> 235cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets fileTreeElement.path.endsWith(variant.rFile()) 236cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 237cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 238cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 239cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets tipOfTreeTasks(extension) { task -> 240cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets registerAndroidProjectForDocsTask(task, variant) 241cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets } 242cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 24378d760ec0aebf990265ae61b955f617e2a0b7474Jake Wharton if (!variant.hasJavaSources()) { 2445e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return@all 2455e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 24678d760ec0aebf990265ae61b955f617e2a0b7474Jake Wharton if (!project.hasApiFolder()) { 2475e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets project.logger.info("Project ${project.name} doesn't have " + 2485e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets "an api folder, ignoring API tasks.") 2495e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return@all 2505e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 251c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val tasks = initializeApiChecksForProject(project) 2525e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets registerAndroidProjectForDocsTask(tasks.generateApi, variant) 2535e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets registerAndroidProjectForDocsTask(tasks.generateDiffs, variant) 254c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets setupDocsTasks(project, tasks) 255a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets anchorTask.dependsOn(tasks.checkApiTask) 2565e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 2575e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 2585e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 259c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets 260c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets private fun setupDocsTasks(project: Project, tasks: Tasks) { 261c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docsTasks.values.forEach { docs -> 262c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets tasks.generateDiffs.dependsOn(docs) 263c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets // Track API change history. 264c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docs.addSinceFilesFrom(project.projectDir) 265c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets // Associate current API surface with the Maven artifact. 266c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val artifact = "${project.group}:${project.name}:${project.version}" 267c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docs.addArtifact(tasks.generateApi.apiFile!!.absolutePath, artifact) 268c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docs.dependsOn(tasks.generateApi) 269c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets } 270c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets } 27122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 27222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 2731638535d6211a53187422aab94792fee85abca0cSergey Vasilinets@Suppress("DEPRECATION") 27478d760ec0aebf990265ae61b955f617e2a0b7474Jake Whartonprivate fun LibraryVariant.hasJavaSources() = !javaCompile.source 27522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets .filter { file -> file.name != "R.java" && file.name != "BuildConfig.java" } 27622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets .isEmpty 27722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 27878d760ec0aebf990265ae61b955f617e2a0b7474Jake Whartonfun Project.hasApiFolder() = File(projectDir, "api").exists() 27922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 28022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun stripExtension(fileName: String) = fileName.substringBeforeLast('.') 28122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 282c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viveretteprivate fun getLastReleasedApiFile(rootFolder: File, refVersion: Version?): File? { 28322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val apiDir = File(rootFolder, "api") 284c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette val lastFile = getLastReleasedApiFileFromDir(apiDir, refVersion) 285c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette if (lastFile != null) { 286c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette return lastFile 287c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette } 288c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette 289c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette return null 290c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette} 29122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 292c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viveretteprivate fun getLastReleasedApiFileFromDir(apiDir: File, refVersion: Version?): File? { 29322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets var lastFile: File? = null 29422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets var lastVersion: Version? = null 2951638535d6211a53187422aab94792fee85abca0cSergey Vasilinets apiDir.listFiles().forEach { file -> 296ec5894ea3e6495260265e695ae6fa918233d4aaaAurimas Liutikas Version.parseOrNull(file)?.let { version -> 297c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets if ((lastFile == null || lastVersion!! < version) && 298c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets (refVersion == null || version < refVersion)) { 2991638535d6211a53187422aab94792fee85abca0cSergey Vasilinets lastFile = file 3001638535d6211a53187422aab94792fee85abca0cSergey Vasilinets lastVersion = version 3011638535d6211a53187422aab94792fee85abca0cSergey Vasilinets } 30222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 30322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 304c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette 30522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets return lastFile 30622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 30722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 30822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun getApiFile(rootDir: File, refVersion: Version): File { 30922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets return getApiFile(rootDir, refVersion, false) 31022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 31122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 31222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets/** 31322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Returns the API file for the specified reference version. 31422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * 31522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * @param refVersion the reference API version, ex. 25.0.0-SNAPSHOT 31622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * @return the most recently released API file 31722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets */ 31822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun getApiFile(rootDir: File, refVersion: Version, forceRelease: Boolean = false): File { 31922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val apiDir = File(rootDir, "api") 32022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 32189ffffd679f65dfe575511bc4b4f420112e67891Yigit Boyar if (refVersion.isFinalApi() || forceRelease) { 32222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Release API file is always X.Y.0.txt. 32322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets return File(apiDir, "${refVersion.major}.${refVersion.minor}.0.txt") 32422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 32522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 32622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Non-release API file is always current.txt. 32722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets return File(apiDir, "current.txt") 32822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 32922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 33022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets// Generates API files 33122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createGenerateApiTask(project: Project, docletpathParam: Collection<File>) = 33222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig("generateApi", DoclavaTask::class.java) { 33322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets setDocletpath(docletpathParam) 33422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets destinationDir = project.docsDir() 33522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Base classpath is Android SDK, sub-projects add their own. 3366eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas classpath = androidJarFile(project) 33722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets apiFile = File(project.docsDir(), "release/${project.name}/current.txt") 33822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets generateDocs = false 33922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 34022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets coreJavadocOptions { 34122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addBooleanOption("stubsourceonly", true) 34222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 34322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 34422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets exclude("**/BuildConfig.java") 34522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets exclude("**/R.java") 34622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 34722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 34822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createCheckApiTask( 349c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets project: Project, 350c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets taskName: String, 351c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets docletpath: Collection<File>, 352a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets config: ChecksConfig, 353c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets oldApi: File?, 354c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets newApi: File, 355c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets whitelist: File? = null 356c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets) = 35722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig(taskName, CheckApiTask::class.java) { 35822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets doclavaClasspath = docletpath 359a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets checksConfig = config 36022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets newApiFile = newApi 36122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets oldApiFile = oldApi 36222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets whitelistErrorsFile = whitelist 36322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets doFirst { 36422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets logger.lifecycle("Verifying ${newApi.name} " + 36522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets "against ${oldApi?.name ?: "nothing"}...") 36622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 36722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 36822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 369c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette/** 370c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Registers a Java project on the given Javadocs task. 371c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <p> 372c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <ul> 373c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Sets up a dependency to ensure the project is compiled prior to running the task 374c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Adds the project's source files to the Javadoc task's source files 375c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Adds the project's compilation classpath (e.g. dependencies) to the task classpath to ensure 376c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * that references in the source files may be resolved 377c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Adds the project's output artifacts to the task classpath to ensure that source references to 378c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * generated code may be resolved 379c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * </ul> 380c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 38122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun registerJavaProjectForDocsTask(task: Javadoc, javaCompileTask: JavaCompile) { 38222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.dependsOn(javaCompileTask) 38322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.source(javaCompileTask.source) 38422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val project = task.project 38522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.classpath += project.files(javaCompileTask.classpath) + 38622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.files(javaCompileTask.destinationDir) 38722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 38822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 389c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette/** 390c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Registers an Android project on the given Javadocs task. 391c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <p> 392c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * @see #registerJavaProjectForDocsTask 393c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 394cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsprivate fun registerAndroidProjectForDocsTask(task: Javadoc, releaseVariant: BaseVariant) { 395c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette // This code makes a number of unsafe assumptions about Android Gradle Plugin, 396c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette // and there's a good chance that this will break in the near future. 3971638535d6211a53187422aab94792fee85abca0cSergey Vasilinets @Suppress("DEPRECATION") 39822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.dependsOn(releaseVariant.javaCompile) 399cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets task.include { fileTreeElement -> 400cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets fileTreeElement.name != "R.java" || fileTreeElement.path.endsWith(releaseVariant.rFile()) } 4011638535d6211a53187422aab94792fee85abca0cSergey Vasilinets @Suppress("DEPRECATION") 402cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets task.source(releaseVariant.javaCompile.source) 4031638535d6211a53187422aab94792fee85abca0cSergey Vasilinets @Suppress("DEPRECATION") 40422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.classpath += releaseVariant.getCompileClasspath(null) + 40522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets task.project.files(releaseVariant.javaCompile.destinationDir) 40622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 40722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 408c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette/** 409c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Constructs a new task to copy a generated API file to an appropriately-named "official" API file 410c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * suitable for source control. This task should be called prior to source control check-in whenever 411c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * the public API has been modified. 412c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <p> 413c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * The output API file varies according to version: 414c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <ul> 415c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Snapshot and pre-release versions (e.g. X.Y.Z-SNAPSHOT, X.Y.Z-alphaN) output to current.txt 416c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <li>Release versions (e.g. X.Y.Z) output to X.Y.0.txt, throwing an exception if the API has been 417c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * finalized and the file already exists 418c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * </ul> 419c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 42022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createUpdateApiTask(project: Project, checkApiRelease: CheckApiTask) = 42122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig("updateApi", UpdateApiTask::class.java) { 42222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets group = JavaBasePlugin.VERIFICATION_GROUP 42322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets description = "Updates the candidate API file to incorporate valid changes." 42422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets newApiFile = checkApiRelease.newApiFile 42522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets oldApiFile = getApiFile(project.projectDir, project.version()) 42622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets whitelistErrors = checkApiRelease.whitelistErrors 42722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets whitelistErrorsFile = checkApiRelease.whitelistErrorsFile 42822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets doFirst { 42941dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets val version = project.version() 43041dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets if (!version.isFinalApi() && 43141dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets getApiFile(project.projectDir, version, true).exists()) { 43241dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets throw GradleException("Inconsistent version. Public API file already exists.") 43341dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets } 43422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Replace the expected whitelist with the detected whitelist. 43522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets whitelistErrors = checkApiRelease.detectedWhitelistErrors 43622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 43722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 43822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 43922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets/** 44022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Converts the <code>fromApi</code>.txt file (or the most recently released 44122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * X.Y.Z.txt if not explicitly defined using -PfromAPi=<file>) to XML format 44222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * for use by JDiff. 44322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets */ 44422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createOldApiXml(project: Project, doclavaConfig: Configuration) = 44522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig("oldApiXml", ApiXmlConversionTask::class.java) { 446526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikas val toApi = project.processProperty("toApi")?.let { 447526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikas Version.parseOrNull(it) 448526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikas } 44922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val fromApi = project.processProperty("fromApi") 45022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets classpath = project.files(doclavaConfig.resolve()) 45122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val rootFolder = project.projectDir 452a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets inputApiFile = if (fromApi != null) { 45322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Use an explicit API file. 454a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets File(rootFolder, "api/$fromApi.txt") 45522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } else { 456c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette // Use the most recently released API file bounded by toApi. 457a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets getLastReleasedApiFile(rootFolder, toApi) 45822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 45922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 46022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets outputApiXmlFile = File(project.docsDir(), 461c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette "release/${stripExtension(inputApiFile?.name ?: "creation")}.xml") 46222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 46322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets dependsOn(doclavaConfig) 46422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 46522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 46622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets/** 46722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Converts the <code>toApi</code>.txt file (or current.txt if not explicitly 46822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * defined using -PtoApi=<file>) to XML format for use by JDiff. 46922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets */ 47022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createNewApiXmlTask( 471c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets project: Project, 472c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets generateApi: DoclavaTask, 473c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets doclavaConfig: Configuration 474c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets) = 47522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig("newApiXml", ApiXmlConversionTask::class.java) { 47622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets classpath = project.files(doclavaConfig.resolve()) 47722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val toApi = project.processProperty("toApi") 47822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 47922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets if (toApi != null) { 48022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Use an explicit API file. 4819c2468843133a54b69a54e9e5f2b2677f2dc9ff3Sergey Vasilinets inputApiFile = File(project.projectDir, "api/$toApi.txt") 48222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } else { 48322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Use the current API file (e.g. current.txt). 48436bbc1da69c861fb6ee0e2f26a2ba2c36f949069Aurimas Liutikas inputApiFile = generateApi.apiFile!! 48522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets dependsOn(generateApi, doclavaConfig) 48622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 48722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 48822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets outputApiXmlFile = File(project.docsDir(), 489c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette "release/${stripExtension(inputApiFile?.name ?: "creation")}.xml") 49022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 49122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 49222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets/** 49322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * Generates API diffs. 49422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <p> 49522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * By default, diffs are generated for the delta between current.txt and the 49622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * next most recent X.Y.Z.txt API file. Behavior may be changed by specifying 49722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * one or both of -PtoApi and -PfromApi. 49822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <p> 49922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * If both fromApi and toApi are specified, diffs will be generated for 50022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * fromApi -> toApi. For example, 25.0.0 -> 26.0.0 diffs could be generated by 50122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * using: 50222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <br><code> 50322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * ./gradlew generateDiffs -PfromApi=25.0.0 -PtoApi=26.0.0 50422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * </code> 50522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <p> 50622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * If only toApi is specified, it MUST be specified as X.Y.Z and diffs will be 50722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * generated for (release before toApi) -> toApi. For example, 24.2.0 -> 25.0.0 50822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * diffs could be generated by using: 50922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <br><code> 51022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * ./gradlew generateDiffs -PtoApi=25.0.0 51122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * </code> 51222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <p> 51322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * If only fromApi is specified, diffs will be generated for fromApi -> current. 51422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * For example, lastApiReview -> current diffs could be generated by using: 51522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <br><code> 51622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * ./gradlew generateDiffs -PfromApi=lastApiReview 51722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * </code> 51822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets * <p> 51922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets */ 52022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createGenerateDiffsTask( 521c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets project: Project, 522c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets oldApiTask: ApiXmlConversionTask, 523c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets newApiTask: ApiXmlConversionTask, 524c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets jdiffConfig: Configuration 525c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets): JDiffTask = 52622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.tasks.createWithConfig("generateDiffs", JDiffTask::class.java) { 52722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Base classpath is Android SDK, sub-projects add their own. 5286eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas classpath = androidJarFile(project) 52922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 53022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // JDiff properties. 53122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets oldApiXmlFile = oldApiTask.outputApiXmlFile 53222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets newApiXmlFile = newApiTask.outputApiXmlFile 53322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 53422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val newApi = newApiXmlFile.name.substringBeforeLast('.') 53522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val docsDir = project.rootProject.docsDir() 53622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 53722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets newJavadocPrefix = "../../../../../reference/" 53822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets destinationDir = File(docsDir, "online/sdk/support_api_diff/${project.name}/$newApi") 53922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 54022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Javadoc properties. 54122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets docletpath = jdiffConfig.resolve() 54222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets title = "Support Library API Differences Report" 54322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 54422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets exclude("**/BuildConfig.java", "**/R.java") 54522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets dependsOn(oldApiTask, newApiTask, jdiffConfig) 54622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 54722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 54822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets// Generates a distribution artifact for online docs. 549a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinetsprivate fun createDistDocsTask(project: Project, generateDocs: DoclavaTask, ruleName: String = ""): Zip = 550a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets project.tasks.createWithConfig("dist${ruleName}Docs", Zip::class.java) { 55122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets dependsOn(generateDocs) 55222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets group = JavaBasePlugin.DOCUMENTATION_GROUP 55322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets description = "Generates distribution artifact for d.android.com-style documentation." 55422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets from(generateDocs.destinationDir) 555a74ee23e7d29eb98dbf54b810826825a9e0a66e3Sergey Vasilinets baseName = "android-support-$ruleName-docs" 55622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets version = project.buildNumber() 557c1e62a3019660ed7c06268f7aa0834b61a937dd7Sergey Vasilinets destinationDir = project.distDir() 55822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets doLast { 55922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets logger.lifecycle("'Wrote API reference to $archivePath") 56022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 56122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 56222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 563c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette/** 564c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * Creates a task to generate an API file from the platform SDK's source and stub JARs. 565c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * <p> 566c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette * This is useful for federating docs against the platform SDK when no API XML file is available. 567c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette */ 568c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viveretteprivate fun createGenerateSdkApiTask(project: Project, doclavaConfig: Configuration): DoclavaTask = 5696eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas project.tasks.createWithConfig("generateSdkApi", DoclavaTask::class.java) { 5706eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas dependsOn(doclavaConfig) 5716eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas description = "Generates API files for the current SDK." 5726eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas setDocletpath(doclavaConfig.resolve()) 5736eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas destinationDir = project.docsDir() 5746eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas classpath = androidJarFile(project) 5756eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas source(project.zipTree(androidSrcJarFile(project))) 5766eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas apiFile = sdkApiFile(project) 5776eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas generateDocs = false 5786eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas coreJavadocOptions { 5796eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas addStringOption("stubpackages", "android.*") 58022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 58122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 58222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 583a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsprivate val GENERATEDOCS_HIDDEN = listOf(105, 106, 107, 111, 112, 113, 115, 116, 121) 584a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinetsprivate val GENERATE_DOCS_CONFIG = ChecksConfig( 585a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets warnings = emptyList(), 586a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets hidden = GENERATEDOCS_HIDDEN + DEFAULT_DOCLAVA_CONFIG.hidden, 587a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets errors = ((101..122) - GENERATEDOCS_HIDDEN) 588a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets) 589a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets 59022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun createGenerateDocsTask( 591c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets project: Project, 592c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets generateSdkApiTask: DoclavaTask, 593c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets doclavaConfig: Configuration, 594c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets supportRootFolder: File, 595c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets dacOptions: DacOptions, 596c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets destDir: File, 597c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets taskName: String = "generateDocs" 598c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets): GenerateDocsTask = 599cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets project.tasks.createWithConfig(taskName, GenerateDocsTask::class.java) { 60022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets dependsOn(generateSdkApiTask, doclavaConfig) 60122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets group = JavaBasePlugin.DOCUMENTATION_GROUP 60222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets description = "Generates d.android.com-style documentation. To generate offline docs " + 60322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets "use \'-PofflineDocs=true\' parameter." 60422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 60522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets setDocletpath(doclavaConfig.resolve()) 60622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val offline = project.processProperty("offlineDocs") != null 607cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets destinationDir = File(destDir, if (offline) "offline" else "online") 6086eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas classpath = androidJarFile(project) 609a14b834943469c91933a93da68c81f0ebc0a5719Sergey Vasilinets checksConfig = GENERATE_DOCS_CONFIG 61022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addSinceFilesFrom(supportRootFolder) 61122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 61222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets coreJavadocOptions { 61322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addStringOption("templatedir", 61422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets "$supportRootFolder/../../external/doclava/res/assets/templates-sdk") 61522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addStringOption("samplesdir", "$supportRootFolder/samples") 61622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addMultilineMultiValueOption("federate").value = listOf( 61722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets listOf("Android", "https://developer.android.com") 61822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets ) 61922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addMultilineMultiValueOption("federationapi").value = listOf( 620c491eac2ccc584b28221a912b11041a9e5806c9eAlan Viverette listOf("Android", generateSdkApiTask.apiFile?.absolutePath) 62122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets ) 62222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addMultilineMultiValueOption("hdf").value = listOf( 62322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets listOf("android.whichdoc", "online"), 62422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets listOf("android.hasSamples", "true"), 62522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets listOf("dac", "true") 62622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets ) 62722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 62822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Specific to reference docs. 62922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets if (!offline) { 63022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addStringOption("toroot", "/") 63122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addBooleanOption("devsite", true) 6329895ad2c710951f0cc09ebd976544cfc56a8bc00Yigit Boyar addBooleanOption("yamlV2", true) 6331638535d6211a53187422aab94792fee85abca0cSergey Vasilinets addStringOption("dac_libraryroot", dacOptions.libraryroot) 6341638535d6211a53187422aab94792fee85abca0cSergey Vasilinets addStringOption("dac_dataname", dacOptions.dataname) 63522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 63622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 63722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets exclude("**/BuildConfig.java") 63822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 63922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 64022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets addArtifactsAndSince() 64122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 64222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 6431638535d6211a53187422aab94792fee85abca0cSergey Vasilinetsprivate data class Tasks( 644c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val generateApi: DoclavaTask, 645c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val generateDiffs: JDiffTask, 646c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets val checkApiTask: CheckApiTask 647c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets) 6481638535d6211a53187422aab94792fee85abca0cSergey Vasilinets 649c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinetsprivate fun initializeApiChecksForProject(project: Project): Tasks { 65022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets if (!project.hasProperty("docsDir")) { 65122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets project.extensions.add("docsDir", File(project.rootProject.docsDir(), project.name)) 65222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 65322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val version = project.version() 65422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val workingDir = project.projectDir 65522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 65622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val doclavaConfiguration = project.rootProject.configurations.getByName("doclava") 65722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val docletClasspath = doclavaConfiguration.resolve() 65822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val generateApi = createGenerateApiTask(project, docletClasspath) 65922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets generateApi.dependsOn(doclavaConfiguration) 66022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 66122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Make sure the API surface has not broken since the last release. 6621638535d6211a53187422aab94792fee85abca0cSergey Vasilinets val lastReleasedApiFile = getLastReleasedApiFile(workingDir, version) 66322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 66422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val whitelistFile = lastReleasedApiFile?.let { apiFile -> 66522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets File(lastReleasedApiFile.parentFile, stripExtension(apiFile.name) + ".ignore") 66622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 66722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val checkApiRelease = createCheckApiTask(project, 66822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets "checkApiRelease", 66922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets docletClasspath, 67022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets CHECK_API_CONFIG_RELEASE, 67122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets lastReleasedApiFile, 67222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets generateApi.apiFile!!, 67322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets whitelistFile) 67422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets checkApiRelease.dependsOn(generateApi) 67522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 67622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Allow a comma-delimited list of whitelisted errors. 67722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets if (project.hasProperty("ignore")) { 67822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets checkApiRelease.whitelistErrors = (project.properties["ignore"] as String) 67922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets .split(',').toSet() 68022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 68122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 68222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets // Check whether the development API surface has changed. 683ec5894ea3e6495260265e695ae6fa918233d4aaaAurimas Liutikas val verifyConfig = if (version.isPatch()) CHECK_API_CONFIG_PATCH else CHECK_API_CONFIG_DEVELOP 68422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val currentApiFile = getApiFile(workingDir, version) 68522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val checkApi = createCheckApiTask(project, 68622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets "checkApi", 68722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets docletClasspath, 68822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets verifyConfig, 68922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets currentApiFile, 69022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets generateApi.apiFile!!, 69122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets null) 69222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets checkApi.dependsOn(generateApi, checkApiRelease) 69322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 69422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets checkApi.group = JavaBasePlugin.VERIFICATION_GROUP 69522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets checkApi.description = "Verify the API surface." 69622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 69722b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val updateApiTask = createUpdateApiTask(project, checkApiRelease) 69841dec6a2d12f2dd7e8c8de0362d49d796da92244Sergey Vasilinets updateApiTask.dependsOn(checkApiRelease) 69922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val newApiTask = createNewApiXmlTask(project, generateApi, doclavaConfiguration) 70022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val oldApiTask = createOldApiXml(project, doclavaConfiguration) 70122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 70222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val jdiffConfiguration = project.rootProject.configurations.getByName("jdiff") 70322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets val generateDiffTask = createGenerateDiffsTask(project, 70422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets oldApiTask, 70522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets newApiTask, 70622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets jdiffConfiguration) 7071638535d6211a53187422aab94792fee85abca0cSergey Vasilinets return Tasks(generateApi, generateDiffTask, checkApi) 70822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 70922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 7105e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinetsfun hasApiTasks(project: Project, extension: SupportLibraryExtension): Boolean { 7115e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets if (!extension.publish) { 7125e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets project.logger.info("Project ${project.name} is not published, ignoring API tasks.") 7135e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return false 7145e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets } 7159c2468843133a54b69a54e9e5f2b2677f2dc9ff3Sergey Vasilinets 7165e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets if (!extension.generateDocs) { 7175e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets project.logger.info("Project ${project.name} specified generateDocs = false, " + 7185e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets "ignoring API tasks.") 7195e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return false 72022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 7215e2eb40367506c8f7aabe08ae78762d788a195f3Sergey Vasilinets return true 72222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets} 72322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 72422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun sdkApiFile(project: Project) = File(project.docsDir(), "release/sdk_current.txt") 72522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 7269c2468843133a54b69a54e9e5f2b2677f2dc9ff3Sergey Vasilinetsprivate fun <T : Task> TaskContainer.createWithConfig( 727c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets name: String, 728c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets taskClass: Class<T>, 729c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets config: T.() -> Unit 730c74c7fb5295c3d5e5f84815beb23c106a38ca65eSergey Vasilinets) = 73122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets create(name, taskClass) { task -> task.config() } 73222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 7336eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikasprivate fun androidJarFile(project: Project): FileCollection = 7346eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas project.files(arrayOf(File(project.fullSdkPath(), 7356eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas "platforms/android-${SupportConfig.CURRENT_SDK_VERSION}/android.jar"))) 7366eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas 7376eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikasprivate fun androidSrcJarFile(project: Project): File = File(project.fullSdkPath(), 7386eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas "platforms/android-${SupportConfig.CURRENT_SDK_VERSION}/android-stubs-src.jar") 7396eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikas 740cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsprivate fun PublishDocsRules.resolve(extension: SupportLibraryExtension) = 741cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets resolve(extension.mavenGroup!!, extension.project.name) 742cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 743cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsprivate fun Prebuilts.dependency(extension: SupportLibraryExtension) = 744cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets "${extension.mavenGroup}:${extension.project.name}:$version" 745cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 746cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinetsprivate fun BaseVariant.rFile() = "${applicationId.replace('.', '/')}/R.java" 747cb30642811f325654c34d2114df4e835d3ff823cSergey Vasilinets 74822b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets// Nasty part. Get rid of that eventually! 74922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun Project.docsDir(): File = properties["docsDir"] as File 75022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 7516eba1fcfea2ea33302d6d8c28b769fa771d9f4f2Aurimas Liutikasprivate fun Project.fullSdkPath(): File = rootProject.properties["fullSdkPath"] as File 75222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 7531638535d6211a53187422aab94792fee85abca0cSergey Vasilinetsprivate fun Project.version() = Version(project.version as String) 75422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 75522b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun Project.buildNumber() = properties["buildNumber"] as String 75622b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets 757c1e62a3019660ed7c06268f7aa0834b61a937dd7Sergey Vasilinetsprivate fun Project.distDir(): File = rootProject.properties["distDir"] as File 758c1e62a3019660ed7c06268f7aa0834b61a937dd7Sergey Vasilinets 75922b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinetsprivate fun Project.processProperty(name: String) = 76022b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets if (hasProperty(name)) { 76122b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets properties[name] as String 76222b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } else { 76322b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets null 76422b62144c356a00c27edec095c244c7a1c88f694Sergey Vasilinets } 765