1 /*
<lambda>null2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.build.docs
18 
19 import androidx.build.AndroidXExtension
20 import androidx.build.SoftwareType
21 import androidx.build.addToBuildOnServer
22 import androidx.build.checkapi.shouldConfigureApiTasks
23 import androidx.build.getSupportRootFolder
24 import androidx.build.multiplatformExtension
25 import androidx.build.uptodatedness.cacheEvenIfNoOutputs
26 import org.gradle.api.DefaultTask
27 import org.gradle.api.GradleException
28 import org.gradle.api.Project
29 import org.gradle.api.file.RegularFileProperty
30 import org.gradle.api.provider.Property
31 import org.gradle.api.tasks.CacheableTask
32 import org.gradle.api.tasks.Input
33 import org.gradle.api.tasks.InputFile
34 import org.gradle.api.tasks.PathSensitive
35 import org.gradle.api.tasks.PathSensitivity
36 import org.gradle.api.tasks.TaskAction
37 
38 /**
39  * Verifies that the text of the [projectPathProvider] can be found in the [tipOfTreeBuildFile] to
40  * enforce that projects enable docs generation.
41  */
42 @CacheableTask
43 abstract class CheckTipOfTreeDocsTask : DefaultTask() {
44     @get:[InputFile PathSensitive(PathSensitivity.NONE)]
45     abstract val tipOfTreeBuildFile: RegularFileProperty
46 
47     @get:Input abstract val projectPathProvider: Property<String>
48 
49     @get:Input abstract val type: Property<DocsType>
50 
51     @TaskAction
52     fun exec() {
53         val projectPath = projectPathProvider.get()
54         // Make sure not to allow a partial project path match, e.g. ":activity:activity" shouldn't
55         // match ":activity:activity-ktx", both need to be listed separately.
56         val projectDependency = "project(\"$projectPath\")"
57 
58         val prefix = type.get().prefix
59         // Check that projects are listed with the right configuration type (docs, kmpDocs, samples)
60         val fullExpectedText = "$prefix($projectDependency)"
61 
62         val fileContents = tipOfTreeBuildFile.asFile.get().readText()
63         val foundExpectedText = fileContents.contains(fullExpectedText)
64 
65         if (!foundExpectedText) {
66             // If this is a KMP project, check if it is present but configured as non-KMP
67             val message =
68                 if (fileContents.contains(projectDependency)) {
69                     "Project $projectPath has the wrong configuration type in " +
70                         "docs-tip-of-tree/build.gradle, should use $prefix\n\n" +
71                         "Update the entry for $projectPath in docs-tip-of-tree/build.gradle to " +
72                         "'$fullExpectedText'."
73                 } else {
74                     "Project $projectPath not found in docs-tip-of-tree/build.gradle\n\n" +
75                         "Use the project creation script (development/project-creator/" +
76                         "create_project.py) when setting up a project to make sure all required " +
77                         "steps are complete.\n\n" +
78                         "The project should be added to docs-tip-of-tree/build.gradle as " +
79                         "\'$fullExpectedText\'.\n\n" +
80                         "If this project should not have published refdocs, first check that the " +
81                         "library type listed in its build.gradle file is accurate. If it is, opt out " +
82                         "of refdoc generation using \'doNotDocumentReason = \"some reason\"\' in the " +
83                         "'androidx' configuration section (this is not common)."
84                 }
85             throw GradleException(message)
86         }
87     }
88 
89     companion object {
90         fun Project.setUpCheckDocsTask(extension: AndroidXExtension) {
91             project.afterEvaluate {
92                 if (!extension.requiresDocs()) return@afterEvaluate
93 
94                 val docsType =
95                     if (extension.type == SoftwareType.Companion.SAMPLES) {
96                         DocsType.SAMPLES
97                     } else if (multiplatformExtension != null) {
98                         DocsType.KMP
99                     } else {
100                         DocsType.STANDARD
101                     }
102 
103                 val checkDocs =
104                     project.tasks.register(
105                         "checkDocsTipOfTree",
106                         CheckTipOfTreeDocsTask::class.java
107                     ) { task ->
108                         task.tipOfTreeBuildFile.set(
109                             project.getSupportRootFolder().resolve("docs-tip-of-tree/build.gradle")
110                         )
111                         task.projectPathProvider.set(path)
112                         task.type.set(docsType)
113                         task.cacheEvenIfNoOutputs()
114                     }
115                 project.addToBuildOnServer(checkDocs)
116             }
117         }
118 
119         enum class DocsType(val prefix: String) {
120             STANDARD("docs"),
121             KMP("kmpDocs"),
122             SAMPLES("samples"),
123         }
124 
125         /**
126          * Whether the project should have public docs. True for API-tracked projects and samples,
127          * unless opted-out with [AndroidXExtension.doNotDocumentReason]
128          */
129         fun AndroidXExtension.requiresDocs() =
130             shouldConfigureApiTasks() && doNotDocumentReason == null
131     }
132 }
133