1 /* <lambda>null2 * Copyright (C) 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 17 package com.android.tools.metalava 18 19 import com.android.SdkConstants 20 import com.android.SdkConstants.FN_FRAMEWORK_LIBRARY 21 import com.android.tools.lint.detector.api.isJdkFolder 22 import com.android.tools.metalava.cli.common.CommonOptions 23 import com.android.tools.metalava.cli.common.ExecutionEnvironment 24 import com.android.tools.metalava.cli.common.IssueReportingOptions 25 import com.android.tools.metalava.cli.common.MetalavaCliException 26 import com.android.tools.metalava.cli.common.SourceOptions 27 import com.android.tools.metalava.cli.common.Terminal 28 import com.android.tools.metalava.cli.common.TerminalColor 29 import com.android.tools.metalava.cli.common.Verbosity 30 import com.android.tools.metalava.cli.common.enumOption 31 import com.android.tools.metalava.cli.common.fileForPathInner 32 import com.android.tools.metalava.cli.common.stringToExistingDir 33 import com.android.tools.metalava.cli.common.stringToExistingFile 34 import com.android.tools.metalava.cli.common.stringToNewDir 35 import com.android.tools.metalava.cli.common.stringToNewFile 36 import com.android.tools.metalava.cli.compatibility.ARG_CHECK_COMPATIBILITY_API_RELEASED 37 import com.android.tools.metalava.cli.compatibility.ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED 38 import com.android.tools.metalava.cli.compatibility.CompatibilityCheckOptions 39 import com.android.tools.metalava.cli.compatibility.CompatibilityCheckOptions.CheckRequest 40 import com.android.tools.metalava.cli.lint.ApiLintOptions 41 import com.android.tools.metalava.cli.signature.SignatureFormatOptions 42 import com.android.tools.metalava.manifest.Manifest 43 import com.android.tools.metalava.manifest.emptyManifest 44 import com.android.tools.metalava.model.AnnotationManager 45 import com.android.tools.metalava.model.TypedefMode 46 import com.android.tools.metalava.model.source.DEFAULT_JAVA_LANGUAGE_LEVEL 47 import com.android.tools.metalava.model.source.DEFAULT_KOTLIN_LANGUAGE_LEVEL 48 import com.android.tools.metalava.model.text.ApiClassResolution 49 import com.android.tools.metalava.model.visitors.ApiVisitor 50 import com.android.tools.metalava.reporter.Baseline 51 import com.android.tools.metalava.reporter.Reporter 52 import com.android.tools.metalava.stub.StubWriterConfig 53 import com.android.utils.SdkUtils.wrap 54 import com.github.ajalt.clikt.core.NoSuchOption 55 import com.github.ajalt.clikt.parameters.groups.OptionGroup 56 import com.github.ajalt.clikt.parameters.options.default 57 import com.github.ajalt.clikt.parameters.options.deprecated 58 import com.github.ajalt.clikt.parameters.options.multiple 59 import com.github.ajalt.clikt.parameters.options.option 60 import com.github.ajalt.clikt.parameters.options.unique 61 import com.github.ajalt.clikt.parameters.types.choice 62 import com.github.ajalt.clikt.parameters.types.file 63 import com.github.ajalt.clikt.parameters.types.int 64 import java.io.File 65 import java.io.IOException 66 import java.io.PrintWriter 67 import java.io.StringWriter 68 import java.util.Optional 69 import kotlin.properties.ReadWriteProperty 70 import kotlin.reflect.KProperty 71 import org.jetbrains.jps.model.java.impl.JavaSdkUtil 72 73 /** 74 * A [ReadWriteProperty] that is used as the delegate for [options]. 75 * 76 * It provides read/write methods and also a [disallowAccess] method which when called will cause 77 * any attempt to read the [options] property to fail. This allows code to ensure that any code 78 * which it calls does not access the deprecated [options] property. 79 */ 80 object OptionsDelegate : ReadWriteProperty<Nothing?, Options> { 81 82 /** 83 * The value of this delegate. 84 * 85 * Is `null` if [setValue] has not been called since the last call to [disallowAccess]. In that 86 * case any attempt to read the value of this delegate will fail. 87 */ 88 private var possiblyNullOptions: Options? = Options() 89 90 /** 91 * The stack trace of the last caller to [disallowAccess] (if any) to make it easy to determine 92 * why a read of [options] failed. 93 */ 94 private var disallowerStackTrace: Throwable? = null 95 96 /** Prevent all future reads of [options] until the [setValue] method is next called. */ 97 fun disallowAccess() { 98 disallowerStackTrace = UnexpectedOptionsAccess("Global options property cleared") 99 possiblyNullOptions = null 100 } 101 102 override fun setValue(thisRef: Nothing?, property: KProperty<*>, value: Options) { 103 disallowerStackTrace = null 104 possiblyNullOptions = value 105 } 106 107 override fun getValue(thisRef: Nothing?, property: KProperty<*>): Options { 108 return possiblyNullOptions 109 ?: throw UnexpectedOptionsAccess("options is not set", disallowerStackTrace!!) 110 } 111 } 112 113 /** A private class to try and avoid it being caught and ignored. */ 114 private class UnexpectedOptionsAccess(message: String, cause: Throwable? = null) : 115 RuntimeException(message, cause) 116 117 /** 118 * Global options for the metadata extraction tool 119 * 120 * This is an empty options which is created to avoid having a nullable options. It is replaced with 121 * the actual options to use, either created from the command line arguments for the main process or 122 * with arguments supplied by tests. 123 */ 124 @Deprecated( 125 """ 126 Do not add any more usages of this and please remove any existing uses that you find. Global 127 variables tightly couple all the code that uses them making them hard to test, modularize and 128 reuse. Which is why there is an ongoing process to remove usages of global variables and 129 eventually the global variable itself. 130 """ 131 ) 132 var options by OptionsDelegate 133 134 private const val INDENT_WIDTH = 45 135 136 const val ARG_CLASS_PATH = "--classpath" 137 const val ARG_SOURCE_FILES = "--source-files" 138 const val ARG_API_CLASS_RESOLUTION = "--api-class-resolution" 139 const val ARG_DEX_API = "--dex-api" 140 const val ARG_SDK_VALUES = "--sdk-values" 141 const val ARG_MERGE_QUALIFIER_ANNOTATIONS = "--merge-qualifier-annotations" 142 const val ARG_MERGE_INCLUSION_ANNOTATIONS = "--merge-inclusion-annotations" 143 const val ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS = "--validate-nullability-from-merged-stubs" 144 const val ARG_VALIDATE_NULLABILITY_FROM_LIST = "--validate-nullability-from-list" 145 const val ARG_NULLABILITY_WARNINGS_TXT = "--nullability-warnings-txt" 146 const val ARG_NULLABILITY_ERRORS_NON_FATAL = "--nullability-errors-non-fatal" 147 const val ARG_DOC_STUBS = "--doc-stubs" 148 const val ARG_KOTLIN_STUBS = "--kotlin-stubs" 149 /** Used by Firebase, see b/116185431#comment15, not used by Android Platform or AndroidX */ 150 const val ARG_PROGUARD = "--proguard" 151 const val ARG_EXTRACT_ANNOTATIONS = "--extract-annotations" 152 const val ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS = "--exclude-documentation-from-stubs" 153 const val ARG_ENHANCE_DOCUMENTATION = "--enhance-documentation" 154 const val ARG_SKIP_READING_COMMENTS = "--ignore-comments" 155 const val ARG_HIDE_PACKAGE = "--hide-package" 156 const val ARG_MANIFEST = "--manifest" 157 const val ARG_MIGRATE_NULLNESS = "--migrate-nullness" 158 const val ARG_HIDE_ANNOTATION = "--hide-annotation" 159 const val ARG_REVERT_ANNOTATION = "--revert-annotation" 160 const val ARG_SUPPRESS_COMPATIBILITY_META_ANNOTATION = "--suppress-compatibility-meta-annotation" 161 const val ARG_SHOW_UNANNOTATED = "--show-unannotated" 162 const val ARG_APPLY_API_LEVELS = "--apply-api-levels" 163 const val ARG_GENERATE_API_LEVELS = "--generate-api-levels" 164 const val ARG_REMOVE_MISSING_CLASS_REFERENCES_IN_API_LEVELS = 165 "--remove-missing-class-references-in-api-levels" 166 const val ARG_ANDROID_JAR_PATTERN = "--android-jar-pattern" 167 const val ARG_CURRENT_VERSION = "--current-version" 168 const val ARG_FIRST_VERSION = "--first-version" 169 const val ARG_CURRENT_CODENAME = "--current-codename" 170 const val ARG_CURRENT_JAR = "--current-jar" 171 const val ARG_GENERATE_API_VERSION_HISTORY = "--generate-api-version-history" 172 const val ARG_API_VERSION_SIGNATURE_FILES = "--api-version-signature-files" 173 const val ARG_API_VERSION_NAMES = "--api-version-names" 174 const val ARG_JAVA_SOURCE = "--java-source" 175 const val ARG_KOTLIN_SOURCE = "--kotlin-source" 176 const val ARG_SDK_HOME = "--sdk-home" 177 const val ARG_JDK_HOME = "--jdk-home" 178 const val ARG_COMPILE_SDK_VERSION = "--compile-sdk-version" 179 const val ARG_INCLUDE_SOURCE_RETENTION = "--include-source-retention" 180 const val ARG_PASS_THROUGH_ANNOTATION = "--pass-through-annotation" 181 const val ARG_EXCLUDE_ANNOTATION = "--exclude-annotation" 182 const val ARG_STUB_PACKAGES = "--stub-packages" 183 const val ARG_STUB_IMPORT_PACKAGES = "--stub-import-packages" 184 const val ARG_DELETE_EMPTY_REMOVED_SIGNATURES = "--delete-empty-removed-signatures" 185 const val ARG_SUBTRACT_API = "--subtract-api" 186 const val ARG_TYPEDEFS_IN_SIGNATURES = "--typedefs-in-signatures" 187 const val ARG_IGNORE_CLASSES_ON_CLASSPATH = "--ignore-classes-on-classpath" 188 const val ARG_SDK_JAR_ROOT = "--sdk-extensions-root" 189 const val ARG_SDK_INFO_FILE = "--sdk-extensions-info" 190 const val ARG_USE_K2_UAST = "--Xuse-k2-uast" 191 const val ARG_SOURCE_MODEL_PROVIDER = "--source-model-provider" 192 193 class Options( 194 private val commonOptions: CommonOptions = CommonOptions(), 195 private val sourceOptions: SourceOptions = SourceOptions(), 196 private val issueReportingOptions: IssueReportingOptions = 197 IssueReportingOptions(commonOptions = commonOptions), 198 private val generalReportingOptions: GeneralReportingOptions = GeneralReportingOptions(), 199 private val apiSelectionOptions: ApiSelectionOptions = ApiSelectionOptions(), 200 val apiLintOptions: ApiLintOptions = ApiLintOptions(), 201 private val compatibilityCheckOptions: CompatibilityCheckOptions = CompatibilityCheckOptions(), 202 signatureFileOptions: SignatureFileOptions = SignatureFileOptions(), 203 signatureFormatOptions: SignatureFormatOptions = SignatureFormatOptions(), 204 stubGenerationOptions: StubGenerationOptions = StubGenerationOptions(), 205 ) : OptionGroup() { 206 /** Execution environment; initialized in [parse]. */ 207 private lateinit var executionEnvironment: ExecutionEnvironment 208 209 /** Writer to direct output to. */ 210 val stdout: PrintWriter 211 get() = executionEnvironment.stdout 212 /** Writer to direct error messages to. */ 213 val stderr: PrintWriter 214 get() = executionEnvironment.stderr 215 216 /** Internal list backing [sources] */ 217 private val mutableSources: MutableList<File> = mutableListOf() 218 /** Internal list backing [classpath] */ 219 private val mutableClassPath: MutableList<File> = mutableListOf() 220 /** Internal builder backing [hideAnnotations] */ 221 private val hideAnnotationsBuilder = AnnotationFilterBuilder() 222 /** Internal builder backing [revertAnnotations] */ 223 private val revertAnnotationsBuilder = AnnotationFilterBuilder() 224 /** Internal list backing [stubImportPackages] */ 225 private val mutableStubImportPackages: MutableSet<String> = mutableSetOf() 226 /** Internal list backing [mergeQualifierAnnotations] */ 227 private val mutableMergeQualifierAnnotations: MutableList<File> = mutableListOf() 228 /** Internal list backing [mergeInclusionAnnotations] */ 229 private val mutableMergeInclusionAnnotations: MutableList<File> = mutableListOf() 230 /** Internal list backing [hidePackages] */ 231 private val mutableHidePackages: MutableList<String> = mutableListOf() 232 /** Internal list backing [passThroughAnnotations] */ 233 private val mutablePassThroughAnnotations: MutableSet<String> = mutableSetOf() 234 /** Internal list backing [excludeAnnotations] */ 235 private val mutableExcludeAnnotations: MutableSet<String> = mutableSetOf() 236 237 /** API to subtract from signature and stub generation. Corresponds to [ARG_SUBTRACT_API]. */ 238 var subtractApi: File? = null 239 240 /** 241 * Backing property for [nullabilityAnnotationsValidator] 242 * 243 * This uses [Optional] to wrap the value as [lazy] cannot handle nullable values as it uses 244 * `null` as a special value. 245 * 246 * Creates [NullabilityAnnotationsValidator] lazily as it depends on a number of different 247 * options which may be supplied in different orders. 248 */ <lambda>null249 private val optionalNullabilityAnnotationsValidator by lazy { 250 Optional.ofNullable( 251 if (validateNullabilityFromMergedStubs || validateNullabilityFromList != null) { 252 NullabilityAnnotationsValidator( 253 reporter, 254 nullabilityErrorsFatal, 255 nullabilityWarningsTxt 256 ) 257 } else null 258 ) 259 } 260 261 /** Validator for nullability annotations, if validation is enabled. */ 262 val nullabilityAnnotationsValidator: NullabilityAnnotationsValidator? 263 get() = optionalNullabilityAnnotationsValidator.orElse(null) 264 265 /** Whether nullability validation errors should be considered fatal. */ 266 private var nullabilityErrorsFatal = true 267 268 /** 269 * A file to write non-fatal nullability validation issues to. If null, all issues are treated 270 * as fatal or else logged as warnings, depending on the value of [nullabilityErrorsFatal]. 271 */ 272 private var nullabilityWarningsTxt: File? = null 273 274 /** 275 * Whether to validate nullability for all the classes where we are merging annotations from 276 * external java stub files. If true, [nullabilityAnnotationsValidator] must be set. 277 */ 278 var validateNullabilityFromMergedStubs = false 279 280 /** 281 * A file containing a list of classes whose nullability annotations should be validated. If 282 * set, [nullabilityAnnotationsValidator] must also be set. 283 */ 284 var validateNullabilityFromList: File? = null 285 286 /** 287 * Whether to include element documentation (javadoc and KDoc) is in the generated stubs. 288 * (Copyright notices are not affected by this, they are always included. Documentation stubs 289 * (--doc-stubs) are not affected.) 290 */ 291 var includeDocumentationInStubs = true 292 293 /** 294 * Enhance documentation in various ways, for example auto-generating documentation based on 295 * source annotations present in the code. This is implied by --doc-stubs. 296 */ 297 var enhanceDocumentation = false 298 299 /** 300 * Whether to allow reading comments If false, any attempts by Metalava to read a PSI comment 301 * will return "" This can help callers to be sure that comment-only changes shouldn't affect 302 * Metalava output 303 */ 304 var allowReadingComments = true 305 306 /** Ths list of source roots in the common module */ 307 val commonSourcePath: List<File> by sourceOptions::commonSourcePath 308 309 /** The list of source roots */ 310 val sourcePath: List<File> by sourceOptions::sourcePath 311 312 /** The list of dependency jars */ 313 val classpath: List<File> = mutableClassPath 314 315 /** All source files to parse */ 316 var sources: List<File> = mutableSources 317 318 val apiClassResolution by 319 enumOption( 320 help = 321 """ 322 Determines how class resolution is performed when loading API signature files. Any 323 classes that cannot be found will be treated as empty.", 324 """ 325 .trimIndent(), <lambda>null326 enumValueHelpGetter = { it.help }, 327 default = ApiClassResolution.API_CLASSPATH, <lambda>null328 key = { it.optionValue }, 329 ) 330 331 val allShowAnnotations by apiSelectionOptions::allShowAnnotations 332 333 /** 334 * Whether to include unannotated elements if {@link #showAnnotations} is set. Note: This only 335 * applies to signature files, not stub files. 336 */ 337 var showUnannotated = false 338 339 /** Packages to include (if null, include all) */ 340 private var stubPackages: PackageFilter? = null 341 342 /** Packages to import (if empty, include all) */ 343 private var stubImportPackages: Set<String> = mutableStubImportPackages 344 345 /** Packages to exclude/hide */ 346 var hidePackages: List<String> = mutableHidePackages 347 348 /** Packages that we should skip generating even if not hidden; typically only used by tests */ 349 val skipEmitPackages 350 get() = executionEnvironment.testEnvironment?.skipEmitPackages ?: emptyList() 351 352 /** Annotations to hide */ 353 val hideAnnotations by lazy(hideAnnotationsBuilder::build) 354 355 /** Annotations to revert */ 356 val revertAnnotations by lazy(revertAnnotationsBuilder::build) 357 <lambda>null358 val annotationManager: AnnotationManager by lazy { 359 DefaultAnnotationManager( 360 DefaultAnnotationManager.Config( 361 passThroughAnnotations = passThroughAnnotations, 362 allShowAnnotations = allShowAnnotations, 363 showAnnotations = apiSelectionOptions.showAnnotations, 364 showSingleAnnotations = apiSelectionOptions.showSingleAnnotations, 365 showForStubPurposesAnnotations = apiSelectionOptions.showForStubPurposesAnnotations, 366 hideAnnotations = hideAnnotations, 367 revertAnnotations = revertAnnotations, 368 suppressCompatibilityMetaAnnotations = suppressCompatibilityMetaAnnotations, 369 excludeAnnotations = excludeAnnotations, 370 typedefMode = typedefMode, 371 apiPredicate = ApiPredicate(config = apiPredicateConfig), 372 previouslyReleasedCodebasesProvider = { 373 compatibilityCheckOptions.previouslyReleasedCodebases(signatureFileCache) 374 }, 375 ) 376 ) 377 } 378 <lambda>null379 internal val signatureFileCache by lazy { SignatureFileCache(annotationManager) } 380 381 /** Meta-annotations for which annotated APIs should not be checked for compatibility. */ 382 private val suppressCompatibilityMetaAnnotations by 383 option( 384 ARG_SUPPRESS_COMPATIBILITY_META_ANNOTATION, 385 help = 386 """ 387 Suppress compatibility checks for any elements within the scope of an 388 annotation which is itself annotated with the given meta-annotation. 389 """ 390 .trimIndent(), 391 metavar = "<meta-annotation class>", 392 ) 393 .multiple() 394 .unique() 395 396 /** 397 * Whether the generated API can contain classes that are not present in the source but are 398 * present on the classpath. Defaults to true for backwards compatibility but is set to false if 399 * any API signatures are imported as they must provide a complete set of all classes required 400 * but not provided by the generated API. 401 * 402 * Once all APIs are either self-contained or imported all the required references this will be 403 * removed and no classes will be allowed from the classpath JARs. 404 */ 405 private var allowClassesFromClasspath = true 406 407 /** The configuration options for the [ApiVisitor] class. */ <lambda>null408 val apiVisitorConfig by lazy { 409 ApiVisitor.Config( 410 packageFilter = stubPackages, 411 apiPredicateConfig = apiPredicateConfig, 412 ) 413 } 414 415 /** The configuration options for the [ApiAnalyzer] class. */ <lambda>null416 val apiAnalyzerConfig by lazy { 417 ApiAnalyzer.Config( 418 manifest = manifest, 419 hidePackages = hidePackages, 420 skipEmitPackages = skipEmitPackages, 421 mergeQualifierAnnotations = mergeQualifierAnnotations, 422 mergeInclusionAnnotations = mergeInclusionAnnotations, 423 stubImportPackages = stubImportPackages, 424 allShowAnnotations = allShowAnnotations, 425 apiPredicateConfig = apiPredicateConfig, 426 ) 427 } 428 <lambda>null429 val apiPredicateConfig by lazy { 430 ApiPredicate.Config( 431 ignoreShown = showUnannotated, 432 allowClassesFromClasspath = allowClassesFromClasspath, 433 addAdditionalOverrides = signatureFileFormat.addAdditionalOverrides, 434 ) 435 } 436 437 /** This is set directly by [preprocessArgv]. */ 438 private var verbosity: Verbosity = Verbosity.NORMAL 439 440 /** Whether to report warnings and other diagnostics along the way */ 441 val quiet: Boolean 442 get() = verbosity.quiet 443 444 /** 445 * Whether to report extra diagnostics along the way (note that verbose isn't the same as not 446 * quiet) 447 */ 448 val verbose: Boolean 449 get() = verbosity.verbose 450 <lambda>null451 internal val stubWriterConfig by lazy { 452 StubWriterConfig( 453 apiVisitorConfig = apiVisitorConfig, 454 kotlinStubs = kotlinStubs, 455 includeDocumentationInStubs = includeDocumentationInStubs, 456 ) 457 } 458 459 val stubsDir by stubGenerationOptions::stubsDir 460 val forceConvertToWarningNullabilityAnnotations by 461 stubGenerationOptions::forceConvertToWarningNullabilityAnnotations 462 val generateAnnotations by stubGenerationOptions::includeAnnotations 463 464 /** 465 * If set, a directory to write documentation stub files to. Corresponds to the --stubs/-stubs 466 * flag. 467 */ 468 var docStubsDir: File? = null 469 470 /** Whether code compiled from Kotlin should be emitted as .kt stubs instead of .java stubs */ 471 var kotlinStubs = false 472 473 /** Proguard Keep list file to write */ 474 var proguard: File? = null 475 476 val apiFile by signatureFileOptions::apiFile 477 val removedApiFile by signatureFileOptions::removedApiFile 478 val signatureFileFormat by signatureFormatOptions::fileFormat 479 480 /** Like [apiFile], but with JDiff xml format. */ 481 var apiXmlFile: File? = null 482 483 /** If set, a file to write the DEX signatures to. Corresponds to [ARG_DEX_API]. */ 484 var dexApiFile: File? = null 485 486 /** Path to directory to write SDK values to */ 487 var sdkValueDir: File? = null 488 489 /** 490 * If set, a file to write extracted annotations to. Corresponds to the --extract-annotations 491 * flag. 492 */ 493 var externalAnnotations: File? = null 494 495 /** An optional manifest [File]. */ 496 private val manifestFile by 497 option( 498 ARG_MANIFEST, 499 help = 500 """ 501 A manifest file, used to check permissions to cross check APIs and retrieve min_sdk_version. 502 (default: no manifest) 503 """ 504 .trimIndent() 505 ) 506 .file(mustExist = true, canBeDir = false, mustBeReadable = true) 507 508 /** 509 * A [Manifest] object to look up available permissions and min_sdk_version. 510 * 511 * Created lazily to make sure that the [reporter] has been initialized. 512 */ <lambda>null513 val manifest by lazy { manifestFile?.let { Manifest(it, reporter) } ?: emptyManifest } 514 515 /** The set of annotation classes that should be passed through unchanged */ 516 private var passThroughAnnotations = mutablePassThroughAnnotations 517 518 /** The set of annotation classes that should be removed from all outputs */ 519 private var excludeAnnotations = mutableExcludeAnnotations 520 521 /** A signature file to migrate nullness data from */ 522 var migrateNullsFrom: File? = null 523 524 /** The list of compatibility checks to run */ 525 val compatibilityChecks: List<CheckRequest> by compatibilityCheckOptions::compatibilityChecks 526 527 /** The API to use a base for the otherwise checked API during compat checks. */ 528 val baseApiForCompatCheck by compatibilityCheckOptions::baseApiForCompatCheck 529 530 /** Existing external annotation files to merge in */ 531 private var mergeQualifierAnnotations: List<File> = mutableMergeQualifierAnnotations 532 private var mergeInclusionAnnotations: List<File> = mutableMergeInclusionAnnotations 533 534 /** mapping from API level to android.jar files, if computing API levels */ 535 var apiLevelJars: Array<File>? = null 536 537 /** The api level of the codebase, or -1 if not known/specified */ 538 var currentApiLevel = -1 539 540 /** 541 * The first api level of the codebase; typically 1 but can be higher for example for the System 542 * API. 543 */ 544 var firstApiLevel = 1 545 546 /** 547 * The codename of the codebase: non-null string if this is a developer preview build, null if 548 * this is a release build. 549 */ 550 var currentCodeName: String? = null 551 552 /** API level XML file to generate */ 553 var generateApiLevelXml: File? = null 554 555 /** Whether references to missing classes should be removed from the api levels file. */ 556 var removeMissingClassesInApiLevels: Boolean = false 557 558 /** Reads API XML file to apply into documentation */ 559 var applyApiLevelsXml: File? = null 560 561 /** Directory of prebuilt extension SDK jars that contribute to the API */ 562 var sdkJarRoot: File? = null 563 564 /** 565 * Rules to filter out some extension SDK APIs from the API, and assign extensions to the APIs 566 * that are kept 567 */ 568 var sdkInfoFile: File? = null 569 570 /** 571 * The latest publicly released SDK extension version. When generating docs for d.android.com, 572 * the SDK extensions that have been finalized but not yet publicly released should be excluded 573 * from the docs. 574 * 575 * If null, the docs will include all SDK extensions. 576 */ 577 val latestReleasedSdkExtension by 578 option( 579 "--hide-sdk-extensions-newer-than", 580 help = 581 "Ignore SDK extensions version INT and above. Used to exclude finalized but not yet released SDK extensions." 582 ) 583 .int() 584 585 /** API version history JSON file to generate */ 586 var generateApiVersionsJson: File? = null 587 588 /** Ordered list of signatures for each past API version, if generating an API version JSON */ 589 var apiVersionSignatureFiles: List<File>? = null 590 591 /** 592 * The names of the API versions in [apiVersionSignatureFiles], in the same order, and the name 593 * of the current API version 594 */ 595 var apiVersionNames: List<String>? = null 596 597 /** Whether to include the signature file format version header in removed signature files */ 598 val includeSignatureFormatVersionRemoved: EmitFileHeader 599 get() = 600 if (deleteEmptyRemovedSignatures) { 601 EmitFileHeader.IF_NONEMPTY_FILE 602 } else { 603 EmitFileHeader.ALWAYS 604 } 605 606 var allBaselines: List<Baseline> = emptyList() 607 608 /** [IssueConfiguration] used by all reporters. */ 609 val issueConfiguration by issueReportingOptions::issueConfiguration 610 611 /** [Reporter] for general use. */ 612 lateinit var reporter: Reporter 613 614 /** 615 * [Reporter] for "api-lint". 616 * 617 * Initialized in [parse]. 618 */ 619 lateinit var reporterApiLint: Reporter 620 621 /** 622 * [Reporter] for "check-compatibility:*:released". (i.e. [ARG_CHECK_COMPATIBILITY_API_RELEASED] 623 * and [ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED]). 624 * 625 * Initialized in [parse]. 626 */ 627 lateinit var reporterCompatibilityReleased: Reporter 628 629 internal var allReporters: List<DefaultReporter> = emptyList() 630 631 /** If generating a removed signature file, and it is empty, delete it */ 632 var deleteEmptyRemovedSignatures = false 633 634 /** The language level to use for Java files, set with [ARG_JAVA_SOURCE] */ 635 var javaLanguageLevelAsString: String = DEFAULT_JAVA_LANGUAGE_LEVEL 636 637 /** The language level to use for Kotlin files, set with [ARG_KOTLIN_SOURCE] */ 638 var kotlinLanguageLevelAsString: String = DEFAULT_KOTLIN_LANGUAGE_LEVEL 639 640 /** 641 * The JDK to use as a platform, if set with [ARG_JDK_HOME]. This is only set when metalava is 642 * used for non-Android projects. 643 */ 644 var jdkHome: File? = null 645 646 /** 647 * The JDK to use as a platform, if set with [ARG_SDK_HOME]. If this is set along with 648 * [ARG_COMPILE_SDK_VERSION], metalava will automatically add the platform's android.jar file to 649 * the classpath if it does not already find the android.jar file in the classpath. 650 */ 651 private var sdkHome: File? = null 652 653 /** 654 * The compileSdkVersion, set by [ARG_COMPILE_SDK_VERSION]. For example, for R it would be "29". 655 * For R preview, it would be "R". 656 */ 657 private var compileSdkVersion: String? = null 658 659 /** 660 * How to handle typedef annotations in signature files; corresponds to 661 * $ARG_TYPEDEFS_IN_SIGNATURES 662 */ 663 private val typedefMode by 664 enumOption( 665 ARG_TYPEDEFS_IN_SIGNATURES, 666 help = """Whether to include typedef annotations in signature files.""", <lambda>null667 enumValueHelpGetter = { it.help }, 668 default = TypedefMode.NONE, <lambda>null669 key = { it.optionValue }, 670 ) 671 672 /** Temporary folder to use instead of the JDK default, if any */ 673 private var tempFolder: File? = null 674 675 var useK2Uast: Boolean? = null 676 677 val sourceModelProvider by 678 option( 679 ARG_SOURCE_MODEL_PROVIDER, 680 hidden = true, 681 ) 682 .choice("psi", "turbine") 683 .default("psi") 684 .deprecated( 685 """WARNING: The turbine model is under work and not usable for now. Eventually this option can be used to set the source model provider to either turbine or psi. The default is psi. """ 686 .trimIndent() 687 ) 688 parsenull689 fun parse( 690 executionEnvironment: ExecutionEnvironment, 691 args: Array<String>, 692 ) { 693 this.executionEnvironment = executionEnvironment 694 695 var androidJarPatterns: MutableList<String>? = null 696 var currentJar: File? = null 697 698 var index = 0 699 while (index < args.size) { 700 when (val arg = args[index]) { 701 // For now, we don't distinguish between bootclasspath and classpath 702 ARG_CLASS_PATH -> { 703 val path = getValue(args, ++index) 704 mutableClassPath.addAll(stringToExistingDirsOrJars(path)) 705 } 706 ARG_SOURCE_FILES -> { 707 val listString = getValue(args, ++index) 708 listString.split(",").forEach { path -> 709 mutableSources.addAll(stringToExistingFiles(path)) 710 } 711 } 712 ARG_SUBTRACT_API -> { 713 if (subtractApi != null) { 714 throw MetalavaCliException( 715 stderr = "Only one $ARG_SUBTRACT_API can be supplied" 716 ) 717 } 718 subtractApi = stringToExistingFile(getValue(args, ++index)) 719 } 720 721 // TODO: Remove the legacy --merge-annotations flag once it's no longer used to 722 // update P docs 723 ARG_MERGE_QUALIFIER_ANNOTATIONS, 724 "--merge-zips", 725 "--merge-annotations" -> 726 mutableMergeQualifierAnnotations.addAll( 727 stringToExistingDirsOrFiles(getValue(args, ++index)) 728 ) 729 ARG_MERGE_INCLUSION_ANNOTATIONS -> 730 mutableMergeInclusionAnnotations.addAll( 731 stringToExistingDirsOrFiles(getValue(args, ++index)) 732 ) 733 ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS -> { 734 validateNullabilityFromMergedStubs = true 735 } 736 ARG_VALIDATE_NULLABILITY_FROM_LIST -> { 737 validateNullabilityFromList = stringToExistingFile(getValue(args, ++index)) 738 } 739 ARG_NULLABILITY_WARNINGS_TXT -> 740 nullabilityWarningsTxt = stringToNewFile(getValue(args, ++index)) 741 ARG_NULLABILITY_ERRORS_NON_FATAL -> nullabilityErrorsFatal = false 742 ARG_SDK_VALUES -> sdkValueDir = stringToNewDir(getValue(args, ++index)) 743 ARG_DEX_API -> dexApiFile = stringToNewFile(getValue(args, ++index)) 744 ARG_SHOW_UNANNOTATED -> showUnannotated = true 745 ARG_HIDE_ANNOTATION -> hideAnnotationsBuilder.add(getValue(args, ++index)) 746 ARG_REVERT_ANNOTATION -> revertAnnotationsBuilder.add(getValue(args, ++index)) 747 ARG_DOC_STUBS -> docStubsDir = stringToNewDir(getValue(args, ++index)) 748 ARG_KOTLIN_STUBS -> kotlinStubs = true 749 ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS -> includeDocumentationInStubs = false 750 ARG_ENHANCE_DOCUMENTATION -> enhanceDocumentation = true 751 ARG_SKIP_READING_COMMENTS -> allowReadingComments = false 752 ARG_PASS_THROUGH_ANNOTATION -> { 753 val annotations = getValue(args, ++index) 754 annotations.split(",").forEach { path -> 755 mutablePassThroughAnnotations.add(path) 756 } 757 } 758 ARG_EXCLUDE_ANNOTATION -> { 759 val annotations = getValue(args, ++index) 760 annotations.split(",").forEach { path -> mutableExcludeAnnotations.add(path) } 761 } 762 ARG_PROGUARD -> proguard = stringToNewFile(getValue(args, ++index)) 763 ARG_HIDE_PACKAGE -> mutableHidePackages.add(getValue(args, ++index)) 764 ARG_STUB_PACKAGES -> { 765 val packages = getValue(args, ++index) 766 val filter = 767 stubPackages 768 ?: run { 769 val newFilter = PackageFilter() 770 stubPackages = newFilter 771 newFilter 772 } 773 filter.addPackages(packages) 774 } 775 ARG_STUB_IMPORT_PACKAGES -> { 776 val packages = getValue(args, ++index) 777 for (pkg in packages.split(File.pathSeparatorChar)) { 778 mutableStubImportPackages.add(pkg) 779 mutableHidePackages.add(pkg) 780 } 781 } 782 ARG_IGNORE_CLASSES_ON_CLASSPATH -> { 783 allowClassesFromClasspath = false 784 } 785 ARG_DELETE_EMPTY_REMOVED_SIGNATURES -> deleteEmptyRemovedSignatures = true 786 ARG_EXTRACT_ANNOTATIONS -> 787 externalAnnotations = stringToNewFile(getValue(args, ++index)) 788 ARG_MIGRATE_NULLNESS -> { 789 migrateNullsFrom = stringToExistingFile(getValue(args, ++index)) 790 } 791 792 // Extracting API levels 793 ARG_ANDROID_JAR_PATTERN -> { 794 val list = 795 androidJarPatterns 796 ?: run { 797 val list = arrayListOf<String>() 798 androidJarPatterns = list 799 list 800 } 801 list.add(getValue(args, ++index)) 802 } 803 ARG_CURRENT_VERSION -> { 804 currentApiLevel = Integer.parseInt(getValue(args, ++index)) 805 if (currentApiLevel <= 26) { 806 throw MetalavaCliException( 807 "Suspicious currentApi=$currentApiLevel, expected at least 27" 808 ) 809 } 810 } 811 ARG_FIRST_VERSION -> { 812 firstApiLevel = Integer.parseInt(getValue(args, ++index)) 813 } 814 ARG_CURRENT_CODENAME -> { 815 val codeName = getValue(args, ++index) 816 if (codeName != "REL") { 817 currentCodeName = codeName 818 } 819 } 820 ARG_CURRENT_JAR -> { 821 currentJar = stringToExistingFile(getValue(args, ++index)) 822 } 823 ARG_GENERATE_API_LEVELS -> { 824 generateApiLevelXml = stringToNewFile(getValue(args, ++index)) 825 } 826 ARG_APPLY_API_LEVELS -> { 827 applyApiLevelsXml = 828 if (args.contains(ARG_GENERATE_API_LEVELS)) { 829 // If generating the API file at the same time, it doesn't have 830 // to already exist 831 stringToNewFile(getValue(args, ++index)) 832 } else { 833 stringToExistingFile(getValue(args, ++index)) 834 } 835 } 836 ARG_REMOVE_MISSING_CLASS_REFERENCES_IN_API_LEVELS -> 837 removeMissingClassesInApiLevels = true 838 ARG_GENERATE_API_VERSION_HISTORY -> { 839 generateApiVersionsJson = stringToNewFile(getValue(args, ++index)) 840 } 841 ARG_API_VERSION_SIGNATURE_FILES -> { 842 apiVersionSignatureFiles = stringToExistingFiles(getValue(args, ++index)) 843 } 844 ARG_API_VERSION_NAMES -> { 845 apiVersionNames = getValue(args, ++index).split(' ') 846 } 847 ARG_JAVA_SOURCE -> { 848 val value = getValue(args, ++index) 849 javaLanguageLevelAsString = value 850 } 851 ARG_KOTLIN_SOURCE -> { 852 val value = getValue(args, ++index) 853 kotlinLanguageLevelAsString = value 854 } 855 ARG_JDK_HOME -> { 856 jdkHome = stringToExistingDir(getValue(args, ++index)) 857 } 858 ARG_SDK_HOME -> { 859 sdkHome = stringToExistingDir(getValue(args, ++index)) 860 } 861 ARG_COMPILE_SDK_VERSION -> { 862 compileSdkVersion = getValue(args, ++index) 863 } 864 ARG_USE_K2_UAST -> useK2Uast = true 865 ARG_SDK_JAR_ROOT -> { 866 sdkJarRoot = stringToExistingDir(getValue(args, ++index)) 867 } 868 ARG_SDK_INFO_FILE -> { 869 sdkInfoFile = stringToExistingFile(getValue(args, ++index)) 870 } 871 "--temp-folder" -> { 872 tempFolder = stringToNewOrExistingDir(getValue(args, ++index)) 873 } 874 875 // Option only meant for tests (not documented); doesn't work in all cases (to do 876 // that we'd 877 // need JNA to call libc) 878 "--pwd" -> { 879 val pwd = stringToExistingDir(getValue(args, ++index)).absoluteFile 880 System.setProperty("user.dir", pwd.path) 881 } 882 else -> { 883 if (arg.startsWith("-")) { 884 // Some other argument: display usage info and exit 885 throw NoSuchOption(givenName = arg) 886 } else { 887 // All args that don't start with "-" are taken to be filenames 888 mutableSources.addAll(stringToExistingFiles(arg)) 889 } 890 } 891 } 892 893 ++index 894 } 895 896 if (generateApiLevelXml != null) { 897 if (currentApiLevel == -1) { 898 throw MetalavaCliException( 899 stderr = "$ARG_GENERATE_API_LEVELS requires $ARG_CURRENT_VERSION" 900 ) 901 } 902 903 // <String> is redundant here but while IDE (with newer type inference engine 904 // understands that) the current 1.3.x compiler does not 905 @Suppress("RemoveExplicitTypeArguments") 906 val patterns = androidJarPatterns ?: run { mutableListOf<String>() } 907 // Fallbacks 908 patterns.add("prebuilts/tools/common/api-versions/android-%/android.jar") 909 patterns.add("prebuilts/sdk/%/public/android.jar") 910 apiLevelJars = 911 findAndroidJars( 912 args, 913 patterns, 914 firstApiLevel, 915 currentApiLevel + if (isDeveloperPreviewBuild()) 1 else 0, 916 currentJar 917 ) 918 } 919 920 if ((sdkJarRoot == null) != (sdkInfoFile == null)) { 921 throw MetalavaCliException( 922 stderr = "$ARG_SDK_JAR_ROOT and $ARG_SDK_INFO_FILE must both be supplied" 923 ) 924 } 925 926 // apiVersionNames will include the current version but apiVersionSignatureFiles will not, 927 // so there should be 1 more name than signature file (or both can be null) 928 val numVersionNames = apiVersionNames?.size ?: 0 929 val numVersionFiles = apiVersionSignatureFiles?.size ?: 0 930 if (numVersionNames != 0 && numVersionNames != numVersionFiles + 1) { 931 throw MetalavaCliException( 932 "$ARG_API_VERSION_NAMES must have one more version than $ARG_API_VERSION_SIGNATURE_FILES to include the current version name" 933 ) 934 } 935 936 // If the caller has not explicitly requested that unannotated classes and 937 // members should be shown in the output then only show them if no annotations were 938 // provided. 939 if (!showUnannotated && allShowAnnotations.isEmpty()) { 940 showUnannotated = true 941 } 942 943 // Initialize the reporters. 944 val baseline = generalReportingOptions.baseline 945 reporter = 946 DefaultReporter( 947 environment = executionEnvironment.reporterEnvironment, 948 issueConfiguration = issueConfiguration, 949 baseline = baseline, 950 packageFilter = stubPackages, 951 config = issueReportingOptions.reporterConfig, 952 ) 953 reporterApiLint = 954 DefaultReporter( 955 environment = executionEnvironment.reporterEnvironment, 956 issueConfiguration = issueConfiguration, 957 baseline = apiLintOptions.baseline ?: baseline, 958 errorMessage = apiLintOptions.errorMessage, 959 packageFilter = stubPackages, 960 config = issueReportingOptions.reporterConfig, 961 ) 962 reporterCompatibilityReleased = 963 DefaultReporter( 964 environment = executionEnvironment.reporterEnvironment, 965 issueConfiguration = issueConfiguration, 966 baseline = compatibilityCheckOptions.baseline ?: baseline, 967 errorMessage = compatibilityCheckOptions.errorMessage, 968 packageFilter = stubPackages, 969 config = issueReportingOptions.reporterConfig, 970 ) 971 972 // Build "all baselines" and "all reporters" 973 974 // Baselines are nullable, so selectively add to the list. 975 allBaselines = 976 listOfNotNull(baseline, apiLintOptions.baseline, compatibilityCheckOptions.baseline) 977 978 // Reporters are non-null. 979 // Downcast to DefaultReporter to gain access to some implementation specific functionality. 980 allReporters = 981 listOf( 982 issueReportingOptions.bootstrapReporter, 983 reporter, 984 reporterApiLint, 985 reporterCompatibilityReleased, 986 ) 987 .map { it as DefaultReporter } 988 989 updateClassPath() 990 } 991 isDeveloperPreviewBuildnull992 fun isDeveloperPreviewBuild(): Boolean = currentCodeName != null 993 994 /** Update the classpath to insert android.jar or JDK classpath elements if necessary */ 995 private fun updateClassPath() { 996 val sdkHome = sdkHome 997 val jdkHome = jdkHome 998 999 if ( 1000 sdkHome != null && 1001 compileSdkVersion != null && 1002 classpath.none { it.name == FN_FRAMEWORK_LIBRARY } 1003 ) { 1004 val jar = File(sdkHome, "platforms/android-$compileSdkVersion") 1005 if (jar.isFile) { 1006 mutableClassPath.add(jar) 1007 } else { 1008 throw MetalavaCliException( 1009 stderr = 1010 "Could not find android.jar for API level " + 1011 "$compileSdkVersion in SDK $sdkHome: $jar does not exist" 1012 ) 1013 } 1014 if (jdkHome != null) { 1015 throw MetalavaCliException( 1016 stderr = "Do not specify both $ARG_SDK_HOME and $ARG_JDK_HOME" 1017 ) 1018 } 1019 } else if (jdkHome != null) { 1020 val isJre = !isJdkFolder(jdkHome) 1021 val roots = JavaSdkUtil.getJdkClassesRoots(jdkHome.toPath(), isJre).map { it.toFile() } 1022 mutableClassPath.addAll(roots) 1023 } 1024 } 1025 1026 /** 1027 * Find an android stub jar that matches the given criteria. 1028 * 1029 * Note because the default baseline file is not explicitly set in the command line, this file 1030 * would trigger a --strict-input-files violation. To avoid that, use 1031 * --strict-input-files-exempt to exempt the jar directory. 1032 */ findAndroidJarsnull1033 private fun findAndroidJars( 1034 args: Array<String>, 1035 androidJarPatterns: List<String>, 1036 minApi: Int, 1037 currentApiLevel: Int, 1038 currentJar: File? 1039 ): Array<File> { 1040 val apiLevelFiles = mutableListOf<File>() 1041 // api level 0: placeholder, should not be processed. 1042 // (This is here because we want the array index to match 1043 // the API level) 1044 val element = File("not an api: the starting API index is $minApi") 1045 for (i in 0 until minApi) { 1046 apiLevelFiles.add(element) 1047 } 1048 1049 // Get all the android.jar. They are in platforms-# 1050 var apiLevel = minApi - 1 1051 while (true) { 1052 apiLevel++ 1053 try { 1054 var jar: File? = null 1055 if (apiLevel == currentApiLevel) { 1056 jar = currentJar 1057 } 1058 if (jar == null) { 1059 jar = getAndroidJarFile(apiLevel, androidJarPatterns) 1060 } 1061 if (jar == null || !jar.isFile) { 1062 if (verbose) { 1063 stdout.println("Last API level found: ${apiLevel - 1}") 1064 } 1065 1066 if (apiLevel < 28) { 1067 // Clearly something is wrong with the patterns; this should result in a 1068 // build error 1069 val argList = mutableListOf<String>() 1070 args.forEachIndexed { index, arg -> 1071 if (arg == ARG_ANDROID_JAR_PATTERN) { 1072 argList.add(args[index + 1]) 1073 } 1074 } 1075 throw MetalavaCliException( 1076 stderr = 1077 "Could not find android.jar for API level $apiLevel; the " + 1078 "$ARG_ANDROID_JAR_PATTERN set might be invalid: ${argList.joinToString()}" 1079 ) 1080 } 1081 1082 break 1083 } 1084 if (verbose) { 1085 stdout.println("Found API $apiLevel at ${jar.path}") 1086 } 1087 apiLevelFiles.add(jar) 1088 } catch (e: IOException) { 1089 e.printStackTrace() 1090 } 1091 } 1092 1093 return apiLevelFiles.toTypedArray() 1094 } 1095 getAndroidJarFilenull1096 private fun getAndroidJarFile(apiLevel: Int, patterns: List<String>): File? { 1097 return patterns 1098 .map { fileForPathInner(it.replace("%", apiLevel.toString())) } 1099 .firstOrNull { it.isFile } 1100 } 1101 getValuenull1102 private fun getValue(args: Array<String>, index: Int): String { 1103 if (index >= args.size) { 1104 throw MetalavaCliException("Missing argument for ${args[index - 1]}") 1105 } 1106 return args[index] 1107 } 1108 stringToExistingDirsOrJarsnull1109 private fun stringToExistingDirsOrJars(value: String): List<File> { 1110 val files = mutableListOf<File>() 1111 for (path in value.split(File.pathSeparatorChar)) { 1112 val file = fileForPathInner(path) 1113 if (!file.isDirectory && !(file.path.endsWith(SdkConstants.DOT_JAR) && file.isFile)) { 1114 throw MetalavaCliException("$file is not a jar or directory") 1115 } 1116 files.add(file) 1117 } 1118 return files 1119 } 1120 stringToExistingDirsOrFilesnull1121 private fun stringToExistingDirsOrFiles(value: String): List<File> { 1122 val files = mutableListOf<File>() 1123 for (path in value.split(File.pathSeparatorChar)) { 1124 val file = fileForPathInner(path) 1125 if (!file.exists()) { 1126 throw MetalavaCliException("$file does not exist") 1127 } 1128 files.add(file) 1129 } 1130 return files 1131 } 1132 1133 @Suppress("unused") stringToExistingFileOrDirnull1134 private fun stringToExistingFileOrDir(value: String): File { 1135 val file = fileForPathInner(value) 1136 if (!file.exists()) { 1137 throw MetalavaCliException("$file is not a file or directory") 1138 } 1139 return file 1140 } 1141 stringToExistingFilesnull1142 private fun stringToExistingFiles(value: String): List<File> { 1143 return value 1144 .split(File.pathSeparatorChar) 1145 .map { fileForPathInner(it) } 1146 .map { file -> 1147 if (!file.isFile) { 1148 throw MetalavaCliException("$file is not a file") 1149 } 1150 file 1151 } 1152 } 1153 stringToNewOrExistingDirnull1154 private fun stringToNewOrExistingDir(value: String): File { 1155 val dir = fileForPathInner(value) 1156 if (!dir.isDirectory) { 1157 val ok = dir.mkdirs() 1158 if (!ok) { 1159 throw MetalavaCliException("Could not create $dir") 1160 } 1161 } 1162 return dir 1163 } 1164 } 1165 1166 object OptionsHelp { getUsagenull1167 fun getUsage(terminal: Terminal, width: Int): String { 1168 val usage = StringWriter() 1169 val printWriter = PrintWriter(usage) 1170 usage(printWriter, terminal, width) 1171 return usage.toString() 1172 } 1173 usagenull1174 private fun usage(out: PrintWriter, terminal: Terminal, width: Int) { 1175 val args = 1176 arrayOf( 1177 "", 1178 "API sources:", 1179 "$ARG_SOURCE_FILES <files>", 1180 "A comma separated list of source files to be parsed. Can also be " + 1181 "@ followed by a path to a text file containing paths to the full set of files to parse.", 1182 "$ARG_CLASS_PATH <paths>", 1183 "One or more directories or jars (separated by " + 1184 "`${File.pathSeparator}`) containing classes that should be on the classpath when parsing the " + 1185 "source files", 1186 "$ARG_MERGE_QUALIFIER_ANNOTATIONS <file>", 1187 "An external annotations file to merge and overlay " + 1188 "the sources, or a directory of such files. Should be used for annotations intended for " + 1189 "inclusion in the API to be written out, e.g. nullability. Formats supported are: IntelliJ's " + 1190 "external annotations database format, .jar or .zip files containing those, Android signature " + 1191 "files, and Java stub files.", 1192 "$ARG_MERGE_INCLUSION_ANNOTATIONS <file>", 1193 "An external annotations file to merge and overlay " + 1194 "the sources, or a directory of such files. Should be used for annotations which determine " + 1195 "inclusion in the API to be written out, i.e. show and hide. The only format supported is " + 1196 "Java stub files.", 1197 ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS, 1198 "Triggers validation of nullability annotations " + 1199 "for any class where $ARG_MERGE_QUALIFIER_ANNOTATIONS includes a Java stub file.", 1200 ARG_VALIDATE_NULLABILITY_FROM_LIST, 1201 "Triggers validation of nullability annotations " + 1202 "for any class listed in the named file (one top-level class per line, # prefix for comment line).", 1203 "$ARG_NULLABILITY_WARNINGS_TXT <file>", 1204 "Specifies where to write warnings encountered during " + 1205 "validation of nullability annotations. (Does not trigger validation by itself.)", 1206 ARG_NULLABILITY_ERRORS_NON_FATAL, 1207 "Specifies that errors encountered during validation of " + 1208 "nullability annotations should not be treated as errors. They will be written out to the " + 1209 "file specified in $ARG_NULLABILITY_WARNINGS_TXT instead.", 1210 "$ARG_HIDE_PACKAGE <package>", 1211 "Remove the given packages from the API even if they have not been " + 1212 "marked with @hide", 1213 "$ARG_HIDE_ANNOTATION <annotation class>", 1214 "Treat any elements annotated with the given annotation " + "as hidden", 1215 ARG_SHOW_UNANNOTATED, 1216 "Include un-annotated public APIs in the signature file as well", 1217 "$ARG_JAVA_SOURCE <level>", 1218 "Sets the source level for Java source files; default is $DEFAULT_JAVA_LANGUAGE_LEVEL.", 1219 "$ARG_KOTLIN_SOURCE <level>", 1220 "Sets the source level for Kotlin source files; default is $DEFAULT_KOTLIN_LANGUAGE_LEVEL.", 1221 "$ARG_SDK_HOME <dir>", 1222 "If set, locate the `android.jar` file from the given Android SDK", 1223 "$ARG_COMPILE_SDK_VERSION <api>", 1224 "Use the given API level", 1225 "$ARG_JDK_HOME <dir>", 1226 "If set, add the Java APIs from the given JDK to the classpath", 1227 "$ARG_STUB_PACKAGES <package-list>", 1228 "List of packages (separated by ${File.pathSeparator}) which will " + 1229 "be used to filter out irrelevant code. If specified, only code in these packages will be " + 1230 "included in signature files, stubs, etc. (This is not limited to just the stubs; the name " + 1231 "is historical.) You can also use \".*\" at the end to match subpackages, so `foo.*` will " + 1232 "match both `foo` and `foo.bar`.", 1233 "$ARG_SUBTRACT_API <api file>", 1234 "Subtracts the API in the given signature or jar file from the " + 1235 "current API being emitted via $ARG_API, $ARG_STUBS, $ARG_DOC_STUBS, etc. " + 1236 "Note that the subtraction only applies to classes; it does not subtract members.", 1237 ARG_IGNORE_CLASSES_ON_CLASSPATH, 1238 "Prevents references to classes on the classpath from being added to " + 1239 "the generated stub files.", 1240 ARG_SKIP_READING_COMMENTS, 1241 "Ignore any comments in source files.", 1242 "", 1243 "Extracting Signature Files:", 1244 // TODO: Document --show-annotation! 1245 "$ARG_DEX_API <file>", 1246 "Generate a DEX signature descriptor file listing the APIs", 1247 "$ARG_PROGUARD <file>", 1248 "Write a ProGuard keep file for the API", 1249 "$ARG_SDK_VALUES <dir>", 1250 "Write SDK values files to the given directory", 1251 "", 1252 "Generating Stubs:", 1253 "$ARG_DOC_STUBS <dir>", 1254 "Generate documentation stub source files for the API. Documentation stub " + 1255 "files are similar to regular stub files, but there are some differences. For example, in " + 1256 "the stub files, we'll use special annotations like @RecentlyNonNull instead of @NonNull to " + 1257 "indicate that an element is recently marked as non null, whereas in the documentation stubs we'll " + 1258 "just list this as @NonNull. Another difference is that @doconly elements are included in " + 1259 "documentation stubs, but not regular stubs, etc.", 1260 ARG_KOTLIN_STUBS, 1261 "[CURRENTLY EXPERIMENTAL] If specified, stubs generated from Kotlin source code will " + 1262 "be written in Kotlin rather than the Java programming language.", 1263 "$ARG_PASS_THROUGH_ANNOTATION <annotation classes>", 1264 "A comma separated list of fully qualified names of " + 1265 "annotation classes that must be passed through unchanged.", 1266 "$ARG_EXCLUDE_ANNOTATION <annotation classes>", 1267 "A comma separated list of fully qualified names of " + 1268 "annotation classes that must be stripped from metalava's outputs.", 1269 ARG_ENHANCE_DOCUMENTATION, 1270 "Enhance documentation in various ways, for example auto-generating documentation based on source " + 1271 "annotations present in the code. This is implied by --doc-stubs.", 1272 ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS, 1273 "Exclude element documentation (javadoc and kdoc) " + 1274 "from the generated stubs. (Copyright notices are not affected by this, they are always included. " + 1275 "Documentation stubs (--doc-stubs) are not affected.)", 1276 "", 1277 "Diffs and Checks:", 1278 "$ARG_MIGRATE_NULLNESS <api file>", 1279 "Compare nullness information with the previous stable API " + 1280 "and mark newly annotated APIs as under migration.", 1281 "", 1282 "Extracting Annotations:", 1283 "$ARG_EXTRACT_ANNOTATIONS <zipfile>", 1284 "Extracts source annotations from the source files and writes " + 1285 "them into the given zip file", 1286 ARG_INCLUDE_SOURCE_RETENTION, 1287 "If true, include source-retention annotations in the stub files. Does " + 1288 "not apply to signature files. Source retention annotations are extracted into the external " + 1289 "annotations files instead.", 1290 "", 1291 "Injecting API Levels:", 1292 "$ARG_APPLY_API_LEVELS <api-versions.xml>", 1293 "Reads an XML file containing API level descriptions " + 1294 "and merges the information into the documentation", 1295 "", 1296 "Extracting API Levels:", 1297 "$ARG_GENERATE_API_LEVELS <xmlfile>", 1298 "Reads android.jar SDK files and generates an XML file recording " + 1299 "the API level for each class, method and field", 1300 ARG_REMOVE_MISSING_CLASS_REFERENCES_IN_API_LEVELS, 1301 "Removes references to missing classes when generating the API levels XML file. " + 1302 "This can happen when generating the XML file for the non-updatable portions of " + 1303 "the module-lib sdk, as those non-updatable portions can reference classes that are " + 1304 "part of an updatable apex.", 1305 "$ARG_ANDROID_JAR_PATTERN <pattern>", 1306 "Patterns to use to locate Android JAR files. The default " + 1307 "is \$ANDROID_HOME/platforms/android-%/android.jar.", 1308 ARG_FIRST_VERSION, 1309 "Sets the first API level to generate an API database from; usually 1", 1310 ARG_CURRENT_VERSION, 1311 "Sets the current API level of the current source code", 1312 ARG_CURRENT_CODENAME, 1313 "Sets the code name for the current source code", 1314 ARG_CURRENT_JAR, 1315 "Points to the current API jar, if any", 1316 ARG_SDK_JAR_ROOT, 1317 "Points to root of prebuilt extension SDK jars, if any. This directory is expected to " + 1318 "contain snapshots of historical extension SDK versions in the form of stub jars. " + 1319 "The paths should be on the format \"<int>/public/<module-name>.jar\", where <int> " + 1320 "corresponds to the extension SDK version, and <module-name> to the name of the mainline module.", 1321 ARG_SDK_INFO_FILE, 1322 "Points to map of extension SDK APIs to include, if any. The file is a plain text file " + 1323 "and describes, per extension SDK, what APIs from that extension to include in the " + 1324 "file created via $ARG_GENERATE_API_LEVELS. The format of each line is one of the following: " + 1325 "\"<module-name> <pattern> <ext-name> [<ext-name> [...]]\", where <module-name> is the " + 1326 "name of the mainline module this line refers to, <pattern> is a common Java name prefix " + 1327 "of the APIs this line refers to, and <ext-name> is a list of extension SDK names " + 1328 "in which these SDKs first appeared, or \"<ext-name> <ext-id> <type>\", where " + 1329 "<ext-name> is the name of an SDK, " + 1330 "<ext-id> its numerical ID and <type> is one of " + 1331 "\"platform\" (the Android platform SDK), " + 1332 "\"platform-ext\" (an extension to the Android platform SDK), " + 1333 "\"standalone\" (a separate SDK). " + 1334 "Fields are separated by whitespace. " + 1335 "A mainline module may be listed multiple times. " + 1336 "The special pattern \"*\" refers to all APIs in the given mainline module. " + 1337 "Lines beginning with # are comments.", 1338 "", 1339 "Generating API version history:", 1340 "$ARG_GENERATE_API_VERSION_HISTORY <jsonfile>", 1341 "Reads API signature files and generates a JSON file recording the API version each " + 1342 "class, method, and field was added in and (if applicable) deprecated in. " + 1343 "Required to generate API version JSON.", 1344 "$ARG_API_VERSION_SIGNATURE_FILES <files>", 1345 "An ordered list of text API signature files. The oldest API version should be " + 1346 "first, the newest last. This should not include a signature file for the " + 1347 "current API version, which will be parsed from the provided source files. Not " + 1348 "required to generate API version JSON if the current version is the only version.", 1349 "$ARG_API_VERSION_NAMES <strings>", 1350 "An ordered list of strings with the names to use for the API versions from " + 1351 "$ARG_API_VERSION_SIGNATURE_FILES, and the name of the current API version. " + 1352 "Required to generate API version JSON.", 1353 "", 1354 "Environment Variables:", 1355 ENV_VAR_METALAVA_DUMP_ARGV, 1356 "Set to true to have metalava emit all the arguments it was invoked with. " + 1357 "Helpful when debugging or reproducing under a debugger what the build system is doing.", 1358 ENV_VAR_METALAVA_PREPEND_ARGS, 1359 "One or more arguments (concatenated by space) to insert into the " + 1360 "command line, before the documentation flags.", 1361 ENV_VAR_METALAVA_APPEND_ARGS, 1362 "One or more arguments (concatenated by space) to append to the " + 1363 "end of the command line, after the generate documentation flags." 1364 ) 1365 1366 val indent = " ".repeat(INDENT_WIDTH) 1367 1368 var i = 0 1369 while (i < args.size) { 1370 val arg = args[i] 1371 if (arg.isEmpty()) { 1372 val groupTitle = args[i + 1] 1373 out.println("\n") 1374 out.println(terminal.colorize(groupTitle, TerminalColor.YELLOW)) 1375 } else { 1376 val description = "\n" + args[i + 1] 1377 val formattedArg = terminal.bold(arg) 1378 val invisibleChars = formattedArg.length - arg.length 1379 // +invisibleChars: the extra chars in the above are counted but don't 1380 // contribute to width so allow more space 1381 val formatString = "%1$-" + (INDENT_WIDTH + invisibleChars) + "s%2\$s" 1382 1383 val output = 1384 wrap( 1385 String.format(formatString, formattedArg, description), 1386 width + invisibleChars, 1387 width, 1388 indent 1389 ) 1390 1391 // Remove trailing whitespace 1392 val lines = output.lines() 1393 lines.forEachIndexed { index, line -> 1394 out.print(line.trimEnd()) 1395 if (index < lines.size - 1) { 1396 out.println() 1397 } 1398 } 1399 } 1400 i += 2 1401 } 1402 } 1403 } 1404