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