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