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&nbsp;Library&nbsp;API&nbsp;Differences&nbsp;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