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