Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 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 | |
Aurimas Liutikas | 526389b | 2018-02-27 14:01:24 -0800 | [diff] [blame] | 17 | package androidx.build.doclava |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 18 | |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 19 | import org.gradle.api.DefaultTask |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 20 | import org.gradle.api.file.FileCollection |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 21 | import org.gradle.api.provider.ListProperty |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 22 | import org.gradle.api.tasks.CacheableTask |
| 23 | import org.gradle.api.tasks.Classpath |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 24 | import org.gradle.api.tasks.Input |
| 25 | import org.gradle.api.tasks.InputFiles |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 26 | import org.gradle.api.tasks.Internal |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 27 | import org.gradle.api.tasks.Optional |
| 28 | import org.gradle.api.tasks.OutputDirectory |
| 29 | import org.gradle.api.tasks.OutputFile |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 30 | import org.gradle.api.tasks.PathSensitive |
| 31 | import org.gradle.api.tasks.PathSensitivity |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 32 | import org.gradle.api.tasks.TaskAction |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 33 | import org.gradle.process.ExecOperations |
| 34 | import org.gradle.workers.WorkAction |
| 35 | import org.gradle.workers.WorkParameters |
| 36 | import org.gradle.workers.WorkerExecutor |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 37 | import java.io.File |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 38 | import javax.inject.Inject |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 39 | |
| 40 | // external/doclava/src/com/google/doclava/Errors.java |
Sergey Vasilinets | a14b834 | 2018-05-30 10:21:02 -0700 | [diff] [blame] | 41 | val DEFAULT_DOCLAVA_CONFIG = ChecksConfig( |
Jeff Gaston | bc5a9b7 | 2020-09-18 14:06:14 -0400 | [diff] [blame] | 42 | errors = listOf( |
| 43 | 101, // unresolved link |
| 44 | 103, // unknown tag |
| 45 | 104 // unknown param name |
| 46 | ), |
| 47 | warnings = listOf(121 /* hidden type param */), |
| 48 | hidden = listOf( |
| 49 | 111, // hidden super class |
| 50 | 113 // @deprecation mismatch |
| 51 | ) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 52 | ) |
| 53 | |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 54 | @CacheableTask() |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 55 | abstract class DoclavaTask @Inject constructor( |
| 56 | private val workerExecutor: WorkerExecutor |
| 57 | ) : DefaultTask() { |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 58 | |
| 59 | // All lowercase name to match MinimalJavadocOptions#docletpath |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 60 | @Classpath |
Jeff Gaston | 6239441 | 2021-07-30 13:47:34 -0400 | [diff] [blame] | 61 | private lateinit var docletpath: FileCollection |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 62 | |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 63 | @Input |
Sergey Vasilinets | a14b834 | 2018-05-30 10:21:02 -0700 | [diff] [blame] | 64 | var checksConfig: ChecksConfig = DEFAULT_DOCLAVA_CONFIG |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 65 | |
| 66 | /** |
| 67 | * If non-null, the list of packages that will be treated as if they were |
| 68 | * marked with {@literal @hide}.<br> |
| 69 | * Packages names will be matched exactly; sub-packages are not automatically recognized. |
| 70 | */ |
| 71 | @Optional |
| 72 | @Input |
| 73 | var hiddenPackages: Collection<String>? = null |
| 74 | |
| 75 | /** |
Aurimas Liutikas | 0442cda | 2020-06-27 00:01:13 +0000 | [diff] [blame] | 76 | * If non-null and not-empty, the inclusion list of packages that will be present in the |
| 77 | * generated stubs; if null or empty, then all packages have stubs generated.<br> |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 78 | * Wildcards are accepted. |
| 79 | */ |
| 80 | @Optional |
| 81 | @Input |
| 82 | var stubPackages: Set<String>? = null |
| 83 | |
| 84 | @Input |
| 85 | var generateDocs = true |
| 86 | |
| 87 | /** |
| 88 | * If non-null, the location of where to place the generated api file. |
| 89 | * If this is non-null, then {@link #removedApiFile} must be non-null as well. |
| 90 | */ |
| 91 | @Optional |
| 92 | @OutputFile |
| 93 | var apiFile: File? = null |
| 94 | |
| 95 | /** |
| 96 | * If non-null, the location of where to place the generated removed api file. |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 97 | */ |
| 98 | @Optional |
| 99 | @OutputFile |
| 100 | var removedApiFile: File? = null |
| 101 | |
| 102 | /** |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 103 | * If non-null, the location to put the generated stub sources. |
| 104 | */ |
| 105 | @Optional |
| 106 | @OutputDirectory |
| 107 | var stubsDir: File? = null |
| 108 | |
| 109 | init { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 110 | // If none of generateDocs, apiFile, or stubJarsDir are true, then there is |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 111 | // no work to do. |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 112 | onlyIf({ generateDocs || apiFile != null || stubsDir != null }) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | /** |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 116 | * The doclet path which has the {@code com.google.doclava.Doclava} class. |
Aurimas Liutikas | db838b1 | 2018-02-12 10:15:23 -0800 | [diff] [blame] | 117 | * This option will override any doclet path set in this instance's |
| 118 | * {@link #options JavadocOptions}. |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 119 | * @see MinimalJavadocOptions#getDocletpath() |
| 120 | */ |
| 121 | @InputFiles |
| 122 | fun getDocletpath(): List<File> { |
Jeff Gaston | 6239441 | 2021-07-30 13:47:34 -0400 | [diff] [blame] | 123 | return docletpath.files.toList() |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Sets the doclet path which has the {@code com.gogole.doclava.Doclava} class. |
Aurimas Liutikas | db838b1 | 2018-02-12 10:15:23 -0800 | [diff] [blame] | 128 | * This option will override any doclet path set in this instance's |
| 129 | * {@link #options JavadocOptions}. |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 130 | * @see MinimalJavadocOptions#setDocletpath(java.util.List) |
| 131 | */ |
Jeff Gaston | 6239441 | 2021-07-30 13:47:34 -0400 | [diff] [blame] | 132 | fun setDocletpath(docletpath: FileCollection) { |
| 133 | this.docletpath = docletpath |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | @OutputDirectory |
| 137 | var destinationDir: File? = null |
| 138 | |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 139 | @InputFiles @Classpath |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 140 | var classpath: FileCollection? = null |
| 141 | |
Jeff Gaston | 5e3e7de | 2022-04-21 14:49:47 -0400 | [diff] [blame] | 142 | @[InputFiles PathSensitive(PathSensitivity.RELATIVE)] |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 143 | val sources = mutableListOf<FileCollection>() |
| 144 | |
| 145 | fun source(files: FileCollection) { |
| 146 | sources.add(files) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 147 | } |
| 148 | |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 149 | /** |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 150 | * Builder containing extra arguments |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 151 | */ |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 152 | @Internal |
| 153 | val extraArgumentsBuilder = DoclavaArgumentBuilder() |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 154 | |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 155 | @Input |
| 156 | val extraArguments = extraArgumentsBuilder.build() |
| 157 | |
| 158 | private fun computeArguments(): List<String> { |
| 159 | val args = DoclavaArgumentBuilder() |
| 160 | |
| 161 | // classpath |
Jeff Gaston | 6a8b766 | 2021-12-16 10:55:04 -0500 | [diff] [blame] | 162 | val classpathFile = File.createTempFile("doclavaClasspath", ".txt") |
| 163 | classpathFile.deleteOnExit() |
| 164 | classpathFile.bufferedWriter().use { writer -> |
| 165 | val classpathString = classpath!!.files.map({ f -> f.toString() }).joinToString(":") |
| 166 | writer.write(classpathString) |
| 167 | } |
| 168 | args.addStringOption("cp", "@$classpathFile") |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 169 | args.addStringOption("doclet", "com.google.doclava.Doclava") |
Jeff Gaston | 6a8b766 | 2021-12-16 10:55:04 -0500 | [diff] [blame] | 170 | args.addStringOption("docletpath", "@$classpathFile") |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 171 | |
| 172 | args.addOption("quiet") |
| 173 | args.addStringOption("encoding", "UTF-8") |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 174 | |
| 175 | // configure doclava error/warning/hide levels |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 176 | args.addRepeatableOption("hide", checksConfig.hidden) |
| 177 | args.addRepeatableOption("warning", checksConfig.warnings) |
| 178 | args.addRepeatableOption("error", checksConfig.errors) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 179 | |
| 180 | if (hiddenPackages != null) { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 181 | args.addRepeatableOption("hidePackage", hiddenPackages!!) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | if (!generateDocs) { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 185 | args.addOption("nodocs") |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | // If requested, generate the API files. |
| 189 | if (apiFile != null) { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 190 | args.addFileOption("api", apiFile!!) |
| 191 | if (removedApiFile != null) { |
| 192 | args.addFileOption("removedApi", removedApiFile!!) |
| 193 | } |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 194 | } |
| 195 | |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 196 | // If requested, generate stubs. |
| 197 | if (stubsDir != null) { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 198 | args.addFileOption("stubs", stubsDir!!) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 199 | val stubs = stubPackages |
| 200 | if (stubs != null) { |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 201 | args.addStringOption("stubpackages", stubs.joinToString(":")) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 202 | } |
| 203 | } |
| 204 | // Always treat this as an Android docs task. |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 205 | args.addOption("android") |
Aurimas Liutikas | 0b31a3c | 2019-11-12 07:45:24 -0800 | [diff] [blame] | 206 | |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 207 | // destination directory |
| 208 | args.addFileOption("d", destinationDir!!) |
| 209 | |
| 210 | // source files |
Jeff Gaston | 6a8b766 | 2021-12-16 10:55:04 -0500 | [diff] [blame] | 211 | val tmpArgs = File.createTempFile("doclavaSourceArgs", ".txt") |
| 212 | tmpArgs.deleteOnExit() |
| 213 | tmpArgs.bufferedWriter().use { writer -> |
| 214 | for (source in sources) { |
| 215 | for (file in source) { |
| 216 | val arg = file.toString() |
| 217 | // Doclava does not know how to parse Kotlin files |
| 218 | if (!arg.endsWith(".kt")) { |
| 219 | writer.write(arg) |
| 220 | writer.newLine() |
| 221 | } |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 222 | } |
| 223 | } |
| 224 | } |
Jeff Gaston | 6a8b766 | 2021-12-16 10:55:04 -0500 | [diff] [blame] | 225 | args.add("@$tmpArgs") |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 226 | |
| 227 | return args.build() + extraArgumentsBuilder.build() |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 228 | } |
| 229 | |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 230 | @TaskAction |
| 231 | fun generate() { |
| 232 | val args = computeArguments() |
Jeff Gaston | 6239441 | 2021-07-30 13:47:34 -0400 | [diff] [blame] | 233 | runDoclavaWithArgs(getDocletpath(), args, workerExecutor) |
Sergey Vasilinets | 17a1890 | 2017-11-14 23:40:41 -0800 | [diff] [blame] | 234 | } |
| 235 | } |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 236 | |
| 237 | class DoclavaArgumentBuilder { |
| 238 | fun add(value: String) { |
| 239 | args.add(value) |
| 240 | } |
| 241 | |
| 242 | fun addOption(name: String) { |
| 243 | args.add("-" + name) |
| 244 | } |
| 245 | |
| 246 | fun addStringOption(name: String, value: String) { |
| 247 | addOption(name) |
| 248 | args.add(value) |
| 249 | } |
| 250 | |
| 251 | fun addBooleanOption(name: String, value: Boolean) { |
| 252 | addStringOption(name, value.toString()) |
| 253 | } |
| 254 | |
| 255 | fun addFileOption(name: String, value: File) { |
| 256 | addStringOption(name, value.toString()) |
| 257 | } |
| 258 | |
| 259 | fun addRepeatableOption(name: String, values: Collection<*>) { |
| 260 | for (value in values) { |
| 261 | addStringOption(name, value.toString()) |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | fun addStringOption(name: String, values: Collection<String>) { |
| 266 | args.add("-" + name) |
| 267 | for (value in values) { |
| 268 | args.add(value) |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | fun build(): List<String> { |
| 273 | return args |
| 274 | } |
| 275 | |
| 276 | private val args = mutableListOf<String>() |
| 277 | } |
| 278 | |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 279 | interface DoclavaParams : WorkParameters { |
| 280 | fun getClasspath(): ListProperty<File> |
| 281 | fun getArgs(): ListProperty<String> |
| 282 | } |
| 283 | |
| 284 | fun runDoclavaWithArgs(classpath: List<File>, args: List<String>, workerExecutor: WorkerExecutor) { |
| 285 | val workQueue = workerExecutor.noIsolation() |
| 286 | workQueue.submit(DoclavaWorkAction::class.java) { parameters -> |
| 287 | parameters.getArgs().set(args) |
| 288 | parameters.getClasspath().set(classpath) |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 289 | } |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 290 | } |
| 291 | |
Jim Sproch | e1b4e86 | 2021-10-06 00:48:22 -0700 | [diff] [blame] | 292 | abstract class DoclavaWorkAction @Inject constructor( |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 293 | private val execOperations: ExecOperations |
| 294 | ) : WorkAction<DoclavaParams> { |
| 295 | override fun execute() { |
| 296 | val args = getParameters().getArgs().get() |
| 297 | val classpath = getParameters().getClasspath().get() |
| 298 | |
| 299 | execOperations.javaexec { |
| 300 | it.classpath(classpath) |
Aurimas Liutikas | 74dbab1 | 2021-06-08 13:46:02 -0700 | [diff] [blame] | 301 | it.mainClass.set("com.google.doclava.Doclava") |
Jeff Gaston | a56b1c1 | 2020-12-10 18:25:15 -0500 | [diff] [blame] | 302 | it.args = args |
| 303 | } |
| 304 | } |
Jeff Gaston | 8b24e08 | 2020-12-09 11:37:27 -0500 | [diff] [blame] | 305 | } |