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