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.sdklib.SdkVersionInfo 21 import com.android.tools.metalava.CompatibilityCheck.CheckRequest 22 import com.android.tools.metalava.doclava1.Issues 23 import com.android.tools.metalava.model.defaultConfiguration 24 import com.android.utils.SdkUtils.wrap 25 import com.google.common.base.CharMatcher 26 import com.google.common.base.Splitter 27 import com.google.common.io.Files 28 import com.intellij.pom.java.LanguageLevel 29 import java.io.File 30 import java.io.IOException 31 import java.io.OutputStreamWriter 32 import java.io.PrintWriter 33 import java.io.StringWriter 34 import java.lang.NumberFormatException 35 import java.util.Locale 36 import kotlin.reflect.KMutableProperty1 37 import kotlin.reflect.full.memberProperties 38 import kotlin.text.Charsets.UTF_8 39 40 /** Global options for the metadata extraction tool */ 41 var options = Options(emptyArray()) 42 43 private const val MAX_LINE_WIDTH = 120 44 private const val INDENT_WIDTH = 45 45 46 const val ARG_COMPAT_OUTPUT = "--compatible-output" 47 const val ARG_FORMAT = "--format" 48 const val ARG_HELP = "--help" 49 const val ARG_VERSION = "--version" 50 const val ARG_QUIET = "--quiet" 51 const val ARG_VERBOSE = "--verbose" 52 const val ARG_CLASS_PATH = "--classpath" 53 const val ARG_SOURCE_PATH = "--source-path" 54 const val ARG_SOURCE_FILES = "--source-files" 55 const val ARG_API = "--api" 56 const val ARG_XML_API = "--api-xml" 57 const val ARG_CONVERT_TO_JDIFF = "--convert-to-jdiff" 58 const val ARG_CONVERT_NEW_TO_JDIFF = "--convert-new-to-jdiff" 59 const val ARG_CONVERT_TO_V1 = "--convert-to-v1" 60 const val ARG_CONVERT_TO_V2 = "--convert-to-v2" 61 const val ARG_CONVERT_NEW_TO_V1 = "--convert-new-to-v1" 62 const val ARG_CONVERT_NEW_TO_V2 = "--convert-new-to-v2" 63 const val ARG_PRIVATE_API = "--private-api" 64 const val ARG_DEX_API = "--dex-api" 65 const val ARG_PRIVATE_DEX_API = "--private-dex-api" 66 const val ARG_SDK_VALUES = "--sdk-values" 67 const val ARG_REMOVED_API = "--removed-api" 68 const val ARG_REMOVED_DEX_API = "--removed-dex-api" 69 const val ARG_MERGE_QUALIFIER_ANNOTATIONS = "--merge-qualifier-annotations" 70 const val ARG_MERGE_INCLUSION_ANNOTATIONS = "--merge-inclusion-annotations" 71 const val ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS = "--validate-nullability-from-merged-stubs" 72 const val ARG_VALIDATE_NULLABILITY_FROM_LIST = "--validate-nullability-from-list" 73 const val ARG_NULLABILITY_WARNINGS_TXT = "--nullability-warnings-txt" 74 const val ARG_NULLABILITY_ERRORS_NON_FATAL = "--nullability-errors-non-fatal" 75 const val ARG_INPUT_API_JAR = "--input-api-jar" 76 const val ARG_EXACT_API = "--exact-api" 77 const val ARG_STUBS = "--stubs" 78 const val ARG_DOC_STUBS = "--doc-stubs" 79 const val ARG_STUBS_SOURCE_LIST = "--write-stubs-source-list" 80 const val ARG_DOC_STUBS_SOURCE_LIST = "--write-doc-stubs-source-list" 81 const val ARG_PROGUARD = "--proguard" 82 const val ARG_EXTRACT_ANNOTATIONS = "--extract-annotations" 83 const val ARG_EXCLUDE_ANNOTATIONS = "--exclude-annotations" 84 const val ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS = "--exclude-documentation-from-stubs" 85 const val ARG_HIDE_PACKAGE = "--hide-package" 86 const val ARG_MANIFEST = "--manifest" 87 const val ARG_MIGRATE_NULLNESS = "--migrate-nullness" 88 const val ARG_CHECK_COMPATIBILITY = "--check-compatibility" 89 const val ARG_CHECK_COMPATIBILITY_API_CURRENT = "--check-compatibility:api:current" 90 const val ARG_CHECK_COMPATIBILITY_API_RELEASED = "--check-compatibility:api:released" 91 const val ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT = "--check-compatibility:removed:current" 92 const val ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED = "--check-compatibility:removed:released" 93 const val ARG_ALLOW_COMPATIBLE_DIFFERENCES = "--allow-compatible-differences" 94 const val ARG_NO_NATIVE_DIFF = "--no-native-diff" 95 const val ARG_INPUT_KOTLIN_NULLS = "--input-kotlin-nulls" 96 const val ARG_OUTPUT_KOTLIN_NULLS = "--output-kotlin-nulls" 97 const val ARG_OUTPUT_DEFAULT_VALUES = "--output-default-values" 98 const val ARG_ANNOTATION_COVERAGE_STATS = "--annotation-coverage-stats" 99 const val ARG_ANNOTATION_COVERAGE_OF = "--annotation-coverage-of" 100 const val ARG_WRITE_CLASS_COVERAGE_TO = "--write-class-coverage-to" 101 const val ARG_WRITE_MEMBER_COVERAGE_TO = "--write-member-coverage-to" 102 const val ARG_WARNINGS_AS_ERRORS = "--warnings-as-errors" 103 const val ARG_LINTS_AS_ERRORS = "--lints-as-errors" 104 const val ARG_SHOW_ANNOTATION = "--show-annotation" 105 const val ARG_SHOW_SINGLE_ANNOTATION = "--show-single-annotation" 106 const val ARG_HIDE_ANNOTATION = "--hide-annotation" 107 const val ARG_HIDE_META_ANNOTATION = "--hide-meta-annotation" 108 const val ARG_SHOW_UNANNOTATED = "--show-unannotated" 109 const val ARG_COLOR = "--color" 110 const val ARG_NO_COLOR = "--no-color" 111 const val ARG_OMIT_COMMON_PACKAGES = "--omit-common-packages" 112 const val ARG_SKIP_JAVA_IN_COVERAGE_REPORT = "--skip-java-in-coverage-report" 113 const val ARG_NO_BANNER = "--no-banner" 114 const val ARG_ERROR = "--error" 115 const val ARG_WARNING = "--warning" 116 const val ARG_LINT = "--lint" 117 const val ARG_HIDE = "--hide" 118 const val ARG_UNHIDE_CLASSPATH_CLASSES = "--unhide-classpath-classes" 119 const val ARG_ALLOW_REFERENCING_UNKNOWN_CLASSES = "--allow-referencing-unknown-classes" 120 const val ARG_NO_UNKNOWN_CLASSES = "--no-unknown-classes" 121 const val ARG_APPLY_API_LEVELS = "--apply-api-levels" 122 const val ARG_GENERATE_API_LEVELS = "--generate-api-levels" 123 const val ARG_ANDROID_JAR_PATTERN = "--android-jar-pattern" 124 const val ARG_CURRENT_VERSION = "--current-version" 125 const val ARG_CURRENT_CODENAME = "--current-codename" 126 const val ARG_CURRENT_JAR = "--current-jar" 127 const val ARG_CHECK_KOTLIN_INTEROP = "--check-kotlin-interop" 128 const val ARG_API_LINT = "--api-lint" 129 const val ARG_API_LINT_IGNORE_PREFIX = "--api-lint-ignore-prefix" 130 const val ARG_PUBLIC = "--public" 131 const val ARG_PROTECTED = "--protected" 132 const val ARG_PACKAGE = "--package" 133 const val ARG_PRIVATE = "--private" 134 const val ARG_HIDDEN = "--hidden" 135 const val ARG_NO_DOCS = "--no-docs" 136 const val ARG_JAVA_SOURCE = "--java-source" 137 const val ARG_REGISTER_ARTIFACT = "--register-artifact" 138 const val ARG_INCLUDE_ANNOTATIONS = "--include-annotations" 139 const val ARG_COPY_ANNOTATIONS = "--copy-annotations" 140 const val ARG_INCLUDE_ANNOTATION_CLASSES = "--include-annotation-classes" 141 const val ARG_REWRITE_ANNOTATIONS = "--rewrite-annotations" 142 const val ARG_INCLUDE_SOURCE_RETENTION = "--include-source-retention" 143 const val ARG_PASS_THROUGH_ANNOTATION = "--pass-through-annotation" 144 const val ARG_INCLUDE_SIG_VERSION = "--include-signature-version" 145 const val ARG_UPDATE_API = "--only-update-api" 146 const val ARG_CHECK_API = "--only-check-api" 147 const val ARG_PASS_BASELINE_UPDATES = "--pass-baseline-updates" 148 const val ARG_DEX_API_MAPPING = "--dex-api-mapping" 149 const val ARG_GENERATE_DOCUMENTATION = "--generate-documentation" 150 const val ARG_REPLACE_DOCUMENTATION = "--replace-documentation" 151 const val ARG_BASELINE = "--baseline" 152 const val ARG_BASELINE_API_LINT = "--baseline:api-lint" 153 const val ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED = "--baseline:compatibility:released" 154 const val ARG_REPORT_EVEN_IF_SUPPRESSED = "--report-even-if-suppressed" 155 const val ARG_UPDATE_BASELINE = "--update-baseline" 156 const val ARG_UPDATE_BASELINE_API_LINT = "--update-baseline:api-lint" 157 const val ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED = "--update-baseline:compatibility:released" 158 const val ARG_MERGE_BASELINE = "--merge-baseline" 159 const val ARG_STUB_PACKAGES = "--stub-packages" 160 const val ARG_STUB_IMPORT_PACKAGES = "--stub-import-packages" 161 const val ARG_DELETE_EMPTY_BASELINES = "--delete-empty-baselines" 162 const val ARG_SUBTRACT_API = "--subtract-api" 163 const val ARG_TYPEDEFS_IN_SIGNATURES = "--typedefs-in-signatures" 164 const val ARG_FORCE_CONVERT_TO_WARNING_NULLABILITY_ANNOTATIONS = "--force-convert-to-warning-nullability-annotations" 165 const val ARG_IGNORE_CLASSES_ON_CLASSPATH = "--ignore-classes-on-classpath" 166 const val ARG_ERROR_MESSAGE_API_LINT = "--error-message:api-lint" 167 const val ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_RELEASED = "--error-message:compatibility:released" 168 const val ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_CURRENT = "--error-message:compatibility:current" 169 const val ARG_NO_IMPLICIT_ROOT = "--no-implicit-root" 170 const val ARG_STRICT_INPUT_FILES = "--strict-input-files" 171 const val ARG_STRICT_INPUT_FILES_STACK = "--strict-input-files:stack" 172 const val ARG_STRICT_INPUT_FILES_WARN = "--strict-input-files:warn" 173 const val ARG_STRICT_INPUT_FILES_EXEMPT = "--strict-input-files-exempt" 174 175 class Options( 176 private val args: Array<String>, 177 /** Writer to direct output to */ 178 var stdout: PrintWriter = PrintWriter(OutputStreamWriter(System.out)), 179 /** Writer to direct error messages to */ 180 var stderr: PrintWriter = PrintWriter(OutputStreamWriter(System.err)) 181 ) { 182 183 /** Internal list backing [sources] */ 184 private val mutableSources: MutableList<File> = mutableListOf() 185 /** Internal list backing [sourcePath] */ 186 private val mutableSourcePath: MutableList<File> = mutableListOf() 187 /** Internal list backing [classpath] */ 188 private val mutableClassPath: MutableList<File> = mutableListOf() 189 /** Internal list backing [showAnnotations] */ 190 private val mutableShowAnnotations = MutableAnnotationFilter() 191 /** Internal list backing [showSingleAnnotations] */ 192 private val mutableShowSingleAnnotations = MutableAnnotationFilter() 193 /** Internal list backing [hideAnnotations] */ 194 private val mutableHideAnnotations = MutableAnnotationFilter() 195 /** Internal list backing [hideMetaAnnotations] */ 196 private val mutableHideMetaAnnotations: MutableList<String> = mutableListOf() 197 /** Internal list backing [stubImportPackages] */ 198 private val mutableStubImportPackages: MutableSet<String> = mutableSetOf() 199 /** Internal list backing [mergeQualifierAnnotations] */ 200 private val mutableMergeQualifierAnnotations: MutableList<File> = mutableListOf() 201 /** Internal list backing [mergeInclusionAnnotations] */ 202 private val mutableMergeInclusionAnnotations: MutableList<File> = mutableListOf() 203 /** Internal list backing [annotationCoverageOf] */ 204 private val mutableAnnotationCoverageOf: MutableList<File> = mutableListOf() 205 /** Internal list backing [hidePackages] */ 206 private val mutableHidePackages: MutableList<String> = mutableListOf() 207 /** Internal list backing [skipEmitPackages] */ 208 private val mutableSkipEmitPackages: MutableList<String> = mutableListOf() 209 /** Internal list backing [convertToXmlFiles] */ 210 private val mutableConvertToXmlFiles: MutableList<ConvertFile> = mutableListOf() 211 /** Internal list backing [passThroughAnnotations] */ 212 private val mutablePassThroughAnnotations: MutableSet<String> = mutableSetOf() 213 214 /** Ignored flags we've already warned about - store here such that we don't keep reporting them */ 215 private val alreadyWarned: MutableSet<String> = mutableSetOf() 216 217 /** 218 * Set of arguments to invoke documentation generation tool (arg 0) with, unless --no-docs is also 219 * supplied 220 */ 221 var invokeDocumentationToolArguments: Array<String> = emptyArray() 222 223 /** 224 * Whether to suppress documentation generation, even if a documentation generator has 225 * been configured via ${#ARG_GENERATE_DOCUMENTATION} 226 */ 227 var noDocs = false 228 229 /** API to subtract from signature and stub generation. Corresponds to [ARG_SUBTRACT_API]. */ 230 var subtractApi: File? = null 231 232 /** 233 * Validator for nullability annotations, if validation is enabled. 234 */ 235 var nullabilityAnnotationsValidator: NullabilityAnnotationsValidator? = null 236 237 /** 238 * Whether nullability validation errors should be considered fatal. 239 */ 240 var nullabilityErrorsFatal = true 241 242 /** 243 * A file to write non-fatal nullability validation issues to. If null, all issues are treated 244 * as fatal or else logged as warnings, depending on the value of [nullabilityErrorsFatal]. 245 */ 246 var nullabilityWarningsTxt: File? = null 247 248 /** 249 * Whether to validate nullability for all the classes where we are merging annotations from 250 * external java stub files. If true, [nullabilityAnnotationsValidator] must be set. 251 */ 252 var validateNullabilityFromMergedStubs = false 253 254 /** 255 * A file containing a list of classes whose nullability annotations should be validated. If 256 * set, [nullabilityAnnotationsValidator] must also be set. 257 */ 258 var validateNullabilityFromList: File? = null 259 260 /** 261 * Whether to include element documentation (javadoc and KDoc) is in the generated stubs. 262 * (Copyright notices are not affected by this, they are always included. Documentation stubs 263 * (--doc-stubs) are not affected.) 264 */ 265 var includeDocumentationInStubs = true 266 267 /** 268 * Whether metalava is invoked as part of updating the API files. When this is true, metalava 269 * should *cancel* various other flags that are also being passed in, such as --check-compatibility. 270 * This is there to ease integration in the build system: for a given target, the build system will 271 * pass all the applicable flags (--stubs, --api, --check-compatibility, --generate-documentation, etc), 272 * and this integration is re-used for the update-api facility where we *only* want to generate the 273 * signature files. This avoids having duplicate metalava invocation logic where potentially newly 274 * added flags are missing in one of the invocations etc. 275 */ 276 var onlyUpdateApi = false 277 278 /** 279 * Whether metalava is invoked as part of running the checkapi target. When this is true, metalava 280 * should *cancel* various other flags that are also being passed in, such as updating signature 281 * files. 282 * 283 * This is there to ease integration in the build system: for a given target, the build system will 284 * pass all the applicable flags (--stubs, --api, --check-compatibility, --generate-documentation, etc), 285 * and this integration is re-used for the checkapi facility where we *only* want to run compatibility 286 * checks. This avoids having duplicate metalava invocation logic where potentially newly 287 * added flags are missing in one of the invocations etc. 288 */ 289 var onlyCheckApi = false 290 291 /** 292 * Whether signature files should emit in "compat" mode, preserving the various 293 * quirks of the previous signature file format -- this will for example use a non-standard 294 * modifier ordering, it will call enums interfaces, etc. See the [Compatibility] class 295 * for more fine grained control (which is not (currently) exposed as individual command line 296 * flags. 297 */ 298 var compatOutput = useCompatMode(args) 299 300 /** Whether nullness annotations should be displayed as ?/!/empty instead of with @NonNull/@Nullable. */ 301 var outputKotlinStyleNulls = false // requires v3 302 303 /** Whether default values should be included in signature files */ 304 var outputDefaultValues = !compatOutput 305 306 /** The output format version being used */ 307 var outputFormat: FileFormat = if (compatOutput) FileFormat.V1 else FileFormat.V2 308 309 /** 310 * Whether reading signature files should assume the input is formatted as Kotlin-style nulls 311 * (e.g. ? means nullable, ! means unknown, empty means not null). 312 * 313 * Even when it's false, if the format supports Kotlin-style nulls, we'll still allow them. 314 */ 315 var inputKotlinStyleNulls: Boolean = false 316 317 /** If true, treat all warnings as errors */ 318 var warningsAreErrors: Boolean = false 319 320 /** If true, treat all API lint warnings as errors */ 321 var lintsAreErrors: Boolean = false 322 323 /** The list of source roots */ 324 val sourcePath: List<File> = mutableSourcePath 325 326 /** The list of dependency jars */ 327 val classpath: List<File> = mutableClassPath 328 329 /** All source files to parse */ 330 var sources: List<File> = mutableSources 331 332 /** Whether to include APIs with annotations (intended for documentation purposes) */ 333 var showAnnotations: AnnotationFilter = mutableShowAnnotations 334 335 /** 336 * Like [showAnnotations], but does not work recursively. Note that 337 * these annotations are *also* show annotations and will be added to the above list; 338 * this is a subset. 339 */ 340 val showSingleAnnotations: AnnotationFilter = mutableShowSingleAnnotations 341 342 /** 343 * Whether to include unannotated elements if {@link #showAnnotations} is set. 344 * Note: This only applies to signature files, not stub files. 345 */ 346 var showUnannotated = false 347 348 /** Whether to validate the API for best practices */ 349 var checkApi = false 350 351 val checkApiIgnorePrefix: MutableList<String> = mutableListOf() 352 353 /** If non null, an API file to use to hide for controlling what parts of the API are new */ 354 var checkApiBaselineApiFile: File? = null 355 356 /** Whether to validate the API for Kotlin interop */ 357 var checkKotlinInterop = false 358 359 /** Packages to include (if null, include all) */ 360 var stubPackages: PackageFilter? = null 361 362 /** Packages to import (if empty, include all) */ 363 var stubImportPackages: Set<String> = mutableStubImportPackages 364 365 /** Packages to exclude/hide */ 366 var hidePackages: List<String> = mutableHidePackages 367 368 /** Packages that we should skip generating even if not hidden; typically only used by tests */ 369 var skipEmitPackages: List<String> = mutableSkipEmitPackages 370 371 var showAnnotationOverridesVisibility: Boolean = false 372 373 /** Annotations to hide */ 374 var hideAnnotations: AnnotationFilter = mutableHideAnnotations 375 376 /** Meta-annotations to hide */ 377 var hideMetaAnnotations = mutableHideMetaAnnotations 378 379 /** Whether the generated API can contain classes that are not present in the source but are present on the 380 * classpath. Defaults to true for backwards compatibility but is set to false if any API signatures are imported 381 * as they must provide a complete set of all classes required but not provided by the generated API. 382 * 383 * Once all APIs are either self contained or imported all the required references this will be removed and no 384 * classes will be allowed from the classpath JARs. */ 385 var allowClassesFromClasspath = true 386 387 /** Whether to report warnings and other diagnostics along the way */ 388 var quiet = false 389 390 /** Whether to report extra diagnostics along the way (note that verbose isn't the same as not quiet) */ 391 var verbose = false 392 393 /** If set, a directory to write stub files to. Corresponds to the --stubs/-stubs flag. */ 394 var stubsDir: File? = null 395 396 /** If set, a directory to write documentation stub files to. Corresponds to the --stubs/-stubs flag. */ 397 var docStubsDir: File? = null 398 399 /** If set, a source file to write the stub index (list of source files) to. Can be passed to 400 * other tools like javac/javadoc using the special @-syntax. */ 401 var stubsSourceList: File? = null 402 403 /** If set, a source file to write the doc stub index (list of source files) to. Can be passed to 404 * other tools like javac/javadoc using the special @-syntax. */ 405 var docStubsSourceList: File? = null 406 407 /** Proguard Keep list file to write */ 408 var proguard: File? = null 409 410 /** If set, a file to write an API file to. Corresponds to the --api/-api flag. */ 411 var apiFile: File? = null 412 413 /** Like [apiFile], but with JDiff xml format. */ 414 var apiXmlFile: File? = null 415 416 /** If set, a file to write the private API file to. Corresponds to the --private-api/-privateApi flag. */ 417 var privateApiFile: File? = null 418 419 /** If set, a file to write the DEX signatures to. Corresponds to [ARG_DEX_API]. */ 420 var dexApiFile: File? = null 421 422 /** If set, a file to write all DEX signatures and file locations to. Corresponds to [ARG_DEX_API_MAPPING]. */ 423 var dexApiMappingFile: File? = null 424 425 /** If set, a file to write the private DEX signatures to. Corresponds to --private-dex-api. */ 426 var privateDexApiFile: File? = null 427 428 /** Path to directory to write SDK values to */ 429 var sdkValueDir: File? = null 430 431 /** If set, a file to write extracted annotations to. Corresponds to the --extract-annotations flag. */ 432 var externalAnnotations: File? = null 433 434 /** For [ARG_COPY_ANNOTATIONS], the source directory to read stub annotations from */ 435 var privateAnnotationsSource: File? = null 436 437 /** For [ARG_COPY_ANNOTATIONS], the target directory to write converted stub annotations from */ 438 var privateAnnotationsTarget: File? = null 439 440 /** 441 * For [ARG_INCLUDE_ANNOTATION_CLASSES], the directory to copy stub annotation source files into the 442 * stubs folder from 443 */ 444 var copyStubAnnotationsFrom: File? = null 445 446 /** 447 * For [ARG_INCLUDE_SOURCE_RETENTION], true if we want to include source-retention annotations 448 * both in the set of files emitted by [ARG_INCLUDE_ANNOTATION_CLASSES] and into the stubs 449 * themselves 450 */ 451 var includeSourceRetentionAnnotations = false 452 453 /** For [ARG_REWRITE_ANNOTATIONS], the jar or bytecode folder to rewrite annotations in */ 454 var rewriteAnnotations: List<File>? = null 455 456 /** A manifest file to read to for example look up available permissions */ 457 var manifest: File? = null 458 459 /** If set, a file to write a dex API file to. Corresponds to the --removed-dex-api/-removedDexApi flag. */ 460 var removedApiFile: File? = null 461 462 /** If set, a file to write an API file to. Corresponds to the --removed-api/-removedApi flag. */ 463 var removedDexApiFile: File? = null 464 465 /** Whether output should be colorized */ 466 var color = System.getenv("TERM")?.startsWith("xterm") ?: System.getenv("COLORTERM") != null ?: false 467 468 /** Whether to omit Java and Kotlin runtime library packages from annotation coverage stats */ 469 var omitRuntimePackageStats = false 470 471 /** Whether to generate annotations into the stubs */ 472 var generateAnnotations = false 473 474 /** The set of annotation classes that should be passed through unchanged */ 475 var passThroughAnnotations = mutablePassThroughAnnotations 476 477 /** 478 * A signature file to migrate nullness data from 479 */ 480 var migrateNullsFrom: File? = null 481 482 /** Private backing list for [compatibilityChecks]] */ 483 private val mutableCompatibilityChecks: MutableList<CheckRequest> = mutableListOf() 484 485 /** The list of compatibility checks to run */ 486 val compatibilityChecks: List<CheckRequest> = mutableCompatibilityChecks 487 488 /** 489 * When checking signature files, whether compatible differences in signature 490 * files are allowed. This is normally not allowed (since it means the next 491 * engineer adding an incompatible change will suddenly see the cumulative 492 * differences show up in their diffs when checking in signature files), 493 * but is useful from the test suite etc. Controlled by 494 * [ARG_ALLOW_COMPATIBLE_DIFFERENCES]. 495 */ 496 var allowCompatibleDifferences = false 497 498 /** If false, attempt to use the native diff utility on the system */ 499 var noNativeDiff = false 500 501 /** Existing external annotation files to merge in */ 502 var mergeQualifierAnnotations: List<File> = mutableMergeQualifierAnnotations 503 var mergeInclusionAnnotations: List<File> = mutableMergeInclusionAnnotations 504 505 /** 506 * We modify the annotations on these APIs to ask kotlinc to treat it as only a warning 507 * if a caller of one of these APIs makes an incorrect assumption about its nullability. 508 */ 509 var forceConvertToWarningNullabilityAnnotations: PackageFilter? = null 510 511 /** Set of jars and class files for existing apps that we want to measure coverage of */ 512 var annotationCoverageOf: List<File> = mutableAnnotationCoverageOf 513 514 /** File to write the annotation class coverage report to, if any */ 515 var annotationCoverageClassReport: File? = null 516 517 /** File to write the annotation member coverage report to, if any */ 518 var annotationCoverageMemberReport: File? = null 519 520 /** An optional <b>jar</b> file to load classes from instead of from source. 521 * This is similar to the [classpath] attribute except we're explicitly saying 522 * that this is the complete set of classes and that we <b>should</b> generate 523 * signatures/stubs from them or use them to diff APIs with (whereas [classpath] 524 * is only used to resolve types.) */ 525 var apiJar: File? = null 526 527 /** Whether to emit coverage statistics for annotations in the API surface */ 528 var dumpAnnotationStatistics = false 529 530 /** Only used for tests: Normally we want to treat classes not found as source (e.g. supplied via 531 * classpath) as hidden, but for the unit tests (where we're not passing in 532 * a complete API surface) this makes the tests more cumbersome. 533 * This option lets the testing infrastructure treat these classes differently. 534 * To see the what this means in practice, try turning it back on for the tests 535 * and see what it does to the results :) 536 */ 537 var hideClasspathClasses = true 538 539 /** Only used for tests: Whether during code filtering we allow referencing super classes 540 * etc that are unknown (because they're not included in the codebase) */ 541 var allowReferencingUnknownClasses = true 542 543 /** Reverse of [allowReferencingUnknownClasses]: Require all classes to be known. This 544 * is used when compiling the main SDK itself (which includes definitions for everything, 545 * including java.lang.Object.) */ 546 var noUnknownClasses = false 547 548 /** 549 * mapping from API level to android.jar files, if computing API levels 550 */ 551 var apiLevelJars: Array<File>? = null 552 553 /** The api level of the codebase, or -1 if not known/specified */ 554 var currentApiLevel = -1 555 556 /** The codename of the codebase, if it's a preview, or null if not specified */ 557 var currentCodeName: String? = null 558 559 /** API level XML file to generate */ 560 var generateApiLevelXml: File? = null 561 562 /** Reads API XML file to apply into documentation */ 563 var applyApiLevelsXml: File? = null 564 565 /** Level to include for javadoc */ 566 var docLevel = DocLevel.PROTECTED 567 568 /** Whether to include the signature file format version header in signature files */ 569 var includeSignatureFormatVersion: Boolean = !compatOutput 570 571 /** A baseline to check against */ 572 var baseline: Baseline? = null 573 574 /** A baseline to check against, specifically used for "API lint" (i.e. [ARG_API_LINT]) */ 575 var baselineApiLint: Baseline? = null 576 577 /** 578 * A baseline to check against, specifically used for "check-compatibility:*:released" 579 * (i.e. [ARG_CHECK_COMPATIBILITY_API_RELEASEED] and [ARG_CHECK_COMPATIBILITY_REMOVED_RELEASEED]) 580 */ 581 var baselineCompatibilityReleased: Baseline? = null 582 583 var allBaselines: List<Baseline> 584 585 /** If set, metalava will show this error message when "API lint" (i.e. [ARG_API_LINT]) fails. */ 586 var errorMessageApiLint: String? = null 587 588 /** 589 * If set, metalava will show this error message when "check-compatibility:*:released" fails. 590 * (i.e. [ARG_CHECK_COMPATIBILITY_API_RELEASEED] and [ARG_CHECK_COMPATIBILITY_REMOVED_RELEASEED]) 591 */ 592 var errorMessageCompatibilityReleased: String? = null 593 594 /** 595 * If set, metalava will show this error message when "check-compatibility:*:current" fails. 596 * (i.e. [ARG_CHECK_COMPATIBILITY_API_CURRENT] and [ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT]) 597 */ 598 var errorMessageCompatibilityCurrent: String? = null 599 600 /** [Reporter] for "api-lint" */ 601 var reporterApiLint: Reporter 602 603 /** 604 * [Reporter] for "check-compatibility:*:released". 605 * (i.e. [ARG_CHECK_COMPATIBILITY_API_RELEASEED] and [ARG_CHECK_COMPATIBILITY_REMOVED_RELEASEED]) 606 */ 607 var reporterCompatibilityReleased: Reporter 608 609 /** 610 * [Reporter] for "check-compatibility:*:current". 611 * (i.e. [ARG_CHECK_COMPATIBILITY_API_CURRENT] and [ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT]) 612 */ 613 var reporterCompatibilityCurrent: Reporter 614 615 var allReporters: List<Reporter> 616 617 /** If updating baselines, don't fail the build */ 618 var passBaselineUpdates = false 619 620 /** If updating baselines and the baseline is empty, delete the file */ 621 var deleteEmptyBaselines = false 622 623 /** Whether the baseline should only contain errors */ 624 var baselineErrorsOnly = false 625 626 /** Writes a list of all errors, even if they were suppressed in baseline or via annotation. */ 627 var reportEvenIfSuppressed: File? = null 628 var reportEvenIfSuppressedWriter: PrintWriter? = null 629 630 /** 631 * DocReplacements to apply to the documentation. 632 */ 633 var docReplacements = mutableListOf<DocReplacement>() 634 635 /** 636 * Whether to omit locations for warnings and errors. This is not a flag exposed to users 637 * or listed in help; this is intended for the unit test suite, used for example for the 638 * test which checks compatibility between signature and API files where the paths vary. 639 */ 640 var omitLocations = false 641 642 /** Directory to write signature files to, if any. */ 643 var androidJarSignatureFiles: File? = null 644 645 /** 646 * The language level to use for Java files, set with [ARG_JAVA_SOURCE] 647 */ 648 var javaLanguageLevel: LanguageLevel = LanguageLevel.JDK_1_8 649 650 /** Map from XML API descriptor file to corresponding artifact id name */ 651 val artifactRegistrations = ArtifactTagger() 652 653 /** List of signature files to export as JDiff files */ 654 val convertToXmlFiles: List<ConvertFile> = mutableConvertToXmlFiles 655 656 enum class TypedefMode { 657 NONE, 658 REFERENCE, 659 INLINE 660 } 661 662 /** How to handle typedef annotations in signature files; corresponds to $ARG_TYPEDEFS_IN_SIGNATURES */ 663 var typedefMode = TypedefMode.NONE 664 665 /** Allow implicit root detection (which is the default behavior). See [ARG_NO_IMPLICIT_ROOT] */ 666 var allowImplicitRoot = true 667 668 enum class StrictInputFileMode { 669 PERMISSIVE, 670 STRICT, 671 STRICT_WARN, 672 STRICT_WITH_STACK; 673 674 companion object { 675 fun fromArgument(arg: String): StrictInputFileMode { 676 return when (arg) { 677 ARG_STRICT_INPUT_FILES -> STRICT 678 ARG_STRICT_INPUT_FILES_WARN -> STRICT_WARN 679 ARG_STRICT_INPUT_FILES_STACK -> STRICT_WITH_STACK 680 else -> PERMISSIVE 681 } 682 } 683 } 684 } 685 686 /** 687 * Whether we should allow metalava to read files that are not explicitly specified in the 688 * command line. See [ARG_STRICT_INPUT_FILES], [ARG_STRICT_INPUT_FILES_WARN] and 689 * [ARG_STRICT_INPUT_FILES_STACK]. 690 */ 691 var strictInputFiles = StrictInputFileMode.PERMISSIVE 692 693 var strictInputViolationsFile: File? = null 694 var strictInputViolationsPrintWriter: PrintWriter? = null 695 696 /** File conversion tasks */ 697 data class ConvertFile( 698 val fromApiFile: File, 699 val outputFile: File, 700 val baseApiFile: File? = null, 701 val strip: Boolean = false, 702 val outputFormat: FileFormat = FileFormat.JDIFF 703 ) 704 705 /** Temporary folder to use instead of the JDK default, if any */ 706 var tempFolder: File? = null 707 708 init { 709 // Pre-check whether --color/--no-color is present and use that to decide how 710 // to emit the banner even before we emit errors 711 if (args.contains(ARG_NO_COLOR)) { 712 color = false 713 } else if (args.contains(ARG_COLOR) || args.contains("-android")) { 714 color = true 715 } 716 // empty args: only when building initial default Options (options field 717 // at the top of this file; replaced once the driver runs and passes in 718 // a real argv. Don't print a banner when initializing the default options.) 719 if (args.isNotEmpty() && !args.contains(ARG_QUIET) && !args.contains(ARG_NO_BANNER) && 720 !args.contains(ARG_VERSION) 721 ) { 722 if (color) { 723 stdout.print(colorized(BANNER.trimIndent(), TerminalColor.BLUE)) 724 } else { 725 stdout.println(BANNER.trimIndent()) 726 } 727 stdout.println() 728 stdout.flush() 729 } 730 731 var androidJarPatterns: MutableList<String>? = null 732 var currentJar: File? = null 733 var delayedCheckApiFiles = false 734 var skipGenerateAnnotations = false 735 reporter = Reporter(null, null) 736 737 var baselineBuilder = Baseline.Builder().apply { description = "base" } 738 var baselineApiLintBuilder = Baseline.Builder().apply { description = "api-lint" } 739 var baselineCompatibilityReleasedBuilder = Baseline.Builder().apply { description = "compatibility:released" } 740 741 fun getBaselineBuilderForArg(flag: String): Baseline.Builder = when (flag) { 742 ARG_BASELINE, ARG_UPDATE_BASELINE, ARG_MERGE_BASELINE -> baselineBuilder 743 ARG_BASELINE_API_LINT, ARG_UPDATE_BASELINE_API_LINT -> baselineApiLintBuilder 744 ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED, ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED 745 -> baselineCompatibilityReleasedBuilder 746 else -> error("Internal error: Invalid flag: $flag") 747 } 748 749 var index = 0 750 while (index < args.size) { 751 val arg = args[index] 752 753 when (arg) { 754 ARG_HELP, "-h", "-?" -> { 755 helpAndQuit(color) 756 } 757 758 ARG_QUIET -> { 759 quiet = true; verbose = false 760 } 761 762 ARG_VERBOSE -> { 763 verbose = true; quiet = false 764 } 765 766 ARG_VERSION -> { 767 throw DriverException(stdout = "$PROGRAM_NAME version: ${Version.VERSION}") 768 } 769 770 ARG_COMPAT_OUTPUT -> compatOutput = true 771 772 // For now we don't distinguish between bootclasspath and classpath 773 ARG_CLASS_PATH, "-classpath", "-bootclasspath" -> { 774 val path = getValue(args, ++index) 775 mutableClassPath.addAll(stringToExistingDirsOrJars(path)) 776 } 777 778 ARG_SOURCE_PATH, "--sources", "--sourcepath", "-sourcepath" -> { 779 val path = getValue(args, ++index) 780 if (path.isBlank()) { 781 // Don't compute absolute path; we want to skip this file later on. 782 // For current directory one should use ".", not "". 783 mutableSourcePath.add(File("")) 784 } else { 785 if (path.endsWith(SdkConstants.DOT_JAVA)) { 786 throw DriverException( 787 "$arg should point to a source root directory, not a source file ($path)" 788 ) 789 } 790 mutableSourcePath.addAll(stringToExistingDirsOrJars(path, false)) 791 } 792 } 793 794 ARG_SOURCE_FILES -> { 795 val listString = getValue(args, ++index) 796 listString.split(",").forEach { path -> 797 mutableSources.addAll(stringToExistingFiles(path)) 798 } 799 } 800 801 ARG_SUBTRACT_API -> { 802 if (subtractApi != null) { 803 throw DriverException(stderr = "Only one $ARG_SUBTRACT_API can be supplied") 804 } 805 subtractApi = stringToExistingFile(getValue(args, ++index)) 806 } 807 808 // TODO: Remove the legacy --merge-annotations flag once it's no longer used to update P docs 809 ARG_MERGE_QUALIFIER_ANNOTATIONS, "--merge-zips", "--merge-annotations" -> mutableMergeQualifierAnnotations.addAll( 810 stringToExistingDirsOrFiles( 811 getValue(args, ++index) 812 ) 813 ) 814 815 ARG_MERGE_INCLUSION_ANNOTATIONS -> mutableMergeInclusionAnnotations.addAll( 816 stringToExistingDirsOrFiles( 817 getValue(args, ++index) 818 ) 819 ) 820 821 ARG_FORCE_CONVERT_TO_WARNING_NULLABILITY_ANNOTATIONS -> { 822 val nextArg = getValue(args, ++index) 823 forceConvertToWarningNullabilityAnnotations = PackageFilter.parse(nextArg) 824 } 825 826 ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS -> { 827 validateNullabilityFromMergedStubs = true 828 nullabilityAnnotationsValidator = 829 nullabilityAnnotationsValidator ?: NullabilityAnnotationsValidator() 830 } 831 ARG_VALIDATE_NULLABILITY_FROM_LIST -> { 832 validateNullabilityFromList = stringToExistingFile(getValue(args, ++index)) 833 nullabilityAnnotationsValidator = 834 nullabilityAnnotationsValidator ?: NullabilityAnnotationsValidator() 835 } 836 ARG_NULLABILITY_WARNINGS_TXT -> 837 nullabilityWarningsTxt = stringToNewFile(getValue(args, ++index)) 838 ARG_NULLABILITY_ERRORS_NON_FATAL -> 839 nullabilityErrorsFatal = false 840 841 "-sdkvalues", ARG_SDK_VALUES -> sdkValueDir = stringToNewDir(getValue(args, ++index)) 842 ARG_API, "-api" -> apiFile = stringToNewFile(getValue(args, ++index)) 843 ARG_XML_API -> apiXmlFile = stringToNewFile(getValue(args, ++index)) 844 ARG_DEX_API, "-dexApi" -> dexApiFile = stringToNewFile(getValue(args, ++index)) 845 ARG_DEX_API_MAPPING, "-apiMapping" -> dexApiMappingFile = stringToNewFile(getValue(args, ++index)) 846 847 ARG_PRIVATE_API, "-privateApi" -> privateApiFile = stringToNewFile(getValue(args, ++index)) 848 ARG_PRIVATE_DEX_API, "-privateDexApi" -> privateDexApiFile = stringToNewFile(getValue(args, ++index)) 849 850 ARG_REMOVED_API, "-removedApi" -> removedApiFile = stringToNewFile(getValue(args, ++index)) 851 ARG_REMOVED_DEX_API, "-removedDexApi" -> removedDexApiFile = stringToNewFile(getValue(args, ++index)) 852 853 ARG_EXACT_API, "-exactApi" -> { 854 getValue(args, ++index) // prevent next arg from tripping up parser 855 unimplemented(arg) // Not yet implemented (because it seems to no longer be hooked up in doclava1) 856 } 857 858 ARG_MANIFEST, "-manifest" -> manifest = stringToExistingFile(getValue(args, ++index)) 859 860 ARG_SHOW_ANNOTATION, "-showAnnotation" -> mutableShowAnnotations.add(getValue(args, ++index)) 861 862 ARG_SHOW_SINGLE_ANNOTATION -> { 863 val annotation = getValue(args, ++index) 864 mutableShowSingleAnnotations.add(annotation) 865 // These should also be counted as show annotations 866 mutableShowAnnotations.add(annotation) 867 } 868 869 ARG_SHOW_UNANNOTATED, "-showUnannotated" -> showUnannotated = true 870 871 "--showAnnotationOverridesVisibility" -> { 872 unimplemented(arg) 873 showAnnotationOverridesVisibility = true 874 } 875 876 ARG_HIDE_ANNOTATION, "--hideAnnotations", "-hideAnnotation" -> 877 mutableHideAnnotations.add(getValue(args, ++index)) 878 ARG_HIDE_META_ANNOTATION, "--hideMetaAnnotations", "-hideMetaAnnotation" -> 879 mutableHideMetaAnnotations.add(getValue(args, ++index)) 880 881 ARG_STUBS, "-stubs" -> stubsDir = stringToNewDir(getValue(args, ++index)) 882 ARG_DOC_STUBS -> docStubsDir = stringToNewDir(getValue(args, ++index)) 883 ARG_STUBS_SOURCE_LIST -> stubsSourceList = stringToNewFile(getValue(args, ++index)) 884 ARG_DOC_STUBS_SOURCE_LIST -> docStubsSourceList = stringToNewFile(getValue(args, ++index)) 885 886 ARG_EXCLUDE_ANNOTATIONS -> generateAnnotations = false 887 888 ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS -> includeDocumentationInStubs = false 889 890 // Note that this only affects stub generation, not signature files. 891 // For signature files, clear the compatibility mode 892 // (--annotations-in-signatures) 893 ARG_INCLUDE_ANNOTATIONS -> generateAnnotations = true 894 895 ARG_PASS_THROUGH_ANNOTATION -> { 896 val annotations = getValue(args, ++index) 897 annotations.split(",").forEach { path -> 898 mutablePassThroughAnnotations.add(path) 899 } 900 } 901 902 // Flag used by test suite to avoid including locations in 903 // the output when diffing against golden files 904 "--omit-locations" -> omitLocations = true 905 906 ARG_PROGUARD, "-proguard" -> proguard = stringToNewFile(getValue(args, ++index)) 907 908 ARG_HIDE_PACKAGE, "-hidePackage" -> mutableHidePackages.add(getValue(args, ++index)) 909 910 ARG_STUB_PACKAGES, "-stubpackages" -> { 911 val packages = getValue(args, ++index) 912 val filter = stubPackages ?: run { 913 val newFilter = PackageFilter() 914 stubPackages = newFilter 915 newFilter 916 } 917 filter.addPackages(packages) 918 } 919 920 ARG_STUB_IMPORT_PACKAGES, "-stubimportpackages" -> { 921 val packages = getValue(args, ++index) 922 for (pkg in packages.split(File.pathSeparatorChar)) { 923 mutableStubImportPackages.add(pkg) 924 mutableHidePackages.add(pkg) 925 } 926 } 927 928 "--skip-emit-packages" -> { 929 val packages = getValue(args, ++index) 930 mutableSkipEmitPackages += packages.split(File.pathSeparatorChar) 931 } 932 933 ARG_TYPEDEFS_IN_SIGNATURES -> { 934 val type = getValue(args, ++index) 935 typedefMode = when (type) { 936 "ref" -> TypedefMode.REFERENCE 937 "inline" -> TypedefMode.INLINE 938 "none" -> TypedefMode.NONE 939 else -> throw DriverException( 940 stderr = "$ARG_TYPEDEFS_IN_SIGNATURES must be one of ref, inline, none; was $type") 941 } 942 } 943 944 ARG_IGNORE_CLASSES_ON_CLASSPATH -> { 945 allowClassesFromClasspath = false 946 } 947 948 ARG_BASELINE, ARG_BASELINE_API_LINT, ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED -> { 949 val nextArg = getValue(args, ++index) 950 val builder = getBaselineBuilderForArg(arg) 951 builder.file = stringToExistingFile(nextArg) 952 } 953 954 ARG_REPORT_EVEN_IF_SUPPRESSED -> { 955 val relative = getValue(args, ++index) 956 if (reportEvenIfSuppressed != null) { 957 throw DriverException("Only one $ARG_REPORT_EVEN_IF_SUPPRESSED is allowed; found both $reportEvenIfSuppressed and $relative") 958 } 959 reportEvenIfSuppressed = stringToNewOrExistingFile(relative) 960 reportEvenIfSuppressedWriter = reportEvenIfSuppressed?.printWriter() 961 } 962 963 ARG_MERGE_BASELINE, ARG_UPDATE_BASELINE, ARG_UPDATE_BASELINE_API_LINT, ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED -> { 964 val builder = getBaselineBuilderForArg(arg) 965 builder.merge = (arg == ARG_MERGE_BASELINE) 966 if (index < args.size - 1) { 967 val nextArg = args[index + 1] 968 if (!nextArg.startsWith("-")) { 969 index++ 970 builder.updateFile = stringToNewOrExistingFile(nextArg) 971 } 972 } 973 } 974 975 ARG_ERROR_MESSAGE_API_LINT -> errorMessageApiLint = getValue(args, ++index) 976 ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_RELEASED -> errorMessageCompatibilityReleased = getValue(args, ++index) 977 ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_CURRENT -> errorMessageCompatibilityCurrent = getValue(args, ++index) 978 979 ARG_PASS_BASELINE_UPDATES -> passBaselineUpdates = true 980 ARG_DELETE_EMPTY_BASELINES -> deleteEmptyBaselines = true 981 982 ARG_PUBLIC, "-public" -> docLevel = DocLevel.PUBLIC 983 ARG_PROTECTED, "-protected" -> docLevel = DocLevel.PROTECTED 984 ARG_PACKAGE, "-package" -> docLevel = DocLevel.PACKAGE 985 ARG_PRIVATE, "-private" -> docLevel = DocLevel.PRIVATE 986 ARG_HIDDEN, "-hidden" -> docLevel = DocLevel.HIDDEN 987 988 ARG_INPUT_API_JAR -> apiJar = stringToExistingFile(getValue(args, ++index)) 989 990 ARG_EXTRACT_ANNOTATIONS -> externalAnnotations = stringToNewFile(getValue(args, ++index)) 991 ARG_COPY_ANNOTATIONS -> { 992 privateAnnotationsSource = stringToExistingDir(getValue(args, ++index)) 993 privateAnnotationsTarget = stringToNewDir(getValue(args, ++index)) 994 } 995 ARG_REWRITE_ANNOTATIONS -> rewriteAnnotations = stringToExistingDirsOrJars(getValue(args, ++index)) 996 ARG_INCLUDE_ANNOTATION_CLASSES -> copyStubAnnotationsFrom = stringToExistingDir(getValue(args, ++index)) 997 ARG_INCLUDE_SOURCE_RETENTION -> includeSourceRetentionAnnotations = true 998 999 "--previous-api" -> { 1000 migrateNullsFrom = stringToExistingFile(getValue(args, ++index)) 1001 reporter.report( 1002 Issues.DEPRECATED_OPTION, null as File?, 1003 "--previous-api is deprecated; instead " + 1004 "use $ARG_MIGRATE_NULLNESS $migrateNullsFrom" 1005 ) 1006 } 1007 1008 ARG_MIGRATE_NULLNESS -> { 1009 // See if the next argument specifies the nullness API codebase 1010 if (index < args.size - 1) { 1011 val nextArg = args[index + 1] 1012 if (!nextArg.startsWith("-")) { 1013 val file = stringToExistingFile(nextArg) 1014 if (file.isFile) { 1015 index++ 1016 migrateNullsFrom = file 1017 } 1018 } 1019 } 1020 } 1021 1022 "--current-api" -> { 1023 val file = stringToExistingFile(getValue(args, ++index)) 1024 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.PUBLIC_API, ReleaseType.DEV)) 1025 reporter.report( 1026 Issues.DEPRECATED_OPTION, null as File?, 1027 "--current-api is deprecated; instead " + 1028 "use $ARG_CHECK_COMPATIBILITY_API_CURRENT" 1029 ) 1030 } 1031 1032 ARG_CHECK_COMPATIBILITY -> { 1033 // See if the next argument specifies the compatibility check. 1034 // Synonymous with ARG_CHECK_COMPATIBILITY_API_CURRENT, though 1035 // for backwards compatibility with earlier versions and usages 1036 // can also works in conjunction with ARG_CURRENT_API where the 1037 // usage was to use ARG_CURRENT_API to point to the API file and 1038 // then specify ARG_CHECK_COMPATIBILITY (without an argument) to 1039 // indicate that the current api should also be checked for 1040 // compatibility. 1041 if (index < args.size - 1) { 1042 val nextArg = args[index + 1] 1043 if (!nextArg.startsWith("-")) { 1044 val file = stringToExistingFile(nextArg) 1045 if (file.isFile) { 1046 index++ 1047 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.PUBLIC_API, ReleaseType.DEV)) 1048 } 1049 } 1050 } 1051 } 1052 1053 ARG_CHECK_COMPATIBILITY_API_CURRENT -> { 1054 val file = stringToExistingFile(getValue(args, ++index)) 1055 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.PUBLIC_API, ReleaseType.DEV)) 1056 } 1057 1058 ARG_CHECK_COMPATIBILITY_API_RELEASED -> { 1059 val file = stringToExistingFile(getValue(args, ++index)) 1060 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.PUBLIC_API, ReleaseType.RELEASED)) 1061 } 1062 1063 ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT -> { 1064 val file = stringToExistingFile(getValue(args, ++index)) 1065 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.REMOVED, ReleaseType.DEV)) 1066 } 1067 1068 ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED -> { 1069 val file = stringToExistingFile(getValue(args, ++index)) 1070 mutableCompatibilityChecks.add(CheckRequest(file, ApiType.REMOVED, ReleaseType.RELEASED)) 1071 } 1072 1073 ARG_ALLOW_COMPATIBLE_DIFFERENCES -> allowCompatibleDifferences = true 1074 ARG_NO_NATIVE_DIFF -> noNativeDiff = true 1075 1076 // Compat flag for the old API check command, invoked from build/make/core/definitions.mk: 1077 "--check-api-files" -> { 1078 if (index < args.size - 1 && args[index + 1].startsWith("-")) { 1079 // Work around bug where --check-api-files is invoked with all 1080 // the other metalava args before the 4 files; this will be 1081 // fixed by https://android-review.googlesource.com/c/platform/build/+/874473 1082 delayedCheckApiFiles = true 1083 } else { 1084 val stableApiFile = stringToExistingFile(getValue(args, ++index)) 1085 val apiFileToBeTested = stringToExistingFile(getValue(args, ++index)) 1086 val stableRemovedApiFile = stringToExistingFile(getValue(args, ++index)) 1087 val removedApiFileToBeTested = stringToExistingFile(getValue(args, ++index)) 1088 mutableCompatibilityChecks.add( 1089 CheckRequest( 1090 stableApiFile, 1091 ApiType.PUBLIC_API, 1092 ReleaseType.RELEASED, 1093 apiFileToBeTested 1094 ) 1095 ) 1096 mutableCompatibilityChecks.add( 1097 CheckRequest( 1098 stableRemovedApiFile, 1099 ApiType.REMOVED, 1100 ReleaseType.RELEASED, 1101 removedApiFileToBeTested 1102 ) 1103 ) 1104 } 1105 } 1106 1107 ARG_ANNOTATION_COVERAGE_STATS -> dumpAnnotationStatistics = true 1108 ARG_ANNOTATION_COVERAGE_OF -> mutableAnnotationCoverageOf.addAll( 1109 stringToExistingDirsOrJars( 1110 getValue(args, ++index) 1111 ) 1112 ) 1113 ARG_WRITE_CLASS_COVERAGE_TO -> { 1114 annotationCoverageClassReport = stringToNewFile(getValue(args, ++index)) 1115 } 1116 ARG_WRITE_MEMBER_COVERAGE_TO -> { 1117 annotationCoverageMemberReport = stringToNewFile(getValue(args, ++index)) 1118 } 1119 1120 ARG_ERROR, "-error" -> setIssueSeverity( 1121 getValue(args, ++index), 1122 Severity.ERROR, 1123 arg 1124 ) 1125 ARG_WARNING, "-warning" -> setIssueSeverity( 1126 getValue(args, ++index), 1127 Severity.WARNING, 1128 arg 1129 ) 1130 ARG_LINT, "-lint" -> setIssueSeverity(getValue(args, ++index), Severity.LINT, arg) 1131 ARG_HIDE, "-hide" -> setIssueSeverity(getValue(args, ++index), Severity.HIDDEN, arg) 1132 1133 ARG_WARNINGS_AS_ERRORS -> warningsAreErrors = true 1134 ARG_LINTS_AS_ERRORS -> lintsAreErrors = true 1135 "-werror" -> { 1136 // Temporarily disabled; this is used in various builds but is pretty much 1137 // never what we want. 1138 // warningsAreErrors = true 1139 } 1140 "-lerror" -> { 1141 // Temporarily disabled; this is used in various builds but is pretty much 1142 // never what we want. 1143 // lintsAreErrors = true 1144 } 1145 1146 ARG_API_LINT -> { 1147 checkApi = true 1148 if (index < args.size - 1) { 1149 val nextArg = args[index + 1] 1150 if (!nextArg.startsWith("-")) { 1151 val file = stringToExistingFile(nextArg) 1152 if (file.isFile) { 1153 index++ 1154 checkApiBaselineApiFile = file 1155 } 1156 } 1157 } 1158 } 1159 ARG_API_LINT_IGNORE_PREFIX -> { 1160 checkApiIgnorePrefix.add(getValue(args, ++index)) 1161 } 1162 1163 ARG_CHECK_KOTLIN_INTEROP -> checkKotlinInterop = true 1164 1165 ARG_COLOR -> color = true 1166 ARG_NO_COLOR -> color = false 1167 ARG_NO_BANNER -> { 1168 // Already processed above but don't flag it here as invalid 1169 } 1170 1171 ARG_OMIT_COMMON_PACKAGES, "$ARG_OMIT_COMMON_PACKAGES=yes" -> compatibility.omitCommonPackages = true 1172 "$ARG_OMIT_COMMON_PACKAGES=no" -> compatibility.omitCommonPackages = false 1173 1174 ARG_SKIP_JAVA_IN_COVERAGE_REPORT -> omitRuntimePackageStats = true 1175 1176 ARG_UNHIDE_CLASSPATH_CLASSES -> hideClasspathClasses = false 1177 ARG_ALLOW_REFERENCING_UNKNOWN_CLASSES -> allowReferencingUnknownClasses = true 1178 ARG_NO_UNKNOWN_CLASSES -> noUnknownClasses = true 1179 1180 // Extracting API levels 1181 ARG_ANDROID_JAR_PATTERN -> { 1182 val list = androidJarPatterns ?: run { 1183 val list = arrayListOf<String>() 1184 androidJarPatterns = list 1185 list 1186 } 1187 list.add(getValue(args, ++index)) 1188 } 1189 ARG_CURRENT_VERSION -> { 1190 currentApiLevel = Integer.parseInt(getValue(args, ++index)) 1191 if (currentApiLevel <= 26) { 1192 throw DriverException("Suspicious currentApi=$currentApiLevel, expected at least 27") 1193 } 1194 } 1195 ARG_CURRENT_CODENAME -> { 1196 currentCodeName = getValue(args, ++index) 1197 } 1198 ARG_CURRENT_JAR -> { 1199 currentJar = stringToExistingFile(getValue(args, ++index)) 1200 } 1201 ARG_GENERATE_API_LEVELS -> { 1202 generateApiLevelXml = stringToNewFile(getValue(args, ++index)) 1203 } 1204 ARG_APPLY_API_LEVELS -> { 1205 applyApiLevelsXml = if (args.contains(ARG_GENERATE_API_LEVELS)) { 1206 // If generating the API file at the same time, it doesn't have 1207 // to already exist 1208 stringToNewFile(getValue(args, ++index)) 1209 } else { 1210 stringToExistingFile(getValue(args, ++index)) 1211 } 1212 } 1213 1214 ARG_NO_DOCS, "-nodocs" -> noDocs = true 1215 1216 ARG_UPDATE_API, "--update-api" -> onlyUpdateApi = true 1217 ARG_CHECK_API -> onlyCheckApi = true 1218 1219 ARG_GENERATE_DOCUMENTATION -> { 1220 // Digest all the remaining arguments. 1221 // Allow "STUBS_DIR" to reference the stubs directory. 1222 var prev = "" 1223 invokeDocumentationToolArguments = args.slice(++index until args.size).mapNotNull { 1224 var argument = it 1225 // When generating documentation, use the doc stubs directory rather than the 1226 // original source path 1227 val docStubsDir = docStubsDir 1228 if (docStubsDir != null && (prev == ARG_SOURCE_PATH || prev == "-sourcepath") && 1229 !argument.contains(docStubsDir.path) 1230 ) { 1231 // Insert the doc stubs as the default place to look for sources 1232 argument = docStubsDir.path 1233 } 1234 prev = it 1235 1236 if (argument == "STUBS_DIR" && docStubsDir != null) { 1237 docStubsDir.path 1238 } else if (argument == "STUBS_DIR" && stubsDir != null) { 1239 stubsDir?.path 1240 } else if (argument == "DOCS_STUBS_DIR" && docStubsDir != null) { 1241 docStubsDir.path 1242 } else if (argument == "DOC_STUBS_SOURCE_LIST" && docStubsSourceList != null) { 1243 "@${docStubsSourceList?.path}" 1244 } else if (argument == "STUBS_SOURCE_LIST" && stubsSourceList != null) { 1245 "@${stubsSourceList?.path}" 1246 } else if (argument == "STUBS_SOURCE_LIST" && docStubsSourceList != null) { 1247 "@${docStubsSourceList?.path}" 1248 } else { 1249 argument 1250 } 1251 }.toTypedArray() 1252 1253 index = args.size // jump to end of argument loop 1254 } 1255 1256 ARG_REPLACE_DOCUMENTATION -> { 1257 val packageNames = args[++index].split(":") 1258 val regex = Regex(args[++index]) 1259 val replacement = args[++index] 1260 val docReplacement = DocReplacement(packageNames, regex, replacement) 1261 docReplacements.add(docReplacement) 1262 } 1263 1264 ARG_REGISTER_ARTIFACT, "-artifact" -> { 1265 val descriptor = stringToExistingFile(getValue(args, ++index)) 1266 val artifactId = getValue(args, ++index) 1267 artifactRegistrations.register(artifactId, descriptor) 1268 } 1269 1270 ARG_CONVERT_TO_JDIFF, 1271 ARG_CONVERT_TO_V1, 1272 ARG_CONVERT_TO_V2, 1273 // doclava compatibility: 1274 "-convert2xml", 1275 "-convert2xmlnostrip" -> { 1276 val strip = arg == "-convert2xml" 1277 val format = when (arg) { 1278 ARG_CONVERT_TO_V1 -> FileFormat.V1 1279 ARG_CONVERT_TO_V2 -> FileFormat.V2 1280 else -> FileFormat.JDIFF 1281 } 1282 1283 val signatureFile = stringToExistingFile(getValue(args, ++index)) 1284 val outputFile = stringToNewFile(getValue(args, ++index)) 1285 mutableConvertToXmlFiles.add(ConvertFile(signatureFile, outputFile, null, strip, format)) 1286 } 1287 1288 ARG_CONVERT_NEW_TO_JDIFF, 1289 ARG_CONVERT_NEW_TO_V1, 1290 ARG_CONVERT_NEW_TO_V2, 1291 // doclava compatibility: 1292 "-new_api", 1293 "-new_api_no_strip" -> { 1294 val format = when (arg) { 1295 ARG_CONVERT_NEW_TO_V1 -> FileFormat.V1 1296 ARG_CONVERT_NEW_TO_V2 -> FileFormat.V2 1297 else -> FileFormat.JDIFF 1298 } 1299 val strip = arg == "-new_api" 1300 if (arg != ARG_CONVERT_NEW_TO_JDIFF) { 1301 // Using old doclava flags: Compatibility behavior: don't include fields in the output 1302 compatibility.includeFieldsInApiDiff = false 1303 } 1304 1305 val baseFile = stringToExistingFile(getValue(args, ++index)) 1306 val signatureFile = stringToExistingFile(getValue(args, ++index)) 1307 val jDiffFile = stringToNewFile(getValue(args, ++index)) 1308 mutableConvertToXmlFiles.add(ConvertFile(signatureFile, jDiffFile, baseFile, strip, format)) 1309 } 1310 1311 "--write-android-jar-signatures" -> { 1312 val root = stringToExistingDir(getValue(args, ++index)) 1313 if (!File(root, "prebuilts/sdk").isDirectory) { 1314 throw DriverException("$androidJarSignatureFiles does not point to an Android source tree") 1315 } 1316 androidJarSignatureFiles = root 1317 } 1318 1319 "-encoding" -> { 1320 val value = getValue(args, ++index) 1321 if (value.toUpperCase() != "UTF-8") { 1322 throw DriverException("$value: Only UTF-8 encoding is supported") 1323 } 1324 } 1325 1326 ARG_JAVA_SOURCE, "-source" -> { 1327 val value = getValue(args, ++index) 1328 val level = LanguageLevel.parse(value) 1329 when { 1330 level == null -> throw DriverException("$value is not a valid or supported Java language level") 1331 level.isLessThan(LanguageLevel.JDK_1_7) -> throw DriverException("$arg must be at least 1.7") 1332 else -> javaLanguageLevel = level 1333 } 1334 } 1335 1336 ARG_NO_IMPLICIT_ROOT -> { 1337 allowImplicitRoot = false 1338 } 1339 1340 ARG_STRICT_INPUT_FILES, ARG_STRICT_INPUT_FILES_WARN, ARG_STRICT_INPUT_FILES_STACK -> { 1341 if (strictInputViolationsFile != null) { 1342 throw DriverException("$ARG_STRICT_INPUT_FILES, $ARG_STRICT_INPUT_FILES_WARN and $ARG_STRICT_INPUT_FILES_STACK may be specified only once") 1343 } 1344 strictInputFiles = StrictInputFileMode.fromArgument(arg) 1345 1346 val file = stringToNewOrExistingFile(getValue(args, ++index)) 1347 strictInputViolationsFile = file 1348 strictInputViolationsPrintWriter = file.printWriter() 1349 } 1350 ARG_STRICT_INPUT_FILES_EXEMPT -> { 1351 val listString = getValue(args, ++index) 1352 listString.split(File.pathSeparatorChar).forEach { path -> 1353 // Throw away the result; just let the function add the files to the whitelist. 1354 stringToExistingFilesOrDirs(path) 1355 } 1356 } 1357 1358 "--temp-folder" -> { 1359 tempFolder = stringToNewOrExistingDir(getValue(args, ++index)) 1360 } 1361 1362 // Option only meant for tests (not documented); doesn't work in all cases (to do that we'd 1363 // need JNA to call libc) 1364 "--pwd" -> { 1365 val pwd = stringToExistingDir(getValue(args, ++index)).absoluteFile 1366 System.setProperty("user.dir", pwd.path) 1367 } 1368 1369 "--noop", "--no-op" -> { 1370 } 1371 1372 // Doclava1 flag: Already the behavior in metalava 1373 "-keepstubcomments" -> { 1374 } 1375 1376 // Unimplemented doclava1 flags (no arguments) 1377 "-quiet", 1378 "-yamlV2" -> { 1379 unimplemented(arg) 1380 } 1381 1382 "-android" -> { // partially implemented: Pick up the color hint, but there may be other implications 1383 color = true 1384 unimplemented(arg) 1385 } 1386 1387 "-stubsourceonly" -> { 1388 /* noop */ 1389 } 1390 1391 // Unimplemented doclava1 flags (1 argument) 1392 "-d" -> { 1393 unimplemented(arg) 1394 index++ 1395 } 1396 1397 // Unimplemented doclava1 flags (2 arguments) 1398 "-since" -> { 1399 unimplemented(arg) 1400 index += 2 1401 } 1402 1403 // doclava1 doc-related flags: only supported here to make this command a drop-in 1404 // replacement 1405 "-referenceonly", 1406 "-devsite", 1407 "-ignoreJdLinks", 1408 "-nodefaultassets", 1409 "-parsecomments", 1410 "-offlinemode", 1411 "-gcmref", 1412 "-metadataDebug", 1413 "-includePreview", 1414 "-staticonly", 1415 "-navtreeonly", 1416 "-atLinksNavtree" -> { 1417 javadoc(arg) 1418 } 1419 1420 // doclava1 flags with 1 argument 1421 "-doclet", 1422 "-docletpath", 1423 "-templatedir", 1424 "-htmldir", 1425 "-knowntags", 1426 "-resourcesdir", 1427 "-resourcesoutdir", 1428 "-yaml", 1429 "-apidocsdir", 1430 "-toroot", 1431 "-samplegroup", 1432 "-samplesdir", 1433 "-dac_libraryroot", 1434 "-dac_dataname", 1435 "-title", 1436 "-proofread", 1437 "-todo", 1438 "-overview" -> { 1439 javadoc(arg) 1440 index++ 1441 } 1442 1443 // doclava1 flags with two arguments 1444 "-federate", 1445 "-federationapi", 1446 "-htmldir2" -> { 1447 javadoc(arg) 1448 index += 2 1449 } 1450 1451 // doclava1 flags with three arguments 1452 "-samplecode" -> { 1453 javadoc(arg) 1454 index += 3 1455 } 1456 1457 // doclava1 flag with variable number of arguments; skip everything until next arg 1458 "-hdf" -> { 1459 javadoc(arg) 1460 index++ 1461 while (index < args.size) { 1462 if (args[index].startsWith("-")) { 1463 break 1464 } 1465 index++ 1466 } 1467 index-- 1468 } 1469 1470 else -> { 1471 if (arg.startsWith("-J-") || arg.startsWith("-XD")) { 1472 // -J: mechanism to pass extra flags to javadoc, e.g. 1473 // -J-XX:-OmitStackTraceInFastThrow 1474 // -XD: mechanism to set properties, e.g. 1475 // -XDignore.symbol.file 1476 javadoc(arg) 1477 } else if (arg.startsWith(ARG_OUTPUT_KOTLIN_NULLS)) { 1478 outputKotlinStyleNulls = if (arg == ARG_OUTPUT_KOTLIN_NULLS) { 1479 true 1480 } else { 1481 yesNo(arg.substring(ARG_OUTPUT_KOTLIN_NULLS.length + 1)) 1482 } 1483 } else if (arg.startsWith(ARG_INPUT_KOTLIN_NULLS)) { 1484 inputKotlinStyleNulls = if (arg == ARG_INPUT_KOTLIN_NULLS) { 1485 true 1486 } else { 1487 yesNo(arg.substring(ARG_INPUT_KOTLIN_NULLS.length + 1)) 1488 } 1489 } else if (arg.startsWith(ARG_OUTPUT_DEFAULT_VALUES)) { 1490 outputDefaultValues = if (arg == ARG_OUTPUT_DEFAULT_VALUES) { 1491 true 1492 } else { 1493 yesNo(arg.substring(ARG_OUTPUT_DEFAULT_VALUES.length + 1)) 1494 } 1495 } else if (arg.startsWith(ARG_OMIT_COMMON_PACKAGES)) { 1496 compatibility.omitCommonPackages = if (arg == ARG_OMIT_COMMON_PACKAGES) { 1497 true 1498 } else { 1499 yesNo(arg.substring(ARG_OMIT_COMMON_PACKAGES.length + 1)) 1500 } 1501 } else if (arg.startsWith(ARG_COMPAT_OUTPUT)) { 1502 compatOutput = if (arg == ARG_COMPAT_OUTPUT) 1503 true 1504 else yesNo(arg.substring(ARG_COMPAT_OUTPUT.length + 1)) 1505 } else if (arg.startsWith(ARG_INCLUDE_SIG_VERSION)) { 1506 includeSignatureFormatVersion = if (arg == ARG_INCLUDE_SIG_VERSION) 1507 true 1508 else yesNo(arg.substring(ARG_INCLUDE_SIG_VERSION.length + 1)) 1509 } else if (arg.startsWith(ARG_FORMAT)) { 1510 outputFormat = when (arg) { 1511 "$ARG_FORMAT=v1" -> { 1512 FileFormat.V1 1513 } 1514 "$ARG_FORMAT=v2", "$ARG_FORMAT=recommended" -> { 1515 FileFormat.V2 1516 } 1517 "$ARG_FORMAT=v3", "$ARG_FORMAT=latest" -> { 1518 FileFormat.V3 1519 } 1520 else -> throw DriverException(stderr = "Unexpected signature format; expected v1, v2 or v3") 1521 } 1522 outputFormat.configureOptions(this, compatibility) 1523 } else if (arg.startsWith("-")) { 1524 // Compatibility flag; map to mutable properties in the Compatibility 1525 // class and assign it 1526 val compatibilityArg = findCompatibilityFlag(arg) 1527 if (compatibilityArg != null) { 1528 val dash = arg.indexOf('=') 1529 val value = if (dash == -1) { 1530 true 1531 } else { 1532 arg.substring(dash + 1).toBoolean() 1533 } 1534 compatibilityArg.set(compatibility, value) 1535 } else { 1536 // Some other argument: display usage info and exit 1537 1538 val usage = getUsage(includeHeader = false, colorize = color) 1539 throw DriverException(stderr = "Invalid argument $arg\n\n$usage") 1540 } 1541 } else { 1542 if (delayedCheckApiFiles) { 1543 delayedCheckApiFiles = false 1544 val stableApiFile = stringToExistingFile(arg) 1545 val apiFileToBeTested = stringToExistingFile(getValue(args, ++index)) 1546 val stableRemovedApiFile = stringToExistingFile(getValue(args, ++index)) 1547 val removedApiFileToBeTested = stringToExistingFile(getValue(args, ++index)) 1548 mutableCompatibilityChecks.add( 1549 CheckRequest( 1550 stableApiFile, 1551 ApiType.PUBLIC_API, 1552 ReleaseType.RELEASED, 1553 apiFileToBeTested 1554 ) 1555 ) 1556 mutableCompatibilityChecks.add( 1557 CheckRequest( 1558 stableRemovedApiFile, 1559 ApiType.REMOVED, 1560 ReleaseType.RELEASED, 1561 removedApiFileToBeTested 1562 ) 1563 ) 1564 } else { 1565 // All args that don't start with "-" are taken to be filenames 1566 mutableSources.addAll(stringToExistingFiles(arg)) 1567 1568 // Temporary workaround for 1569 // aosp/I73ff403bfc3d9dfec71789a3e90f9f4ea95eabe3 1570 if (arg.endsWith("hwbinder-stubs-docs-stubs.srcjar.rsp")) { 1571 skipGenerateAnnotations = true 1572 } 1573 } 1574 } 1575 } 1576 } 1577 1578 ++index 1579 } 1580 1581 if (generateApiLevelXml != null) { 1582 val patterns = androidJarPatterns ?: run { 1583 mutableListOf<String>() 1584 } 1585 // Fallbacks 1586 patterns.add("prebuilts/tools/common/api-versions/android-%/android.jar") 1587 patterns.add("prebuilts/sdk/%/public/android.jar") 1588 apiLevelJars = findAndroidJars(patterns, currentApiLevel, currentCodeName, currentJar) 1589 } 1590 1591 // outputKotlinStyleNulls implies format=v3 1592 if (outputKotlinStyleNulls) { 1593 outputFormat = FileFormat.V3 1594 outputFormat.configureOptions(this, compatibility) 1595 } 1596 1597 // If the caller has not explicitly requested that unannotated classes and 1598 // members should be shown in the output then only show them if no annotations were provided. 1599 if (!showUnannotated && showAnnotations.isEmpty()) { 1600 showUnannotated = true 1601 } 1602 1603 if (noUnknownClasses) { 1604 allowReferencingUnknownClasses = false 1605 } 1606 1607 if (skipGenerateAnnotations) { 1608 generateAnnotations = false 1609 } 1610 1611 if (onlyUpdateApi) { 1612 if (onlyCheckApi) { 1613 throw DriverException(stderr = "Cannot supply both $ARG_UPDATE_API and $ARG_CHECK_API at the same time") 1614 } 1615 // We're running in update API mode: cancel other "action" flags; only signature file generation 1616 // flags count 1617 annotationCoverageClassReport = null 1618 annotationCoverageMemberReport = null 1619 dumpAnnotationStatistics = false 1620 apiLevelJars = null 1621 generateApiLevelXml = null 1622 applyApiLevelsXml = null 1623 androidJarSignatureFiles = null 1624 stubsDir = null 1625 docStubsDir = null 1626 stubsSourceList = null 1627 docStubsSourceList = null 1628 sdkValueDir = null 1629 externalAnnotations = null 1630 proguard = null 1631 noDocs = true 1632 invokeDocumentationToolArguments = emptyArray() 1633 checkKotlinInterop = false 1634 mutableCompatibilityChecks.clear() 1635 mutableAnnotationCoverageOf.clear() 1636 artifactRegistrations.clear() 1637 mutableConvertToXmlFiles.clear() 1638 nullabilityAnnotationsValidator = null 1639 nullabilityWarningsTxt = null 1640 validateNullabilityFromMergedStubs = false 1641 validateNullabilityFromMergedStubs = false 1642 validateNullabilityFromList = null 1643 } else if (onlyCheckApi) { 1644 annotationCoverageClassReport = null 1645 annotationCoverageMemberReport = null 1646 dumpAnnotationStatistics = false 1647 apiLevelJars = null 1648 generateApiLevelXml = null 1649 applyApiLevelsXml = null 1650 androidJarSignatureFiles = null 1651 stubsDir = null 1652 docStubsDir = null 1653 stubsSourceList = null 1654 docStubsSourceList = null 1655 sdkValueDir = null 1656 externalAnnotations = null 1657 proguard = null 1658 noDocs = true 1659 invokeDocumentationToolArguments = emptyArray() 1660 checkKotlinInterop = false 1661 mutableAnnotationCoverageOf.clear() 1662 artifactRegistrations.clear() 1663 mutableConvertToXmlFiles.clear() 1664 nullabilityAnnotationsValidator = null 1665 nullabilityWarningsTxt = null 1666 validateNullabilityFromMergedStubs = false 1667 validateNullabilityFromMergedStubs = false 1668 validateNullabilityFromList = null 1669 apiFile = null 1670 apiXmlFile = null 1671 privateApiFile = null 1672 dexApiFile = null 1673 dexApiMappingFile = null 1674 privateDexApiFile = null 1675 removedApiFile = null 1676 removedDexApiFile = null 1677 } 1678 1679 // Fix up [Baseline] files and [Reporter]s. 1680 1681 val baselineHeaderComment = if (isBuildingAndroid()) 1682 "// See tools/metalava/API-LINT.md for how to update this file.\n\n" 1683 else 1684 "" 1685 baselineBuilder.headerComment = baselineHeaderComment 1686 baselineApiLintBuilder.headerComment = baselineHeaderComment 1687 baselineCompatibilityReleasedBuilder.headerComment = baselineHeaderComment 1688 1689 if (baselineBuilder.file == null) { 1690 // If default baseline is a file, use it. 1691 val defaultBaselineFile = getDefaultBaselineFile() 1692 if (defaultBaselineFile != null && defaultBaselineFile.isFile) { 1693 baselineBuilder.file = defaultBaselineFile 1694 } 1695 } 1696 1697 baseline = baselineBuilder.build() 1698 baselineApiLint = baselineApiLintBuilder.build() 1699 baselineCompatibilityReleased = baselineCompatibilityReleasedBuilder.build() 1700 1701 reporterApiLint = Reporter( 1702 baselineApiLint ?: baseline, 1703 errorMessageApiLint 1704 ) 1705 reporterCompatibilityReleased = Reporter( 1706 baselineCompatibilityReleased ?: baseline, 1707 errorMessageCompatibilityReleased 1708 ) 1709 reporterCompatibilityCurrent = Reporter( 1710 // Note, the compat-check:current shouldn't take a baseline file, so we don't have 1711 // a task specific baseline file, but we still respect the global baseline file. 1712 baseline, 1713 errorMessageCompatibilityCurrent 1714 ) 1715 1716 // Build "all baselines" and "all reporters" 1717 1718 // Baselines are nullable, so selectively add to the list. 1719 allBaselines = listOfNotNull(baseline, baselineApiLint, baselineCompatibilityReleased) 1720 1721 // Reporters are non-null. 1722 allReporters = listOf( 1723 reporter, 1724 reporterApiLint, 1725 reporterCompatibilityReleased, 1726 reporterCompatibilityCurrent 1727 ) 1728 1729 checkFlagConsistency() 1730 } 1731 1732 private fun findCompatibilityFlag(arg: String): KMutableProperty1<Compatibility, Boolean>? { 1733 val index = arg.indexOf('=') 1734 val name = arg 1735 .substring(0, if (index != -1) index else arg.length) 1736 .removePrefix("--") 1737 .replace('-', '_') 1738 val propertyName = SdkVersionInfo.underlinesToCamelCase(name).decapitalize() 1739 return Compatibility::class.memberProperties 1740 .filterIsInstance<KMutableProperty1<Compatibility, Boolean>>() 1741 .find { 1742 it.name == propertyName 1743 } 1744 } 1745 1746 /** 1747 * Produce a default file name for the baseline. It's normally "baseline.txt", but can 1748 * be prefixed by show annotations; e.g. @TestApi -> test-baseline.txt, @SystemApi -> system-baseline.txt, 1749 * etc. 1750 * 1751 * Note because the default baseline file is not explicitly set in the command line, 1752 * this file would trigger a --strict-input-files violation. To avoid that, always explicitly 1753 * pass a baseline file. 1754 */ 1755 private fun getDefaultBaselineFile(): File? { 1756 if (sourcePath.isNotEmpty() && sourcePath[0].path.isNotBlank()) { 1757 fun annotationToPrefix(qualifiedName: String): String { 1758 val name = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1) 1759 return name.toLowerCase(Locale.US).removeSuffix("api") + "-" 1760 } 1761 val sb = StringBuilder() 1762 showAnnotations.getIncludedAnnotationNames().forEach { sb.append(annotationToPrefix(it)) } 1763 sb.append(DEFAULT_BASELINE_NAME) 1764 var base = sourcePath[0] 1765 // Convention: in AOSP, signature files are often in sourcepath/api: let's place baseline 1766 // files there too 1767 val api = File(base, "api") 1768 if (api.isDirectory) { 1769 base = api 1770 } 1771 return File(base, sb.toString()) 1772 } else { 1773 return null 1774 } 1775 } 1776 1777 /** 1778 * Find an android stub jar that matches the given criteria. 1779 * 1780 * Note because the default baseline file is not explicitly set in the command line, 1781 * this file would trigger a --strict-input-files violation. To avoid that, use 1782 * --strict-input-files-exempt to exempt the jar directory. 1783 */ 1784 private fun findAndroidJars( 1785 androidJarPatterns: List<String>, 1786 currentApiLevel: Int, 1787 currentCodeName: String?, 1788 currentJar: File? 1789 ): Array<File> { 1790 1791 @Suppress("NAME_SHADOWING") 1792 val currentApiLevel = if (currentCodeName != null && "REL" != currentCodeName) { 1793 currentApiLevel + 1 1794 } else { 1795 currentApiLevel 1796 } 1797 1798 val apiLevelFiles = mutableListOf<File>() 1799 apiLevelFiles.add(File("there is no api 0")) // api level 0: dummy, should not be processed 1800 val minApi = 1 1801 1802 // Get all the android.jar. They are in platforms-# 1803 var apiLevel = minApi - 1 1804 while (true) { 1805 apiLevel++ 1806 try { 1807 var jar: File? = null 1808 if (apiLevel == currentApiLevel) { 1809 jar = currentJar 1810 } 1811 if (jar == null) { 1812 jar = getAndroidJarFile(apiLevel, androidJarPatterns) 1813 } 1814 if (jar == null || !jar.isFile) { 1815 if (verbose) { 1816 stdout.println("Last API level found: ${apiLevel - 1}") 1817 } 1818 1819 if (apiLevel < 28) { 1820 // Clearly something is wrong with the patterns; this should result in a build error 1821 val argList = mutableListOf<String>() 1822 args.forEachIndexed { index, arg -> 1823 if (arg == ARG_ANDROID_JAR_PATTERN) { 1824 argList.add(args[index + 1]) 1825 } 1826 } 1827 throw DriverException(stderr = "Could not find android.jar for API level $apiLevel; the " + 1828 "$ARG_ANDROID_JAR_PATTERN set might be invalid: ${argList.joinToString()}") 1829 } 1830 1831 break 1832 } 1833 if (verbose) { 1834 stdout.println("Found API $apiLevel at ${jar.path}") 1835 } 1836 apiLevelFiles.add(jar) 1837 } catch (e: IOException) { 1838 e.printStackTrace() 1839 } 1840 } 1841 1842 return apiLevelFiles.toTypedArray() 1843 } 1844 1845 private fun getAndroidJarFile(apiLevel: Int, patterns: List<String>): File? { 1846 // Note this method doesn't register the result to [FileReadSandbox] 1847 return patterns 1848 .map { fileForPathInner(it.replace("%", Integer.toString(apiLevel))) } 1849 .firstOrNull { it.isFile } 1850 } 1851 1852 private fun yesNo(answer: String): Boolean { 1853 return when (answer) { 1854 "yes", "true", "enabled", "on" -> true 1855 "no", "false", "disabled", "off" -> false 1856 else -> throw DriverException(stderr = "Unexpected $answer; expected yes or no") 1857 } 1858 } 1859 1860 /** Makes sure that the flag combinations make sense */ 1861 private fun checkFlagConsistency() { 1862 if (apiJar != null && sources.isNotEmpty()) { 1863 throw DriverException(stderr = "Specify either $ARG_SOURCE_FILES or $ARG_INPUT_API_JAR, not both") 1864 } 1865 1866 if (compatOutput && outputKotlinStyleNulls) { 1867 throw DriverException( 1868 stderr = "$ARG_OUTPUT_KOTLIN_NULLS=yes should not be combined with " + 1869 "$ARG_COMPAT_OUTPUT=yes" 1870 ) 1871 } 1872 1873 if (compatOutput && outputDefaultValues) { 1874 throw DriverException( 1875 stderr = "$ARG_OUTPUT_DEFAULT_VALUES=yes should not be combined with " + 1876 "$ARG_COMPAT_OUTPUT=yes" 1877 ) 1878 } 1879 1880 if (compatOutput && includeSignatureFormatVersion) { 1881 throw DriverException( 1882 stderr = "$ARG_INCLUDE_SIG_VERSION=yes should not be combined with " + 1883 "$ARG_COMPAT_OUTPUT=yes" 1884 ) 1885 } 1886 } 1887 1888 private fun javadoc(arg: String) { 1889 if (!alreadyWarned.add(arg)) { 1890 return 1891 } 1892 if (!options.quiet) { 1893 reporter.report( 1894 Severity.WARNING, null as String?, "Ignoring javadoc-related doclava1 flag $arg", 1895 color = color 1896 ) 1897 } 1898 } 1899 1900 private fun unimplemented(arg: String) { 1901 if (!alreadyWarned.add(arg)) { 1902 return 1903 } 1904 if (!options.quiet) { 1905 val message = "Ignoring unimplemented doclava1 flag $arg" + 1906 when (arg) { 1907 "-encoding" -> " (UTF-8 assumed)" 1908 "-source" -> " (1.8 assumed)" 1909 else -> "" 1910 } 1911 reporter.report(Severity.WARNING, null as String?, message, color = color) 1912 } 1913 } 1914 1915 private fun helpAndQuit(colorize: Boolean = color) { 1916 throw DriverException(stdout = getUsage(colorize = colorize)) 1917 } 1918 1919 private fun getValue(args: Array<String>, index: Int): String { 1920 if (index >= args.size) { 1921 throw DriverException("Missing argument for ${args[index - 1]}") 1922 } 1923 return args[index] 1924 } 1925 1926 private fun stringToExistingDir(value: String): File { 1927 val file = fileForPathInner(value) 1928 if (!file.isDirectory) { 1929 throw DriverException("$file is not a directory") 1930 } 1931 return FileReadSandbox.allowAccess(file) 1932 } 1933 1934 @Suppress("unused") 1935 private fun stringToExistingDirs(value: String): List<File> { 1936 val files = mutableListOf<File>() 1937 for (path in value.split(File.pathSeparatorChar)) { 1938 val file = fileForPathInner(path) 1939 if (!file.isDirectory) { 1940 throw DriverException("$file is not a directory") 1941 } 1942 files.add(file) 1943 } 1944 return FileReadSandbox.allowAccess(files) 1945 } 1946 1947 private fun stringToExistingDirsOrJars(value: String, exempt: Boolean = true): List<File> { 1948 val files = mutableListOf<File>() 1949 for (path in value.split(File.pathSeparatorChar)) { 1950 val file = fileForPathInner(path) 1951 if (!file.isDirectory && !(file.path.endsWith(SdkConstants.DOT_JAR) && file.isFile)) { 1952 throw DriverException("$file is not a jar or directory") 1953 } 1954 files.add(file) 1955 } 1956 if (exempt) { 1957 return FileReadSandbox.allowAccess(files) 1958 } 1959 return files 1960 } 1961 1962 private fun stringToExistingDirsOrFiles(value: String): List<File> { 1963 val files = mutableListOf<File>() 1964 for (path in value.split(File.pathSeparatorChar)) { 1965 val file = fileForPathInner(path) 1966 if (!file.exists()) { 1967 throw DriverException("$file does not exist") 1968 } 1969 files.add(file) 1970 } 1971 return FileReadSandbox.allowAccess(files) 1972 } 1973 1974 private fun stringToExistingFile(value: String): File { 1975 val file = fileForPathInner(value) 1976 if (!file.isFile) { 1977 throw DriverException("$file is not a file") 1978 } 1979 return FileReadSandbox.allowAccess(file) 1980 } 1981 1982 @Suppress("unused") 1983 private fun stringToExistingFileOrDir(value: String): File { 1984 val file = fileForPathInner(value) 1985 if (!file.exists()) { 1986 throw DriverException("$file is not a file or directory") 1987 } 1988 return FileReadSandbox.allowAccess(file) 1989 } 1990 1991 private fun stringToExistingFiles(value: String): List<File> { 1992 return stringToExistingFilesOrDirsInternal(value, false) 1993 } 1994 1995 private fun stringToExistingFilesOrDirs(value: String): List<File> { 1996 return stringToExistingFilesOrDirsInternal(value, true) 1997 } 1998 1999 private fun stringToExistingFilesOrDirsInternal(value: String, allowDirs: Boolean): List<File> { 2000 val files = mutableListOf<File>() 2001 value.split(File.pathSeparatorChar) 2002 .map { fileForPathInner(it) } 2003 .forEach { file -> 2004 if (file.path.startsWith("@")) { 2005 // File list; files to be read are stored inside. SHOULD have been one per line 2006 // but sadly often uses spaces for separation too (so we split by whitespace, 2007 // which means you can't point to files in paths with spaces) 2008 val listFile = File(file.path.substring(1)) 2009 if (!allowDirs && !listFile.isFile) { 2010 throw DriverException("$listFile is not a file") 2011 } 2012 val contents = Files.asCharSource(listFile, UTF_8).read() 2013 val pathList = Splitter.on(CharMatcher.whitespace()).trimResults().omitEmptyStrings().split( 2014 contents 2015 ) 2016 pathList.asSequence().map { File(it) }.forEach { 2017 if (!allowDirs && !it.isFile) { 2018 throw DriverException("$it is not a file") 2019 } 2020 files.add(it) 2021 } 2022 } else { 2023 if (!allowDirs && !file.isFile) { 2024 throw DriverException("$file is not a file") 2025 } 2026 files.add(file) 2027 } 2028 } 2029 return FileReadSandbox.allowAccess(files) 2030 } 2031 2032 private fun stringToNewFile(value: String): File { 2033 val output = fileForPathInner(value) 2034 2035 if (output.exists()) { 2036 if (output.isDirectory) { 2037 throw DriverException("$output is a directory") 2038 } 2039 val deleted = output.delete() 2040 if (!deleted) { 2041 throw DriverException("Could not delete previous version of $output") 2042 } 2043 } else if (output.parentFile != null && !output.parentFile.exists()) { 2044 val ok = output.parentFile.mkdirs() 2045 if (!ok) { 2046 throw DriverException("Could not create ${output.parentFile}") 2047 } 2048 } 2049 2050 return FileReadSandbox.allowAccess(output) 2051 } 2052 2053 private fun stringToNewOrExistingDir(value: String): File { 2054 val dir = fileForPathInner(value) 2055 if (!dir.isDirectory) { 2056 val ok = dir.mkdirs() 2057 if (!ok) { 2058 throw DriverException("Could not create $dir") 2059 } 2060 } 2061 return FileReadSandbox.allowAccess(dir) 2062 } 2063 2064 private fun stringToNewOrExistingFile(value: String): File { 2065 val file = fileForPathInner(value) 2066 if (!file.exists()) { 2067 val parentFile = file.parentFile 2068 if (parentFile != null && !parentFile.isDirectory) { 2069 val ok = parentFile.mkdirs() 2070 if (!ok) { 2071 throw DriverException("Could not create $parentFile") 2072 } 2073 } 2074 } 2075 return FileReadSandbox.allowAccess(file) 2076 } 2077 2078 private fun stringToNewDir(value: String): File { 2079 val output = fileForPathInner(value) 2080 val ok = 2081 if (output.exists()) { 2082 if (output.isDirectory) { 2083 output.deleteRecursively() 2084 } 2085 if (output.exists()) { 2086 true 2087 } else { 2088 output.mkdir() 2089 } 2090 } else { 2091 output.mkdirs() 2092 } 2093 if (!ok) { 2094 throw DriverException("Could not create $output") 2095 } 2096 2097 return FileReadSandbox.allowAccess(output) 2098 } 2099 2100 /** 2101 * Converts a path to a [File] that represents the absolute path, with the following special 2102 * behavior: 2103 * - "~" will be expanded into the home directory path. 2104 * - If the given path starts with "@", it'll be converted into "@" + [file's absolute path] 2105 * 2106 * Note, unlike the other "stringToXxx" methods, this method won't register the given path 2107 * to [FileReadSandbox]. 2108 */ 2109 private fun fileForPathInner(path: String): File { 2110 // java.io.File doesn't automatically handle ~/ -> home directory expansion. 2111 // This isn't necessary when metalava is run via the command line driver 2112 // (since shells will perform this expansion) but when metalava is run 2113 // directly, not from a shell. 2114 if (path.startsWith("~/")) { 2115 val home = System.getProperty("user.home") ?: return File(path) 2116 return File(home + path.substring(1)) 2117 } else if (path.startsWith("@")) { 2118 return File("@" + File(path.substring(1)).absolutePath) 2119 } 2120 2121 return File(path).absoluteFile 2122 } 2123 2124 private fun getUsage(includeHeader: Boolean = true, colorize: Boolean = color): String { 2125 val usage = StringWriter() 2126 val printWriter = PrintWriter(usage) 2127 usage(printWriter, includeHeader, colorize) 2128 return usage.toString() 2129 } 2130 2131 private fun usage(out: PrintWriter, includeHeader: Boolean = true, colorize: Boolean = color) { 2132 if (includeHeader) { 2133 out.println(wrap(HELP_PROLOGUE, MAX_LINE_WIDTH, "")) 2134 } 2135 2136 if (colorize) { 2137 out.println("Usage: ${colorized(PROGRAM_NAME, TerminalColor.BLUE)} <flags>") 2138 } else { 2139 out.println("Usage: $PROGRAM_NAME <flags>") 2140 } 2141 2142 val args = arrayOf( 2143 "", "\nGeneral:", 2144 ARG_HELP, "This message.", 2145 ARG_VERSION, "Show the version of $PROGRAM_NAME.", 2146 ARG_QUIET, "Only include vital output", 2147 ARG_VERBOSE, "Include extra diagnostic output", 2148 ARG_COLOR, "Attempt to colorize the output (defaults to true if \$TERM is xterm)", 2149 ARG_NO_COLOR, "Do not attempt to colorize the output", 2150 ARG_NO_DOCS, "Cancel any other documentation flags supplied to $PROGRAM_NAME. This is here " + 2151 "to make it easier customize build system tasks.", 2152 ARG_UPDATE_API, "Cancel any other \"action\" flags other than generating signature files. This is here " + 2153 "to make it easier customize build system tasks, particularly for the \"make update-api\" task.", 2154 ARG_CHECK_API, "Cancel any other \"action\" flags other than checking signature files. This is here " + 2155 "to make it easier customize build system tasks, particularly for the \"make checkapi\" task.", 2156 2157 "", "\nAPI sources:", 2158 "$ARG_SOURCE_FILES <files>", "A comma separated list of source files to be parsed. Can also be " + 2159 "@ followed by a path to a text file containing paths to the full set of files to parse.", 2160 2161 "$ARG_SOURCE_PATH <paths>", "One or more directories (separated by `${File.pathSeparator}`) " + 2162 "containing source files (within a package hierarchy). If $ARG_STRICT_INPUT_FILES, " + 2163 "$ARG_STRICT_INPUT_FILES_WARN, or $ARG_STRICT_INPUT_FILES_STACK are used, files accessed under " + 2164 "$ARG_SOURCE_PATH that are not explicitly specified in $ARG_SOURCE_FILES are reported as " + 2165 "violations.", 2166 2167 "$ARG_CLASS_PATH <paths>", "One or more directories or jars (separated by " + 2168 "`${File.pathSeparator}`) containing classes that should be on the classpath when parsing the " + 2169 "source files", 2170 2171 "$ARG_MERGE_QUALIFIER_ANNOTATIONS <file>", "An external annotations file to merge and overlay " + 2172 "the sources, or a directory of such files. Should be used for annotations intended for " + 2173 "inclusion in the API to be written out, e.g. nullability. Formats supported are: IntelliJ's " + 2174 "external annotations database format, .jar or .zip files containing those, Android signature " + 2175 "files, and Java stub files.", 2176 2177 "$ARG_MERGE_INCLUSION_ANNOTATIONS <file>", "An external annotations file to merge and overlay " + 2178 "the sources, or a directory of such files. Should be used for annotations which determine " + 2179 "inclusion in the API to be written out, i.e. show and hide. The only format supported is " + 2180 "Java stub files.", 2181 2182 ARG_VALIDATE_NULLABILITY_FROM_MERGED_STUBS, "Triggers validation of nullability annotations " + 2183 "for any class where $ARG_MERGE_QUALIFIER_ANNOTATIONS includes a Java stub file.", 2184 2185 ARG_VALIDATE_NULLABILITY_FROM_LIST, "Triggers validation of nullability annotations " + 2186 "for any class listed in the named file (one top-level class per line, # prefix for comment line).", 2187 2188 "$ARG_NULLABILITY_WARNINGS_TXT <file>", "Specifies where to write warnings encountered during " + 2189 "validation of nullability annotations. (Does not trigger validation by itself.)", 2190 2191 ARG_NULLABILITY_ERRORS_NON_FATAL, "Specifies that errors encountered during validation of " + 2192 "nullability annotations should not be treated as errors. They will be written out to the " + 2193 "file specified in $ARG_NULLABILITY_WARNINGS_TXT instead.", 2194 2195 "$ARG_INPUT_API_JAR <file>", "A .jar file to read APIs from directly", 2196 2197 "$ARG_MANIFEST <file>", "A manifest file, used to for check permissions to cross check APIs", 2198 2199 "$ARG_REPLACE_DOCUMENTATION <p> <r> <t>", "Amongst nonempty documentation of items from Java " + 2200 "packages <p> and their subpackages, replaces any matches of regular expression <r> " + 2201 "with replacement text <t>. <p> is given as a nonempty list of Java package names separated " + 2202 "by ':' (e.g. \"java:android.util\"); <t> may contain backreferences (\$1, \$2 etc.) to " + 2203 "matching groups from <r>.", 2204 2205 "$ARG_HIDE_PACKAGE <package>", "Remove the given packages from the API even if they have not been " + 2206 "marked with @hide", 2207 2208 "$ARG_SHOW_ANNOTATION <annotation class>", "Unhide any hidden elements that are also annotated " + 2209 "with the given annotation", 2210 "$ARG_SHOW_SINGLE_ANNOTATION <annotation>", "Like $ARG_SHOW_ANNOTATION, but does not apply " + 2211 "to members; these must also be explicitly annotated", 2212 "$ARG_HIDE_ANNOTATION <annotation class>", "Treat any elements annotated with the given annotation " + 2213 "as hidden", 2214 "$ARG_HIDE_META_ANNOTATION <meta-annotation class>", "Treat as hidden any elements annotated with an " + 2215 "annotation which is itself annotated with the given meta-annotation", 2216 ARG_SHOW_UNANNOTATED, "Include un-annotated public APIs in the signature file as well", 2217 "$ARG_JAVA_SOURCE <level>", "Sets the source level for Java source files; default is 1.8.", 2218 "$ARG_STUB_PACKAGES <package-list>", "List of packages (separated by ${File.pathSeparator}) which will " + 2219 "be used to filter out irrelevant code. If specified, only code in these packages will be " + 2220 "included in signature files, stubs, etc. (This is not limited to just the stubs; the name " + 2221 "is historical.) You can also use \".*\" at the end to match subpackages, so `foo.*` will " + 2222 "match both `foo` and `foo.bar`.", 2223 "$ARG_SUBTRACT_API <api file>", "Subtracts the API in the given signature or jar file from the " + 2224 "current API being emitted via $ARG_API, $ARG_STUBS, $ARG_DOC_STUBS, etc. " + 2225 "Note that the subtraction only applies to classes; it does not subtract members.", 2226 "$ARG_TYPEDEFS_IN_SIGNATURES <ref|inline>", "Whether to include typedef annotations in signature " + 2227 "files. `$ARG_TYPEDEFS_IN_SIGNATURES ref` will include just a reference to the typedef class, " + 2228 "which is not itself part of the API and is not included as a class, and " + 2229 "`$ARG_TYPEDEFS_IN_SIGNATURES inline` will include the constants themselves into each usage " + 2230 "site. You can also supply `$ARG_TYPEDEFS_IN_SIGNATURES none` to explicitly turn it off, if the " + 2231 "default ever changes.", 2232 ARG_IGNORE_CLASSES_ON_CLASSPATH, "Prevents references to classes on the classpath from being added to " + 2233 "the generated stub files.", 2234 2235 "", "\nDocumentation:", 2236 ARG_PUBLIC, "Only include elements that are public", 2237 ARG_PROTECTED, "Only include elements that are public or protected", 2238 ARG_PACKAGE, "Only include elements that are public, protected or package protected", 2239 ARG_PRIVATE, "Include all elements except those that are marked hidden", 2240 ARG_HIDDEN, "Include all elements, including hidden", 2241 2242 "", "\nExtracting Signature Files:", 2243 // TODO: Document --show-annotation! 2244 "$ARG_API <file>", "Generate a signature descriptor file", 2245 "$ARG_PRIVATE_API <file>", "Generate a signature descriptor file listing the exact private APIs", 2246 "$ARG_DEX_API <file>", "Generate a DEX signature descriptor file listing the APIs", 2247 "$ARG_PRIVATE_DEX_API <file>", "Generate a DEX signature descriptor file listing the exact private APIs", 2248 "$ARG_DEX_API_MAPPING <file>", "Generate a DEX signature descriptor along with file and line numbers", 2249 "$ARG_REMOVED_API <file>", "Generate a signature descriptor file for APIs that have been removed", 2250 "$ARG_FORMAT=<v1,v2,v3,...>", "Sets the output signature file format to be the given version.", 2251 "$ARG_OUTPUT_KOTLIN_NULLS[=yes|no]", "Controls whether nullness annotations should be formatted as " + 2252 "in Kotlin (with \"?\" for nullable types, \"\" for non nullable types, and \"!\" for unknown. " + 2253 "The default is yes.", 2254 "$ARG_OUTPUT_DEFAULT_VALUES[=yes|no]", "Controls whether default values should be included in " + 2255 "signature files. The default is yes.", 2256 "$ARG_COMPAT_OUTPUT=[yes|no]", "Controls whether to keep signature files compatible with the " + 2257 "historical format (with its various quirks) or to generate the new format (which will also include " + 2258 "annotations that are part of the API, etc.)", 2259 "$ARG_OMIT_COMMON_PACKAGES[=yes|no]", "Skip common package prefixes like java.lang.* and " + 2260 "kotlin.* in signature files, along with packages for well known annotations like @Nullable and " + 2261 "@NonNull.", 2262 "$ARG_INCLUDE_SIG_VERSION[=yes|no]", "Whether the signature files should include a comment listing " + 2263 "the format version of the signature file.", 2264 2265 "$ARG_PROGUARD <file>", "Write a ProGuard keep file for the API", 2266 "$ARG_SDK_VALUES <dir>", "Write SDK values files to the given directory", 2267 2268 "", "\nGenerating Stubs:", 2269 "$ARG_STUBS <dir>", "Generate stub source files for the API", 2270 "$ARG_DOC_STUBS <dir>", "Generate documentation stub source files for the API. Documentation stub " + 2271 "files are similar to regular stub files, but there are some differences. For example, in " + 2272 "the stub files, we'll use special annotations like @RecentlyNonNull instead of @NonNull to " + 2273 "indicate that an element is recently marked as non null, whereas in the documentation stubs we'll " + 2274 "just list this as @NonNull. Another difference is that @doconly elements are included in " + 2275 "documentation stubs, but not regular stubs, etc.", 2276 ARG_INCLUDE_ANNOTATIONS, "Include annotations such as @Nullable in the stub files.", 2277 ARG_EXCLUDE_ANNOTATIONS, "Exclude annotations such as @Nullable from the stub files; the default.", 2278 "$ARG_PASS_THROUGH_ANNOTATION <annotation classes>", "A comma separated list of fully qualified names of" + 2279 " annotation classes that must be passed through unchanged.", 2280 ARG_EXCLUDE_DOCUMENTATION_FROM_STUBS, "Exclude element documentation (javadoc and kdoc) " + 2281 "from the generated stubs. (Copyright notices are not affected by this, they are always included. " + 2282 "Documentation stubs (--doc-stubs) are not affected.)", 2283 "$ARG_STUBS_SOURCE_LIST <file>", "Write the list of generated stub files into the given source " + 2284 "list file. If generating documentation stubs and you haven't also specified " + 2285 "$ARG_DOC_STUBS_SOURCE_LIST, this list will refer to the documentation stubs; " + 2286 "otherwise it's the non-documentation stubs.", 2287 "$ARG_DOC_STUBS_SOURCE_LIST <file>", "Write the list of generated doc stub files into the given source " + 2288 "list file", 2289 "$ARG_REGISTER_ARTIFACT <api-file> <id>", "Registers the given id for the packages found in " + 2290 "the given signature file. $PROGRAM_NAME will inject an @artifactId <id> tag into every top " + 2291 "level stub class in that API.", 2292 2293 "", "\nDiffs and Checks:", 2294 "$ARG_INPUT_KOTLIN_NULLS[=yes|no]", "Whether the signature file being read should be " + 2295 "interpreted as having encoded its types using Kotlin style types: a suffix of \"?\" for nullable " + 2296 "types, no suffix for non nullable types, and \"!\" for unknown. The default is no.", 2297 "$ARG_CHECK_COMPATIBILITY:type:state <file>", "Check compatibility. Type is one of 'api' " + 2298 "and 'removed', which checks either the public api or the removed api. State is one of " + 2299 "'current' and 'released', to check either the currently in development API or the last publicly " + 2300 "released API, respectively. Different compatibility checks apply in the two scenarios. " + 2301 "For example, to check the code base against the current public API, use " + 2302 "$ARG_CHECK_COMPATIBILITY:api:current.", 2303 "$ARG_API_LINT [api file]", "Check API for Android API best practices. If a signature file is " + 2304 "provided, only the APIs that are new since the API will be checked.", 2305 "$ARG_API_LINT_IGNORE_PREFIX [prefix]", "A list of package prefixes to ignore API issues in " + 2306 "when running with $ARG_API_LINT.", 2307 ARG_CHECK_KOTLIN_INTEROP, "Check API intended to be used from both Kotlin and Java for interoperability " + 2308 "issues", 2309 "$ARG_MIGRATE_NULLNESS <api file>", "Compare nullness information with the previous stable API " + 2310 "and mark newly annotated APIs as under migration.", 2311 ARG_WARNINGS_AS_ERRORS, "Promote all warnings to errors", 2312 ARG_LINTS_AS_ERRORS, "Promote all API lint warnings to errors", 2313 "$ARG_ERROR <id>", "Report issues of the given id as errors", 2314 "$ARG_WARNING <id>", "Report issues of the given id as warnings", 2315 "$ARG_LINT <id>", "Report issues of the given id as having lint-severity", 2316 "$ARG_HIDE <id>", "Hide/skip issues of the given id", 2317 "$ARG_REPORT_EVEN_IF_SUPPRESSED <file>", "Write all issues into the given file, even if suppressed (via annotation or baseline) but not if hidden (by '$ARG_HIDE')", 2318 "$ARG_BASELINE <file>", "Filter out any errors already reported in the given baseline file, or " + 2319 "create if it does not already exist", 2320 "$ARG_UPDATE_BASELINE [file]", "Rewrite the existing baseline file with the current set of warnings. " + 2321 "If some warnings have been fixed, this will delete them from the baseline files. If a file " + 2322 "is provided, the updated baseline is written to the given file; otherwise the original source " + 2323 "baseline file is updated.", 2324 "$ARG_BASELINE_API_LINT <file> $ARG_UPDATE_BASELINE_API_LINT [file]", "Same as $ARG_BASELINE and " + 2325 "$ARG_UPDATE_BASELINE respectively, but used specifically for API lint issues performed by " + 2326 "$ARG_API_LINT.", 2327 "$ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED <file> $ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED [file]", 2328 "Same as $ARG_BASELINE and " + 2329 "$ARG_UPDATE_BASELINE respectively, but used specifically for API compatibility issues performed by " + 2330 "$ARG_CHECK_COMPATIBILITY_API_RELEASED and $ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED.", 2331 "$ARG_MERGE_BASELINE [file]", "Like $ARG_UPDATE_BASELINE, but instead of always replacing entries " + 2332 "in the baseline, it will merge the existing baseline with the new baseline. This is useful " + 2333 "if $PROGRAM_NAME runs multiple times on the same source tree with different flags at different " + 2334 "times, such as occasionally with $ARG_API_LINT.", 2335 ARG_PASS_BASELINE_UPDATES, "Normally, encountering error will fail the build, even when updating " + 2336 "baselines. This flag allows you to tell $PROGRAM_NAME to continue without errors, such that " + 2337 "all the baselines in the source tree can be updated in one go.", 2338 ARG_DELETE_EMPTY_BASELINES, "Whether to delete baseline files if they are updated and there is nothing " + 2339 "to include.", 2340 "$ARG_ERROR_MESSAGE_API_LINT <message>", "If set, $PROGRAM_NAME shows it when errors are detected in $ARG_API_LINT.", 2341 "$ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_RELEASED <message>", "If set, $PROGRAM_NAME shows it " + 2342 " when errors are detected in $ARG_CHECK_COMPATIBILITY_API_RELEASED and $ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED.", 2343 "$ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_CURRENT <message>", "If set, $PROGRAM_NAME shows it " + 2344 " when errors are detected in $ARG_CHECK_COMPATIBILITY_API_CURRENT and $ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT.", 2345 2346 "", "\nJDiff:", 2347 "$ARG_XML_API <file>", "Like $ARG_API, but emits the API in the JDiff XML format instead", 2348 "$ARG_CONVERT_TO_JDIFF <sig> <xml>", "Reads in the given signature file, and writes it out " + 2349 "in the JDiff XML format. Can be specified multiple times.", 2350 "$ARG_CONVERT_NEW_TO_JDIFF <old> <new> <xml>", "Reads in the given old and new api files, " + 2351 "computes the difference, and writes out only the new parts of the API in the JDiff XML format.", 2352 "$ARG_CONVERT_TO_V1 <sig> <sig>", "Reads in the given signature file and writes it out as a " + 2353 "signature file in the original v1/doclava format.", 2354 "$ARG_CONVERT_TO_V2 <sig> <sig>", "Reads in the given signature file and writes it out as a " + 2355 "signature file in the new signature format, v2.", 2356 "$ARG_CONVERT_NEW_TO_V2 <old> <new> <sig>", "Reads in the given old and new api files, " + 2357 "computes the difference, and writes out only the new parts of the API in the v2 format.", 2358 2359 "", "\nStatistics:", 2360 ARG_ANNOTATION_COVERAGE_STATS, "Whether $PROGRAM_NAME should emit coverage statistics for " + 2361 "annotations, listing the percentage of the API that has been annotated with nullness information.", 2362 2363 "$ARG_ANNOTATION_COVERAGE_OF <paths>", "One or more jars (separated by `${File.pathSeparator}`) " + 2364 "containing existing apps that we want to measure annotation coverage statistics for. The set of " + 2365 "API usages in those apps are counted up and the most frequently used APIs that are missing " + 2366 "annotation metadata are listed in descending order.", 2367 2368 ARG_SKIP_JAVA_IN_COVERAGE_REPORT, "In the coverage annotation report, skip java.** and kotlin.** to " + 2369 "narrow the focus down to the Android framework APIs.", 2370 2371 "$ARG_WRITE_CLASS_COVERAGE_TO <path>", "Specifies a file to write the annotation " + 2372 "coverage report for classes to.", 2373 "$ARG_WRITE_MEMBER_COVERAGE_TO <path>", "Specifies a file to write the annotation " + 2374 "coverage report for members to.", 2375 2376 "", "\nExtracting Annotations:", 2377 "$ARG_EXTRACT_ANNOTATIONS <zipfile>", "Extracts source annotations from the source files and writes " + 2378 "them into the given zip file", 2379 "$ARG_INCLUDE_ANNOTATION_CLASSES <dir>", "Copies the given stub annotation source files into the " + 2380 "generated stub sources; <dir> is typically $PROGRAM_NAME/stub-annotations/src/main/java/.", 2381 "$ARG_REWRITE_ANNOTATIONS <dir/jar>", "For a bytecode folder or output jar, rewrites the " + 2382 "androidx annotations to be package private", 2383 "$ARG_FORCE_CONVERT_TO_WARNING_NULLABILITY_ANNOTATIONS <package1:-package2:...>", "On every API declared " + 2384 "in a class referenced by the given filter, makes nullability issues appear to callers as warnings " + 2385 "rather than errors by replacing @Nullable/@NonNull in these APIs with " + 2386 "@RecentlyNullable/@RecentlyNonNull", 2387 "$ARG_COPY_ANNOTATIONS <source> <dest>", "For a source folder full of annotation " + 2388 "sources, generates corresponding package private versions of the same annotations.", 2389 ARG_INCLUDE_SOURCE_RETENTION, "If true, include source-retention annotations in the stub files. Does " + 2390 "not apply to signature files. Source retention annotations are extracted into the external " + 2391 "annotations files instead.", 2392 "", "\nInjecting API Levels:", 2393 "$ARG_APPLY_API_LEVELS <api-versions.xml>", "Reads an XML file containing API level descriptions " + 2394 "and merges the information into the documentation", 2395 2396 "", "\nExtracting API Levels:", 2397 "$ARG_GENERATE_API_LEVELS <xmlfile>", 2398 "Reads android.jar SDK files and generates an XML file recording " + 2399 "the API level for each class, method and field", 2400 "$ARG_ANDROID_JAR_PATTERN <pattern>", "Patterns to use to locate Android JAR files. The default " + 2401 "is \$ANDROID_HOME/platforms/android-%/android.jar.", 2402 ARG_CURRENT_VERSION, "Sets the current API level of the current source code", 2403 ARG_CURRENT_CODENAME, "Sets the code name for the current source code", 2404 ARG_CURRENT_JAR, "Points to the current API jar, if any", 2405 2406 "", "\nSandboxing:", 2407 ARG_NO_IMPLICIT_ROOT, "Disable implicit root directory detection. " + 2408 "Otherwise, $PROGRAM_NAME adds in source roots implied by the source files", 2409 "$ARG_STRICT_INPUT_FILES <file>", "Do not read files that are not explicitly specified in the command line. " + 2410 "All violations are written to the given file. Reads on directories are always allowed, but " + 2411 "$PROGRAM_NAME still tracks reads on directories that are not specified in the command line, " + 2412 "and write them to the file.", 2413 "$ARG_STRICT_INPUT_FILES_WARN <file>", "Warn when files not explicitly specified on the command line are " + 2414 "read. All violations are written to the given file. Reads on directories not specified in the command " + 2415 "line are allowed but also logged.", 2416 "$ARG_STRICT_INPUT_FILES_STACK <file>", "Same as $ARG_STRICT_INPUT_FILES but also print stacktraces.", 2417 "$ARG_STRICT_INPUT_FILES_EXEMPT <files or dirs>", "Used with $ARG_STRICT_INPUT_FILES. Explicitly allow " + 2418 "access to files and/or directories (separated by `${File.pathSeparator}). Can also be " + 2419 "@ followed by a path to a text file containing paths to the full set of files and/or directories.", 2420 2421 "", "\nEnvironment Variables:", 2422 ENV_VAR_METALAVA_DUMP_ARGV, "Set to true to have metalava emit all the arguments it was invoked with. " + 2423 "Helpful when debugging or reproducing under a debugger what the build system is doing.", 2424 ENV_VAR_METALAVA_PREPEND_ARGS, "One or more arguments (concatenated by space) to insert into the " + 2425 "command line, before the documentation flags.", 2426 ENV_VAR_METALAVA_APPEND_ARGS, "One or more arguments (concatenated by space) to append to the " + 2427 "end of the command line, after the generate documentation flags." 2428 ) 2429 2430 val sb = StringBuilder(INDENT_WIDTH) 2431 for (indent in 0 until INDENT_WIDTH) { 2432 sb.append(' ') 2433 } 2434 val indent = sb.toString() 2435 val formatString = "%1$-" + INDENT_WIDTH + "s%2\$s" 2436 2437 var i = 0 2438 while (i < args.size) { 2439 val arg = args[i] 2440 val description = "\n" + args[i + 1] 2441 if (arg.isEmpty()) { 2442 if (colorize) { 2443 out.println(colorized(description, TerminalColor.YELLOW)) 2444 } else { 2445 out.println(description) 2446 } 2447 } else { 2448 if (colorize) { 2449 val colorArg = bold(arg) 2450 val invisibleChars = colorArg.length - arg.length 2451 // +invisibleChars: the extra chars in the above are counted but don't contribute to width 2452 // so allow more space 2453 val colorFormatString = "%1$-" + (INDENT_WIDTH + invisibleChars) + "s%2\$s" 2454 2455 out.print( 2456 wrap( 2457 String.format(colorFormatString, colorArg, description), 2458 MAX_LINE_WIDTH + invisibleChars, MAX_LINE_WIDTH, indent 2459 ) 2460 ) 2461 } else { 2462 out.print( 2463 wrap( 2464 String.format(formatString, arg, description), 2465 MAX_LINE_WIDTH, indent 2466 ) 2467 ) 2468 } 2469 } 2470 i += 2 2471 } 2472 } 2473 2474 companion object { 2475 /** Whether we should use [Compatibility] mode */ 2476 fun useCompatMode(args: Array<String>): Boolean { 2477 return COMPAT_MODE_BY_DEFAULT && !args.contains("$ARG_COMPAT_OUTPUT=no") && 2478 (args.none { it.startsWith("$ARG_FORMAT=") } || args.contains("--format=v1")) 2479 } 2480 2481 private fun setIssueSeverity( 2482 id: String, 2483 severity: Severity, 2484 arg: String 2485 ) { 2486 if (id.contains(",")) { // Handle being passed in multiple comma separated id's 2487 id.split(",").forEach { 2488 setIssueSeverity(it.trim(), severity, arg) 2489 } 2490 return 2491 } 2492 2493 val numericId = try { 2494 id.toInt() 2495 } catch (e: NumberFormatException) { 2496 -1 2497 } 2498 2499 val issue = Issues.findIssueById(id) 2500 ?: Issues.findIssueById(numericId)?.also { 2501 reporter.report( 2502 Issues.DEPRECATED_OPTION, null as File?, 2503 "Issue lookup by numeric id is deprecated, use " + 2504 "$arg ${it.name} instead of $arg $id" 2505 ) 2506 } ?: Issues.findIssueByIdIgnoringCase(id)?.also { 2507 reporter.report( 2508 Issues.DEPRECATED_OPTION, null as File?, 2509 "Case-insensitive issue matching is deprecated, use " + 2510 "$arg ${it.name} instead of $arg $id" 2511 ) 2512 } ?: throw DriverException("Unknown issue id: $arg $id") 2513 2514 defaultConfiguration.setSeverity(issue, severity) 2515 } 2516 } 2517 } 2518