1// Copyright 2020 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package java 16 17import ( 18 "fmt" 19 "sort" 20 "strconv" 21 "strings" 22 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/java/config" 27 "android/soong/remoteexec" 28) 29 30// lint checks automatically enforced for modules that have different min_sdk_version than 31// sdk_version 32var updatabilityChecks = []string{"NewApi"} 33 34type LintProperties struct { 35 // Controls for running Android Lint on the module. 36 Lint struct { 37 38 // If true, run Android Lint on the module. Defaults to true. 39 Enabled *bool 40 41 // Flags to pass to the Android Lint tool. 42 Flags []string 43 44 // Checks that should be treated as fatal. 45 Fatal_checks []string 46 47 // Checks that should be treated as errors. 48 Error_checks []string 49 50 // Checks that should be treated as warnings. 51 Warning_checks []string 52 53 // Checks that should be skipped. 54 Disabled_checks []string 55 56 // Modules that provide extra lint checks 57 Extra_check_modules []string 58 59 // Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml". 60 Baseline_filename *string 61 62 // If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false. 63 Strict_updatability_linting *bool 64 65 // Treat the code in this module as test code for @VisibleForTesting enforcement. 66 // This will be true by default for test module types, false otherwise. 67 // If soong gets support for testonly, this flag should be replaced with that. 68 Test *bool 69 } 70} 71 72type linter struct { 73 name string 74 manifest android.Path 75 mergedManifest android.Path 76 srcs android.Paths 77 srcJars android.Paths 78 resources android.Paths 79 classpath android.Paths 80 classes android.Path 81 extraLintCheckJars android.Paths 82 library bool 83 minSdkVersion int 84 targetSdkVersion int 85 compileSdkVersion int 86 compileSdkKind android.SdkKind 87 javaLanguageLevel string 88 kotlinLanguageLevel string 89 outputs lintOutputs 90 properties LintProperties 91 extraMainlineLintErrors []string 92 93 reports android.Paths 94 95 buildModuleReportZip bool 96} 97 98type lintOutputs struct { 99 html android.Path 100 text android.Path 101 xml android.Path 102 referenceBaseline android.Path 103 104 depSets LintDepSets 105} 106 107type lintOutputsIntf interface { 108 lintOutputs() *lintOutputs 109} 110 111type LintDepSetsIntf interface { 112 LintDepSets() LintDepSets 113 114 // Methods used to propagate strict_updatability_linting values. 115 GetStrictUpdatabilityLinting() bool 116 SetStrictUpdatabilityLinting(bool) 117} 118 119type LintDepSets struct { 120 HTML, Text, XML *android.DepSet 121} 122 123type LintDepSetsBuilder struct { 124 HTML, Text, XML *android.DepSetBuilder 125} 126 127func NewLintDepSetBuilder() LintDepSetsBuilder { 128 return LintDepSetsBuilder{ 129 HTML: android.NewDepSetBuilder(android.POSTORDER), 130 Text: android.NewDepSetBuilder(android.POSTORDER), 131 XML: android.NewDepSetBuilder(android.POSTORDER), 132 } 133} 134 135func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder { 136 l.HTML.Direct(html) 137 l.Text.Direct(text) 138 l.XML.Direct(xml) 139 return l 140} 141 142func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder { 143 if depSets.HTML != nil { 144 l.HTML.Transitive(depSets.HTML) 145 } 146 if depSets.Text != nil { 147 l.Text.Transitive(depSets.Text) 148 } 149 if depSets.XML != nil { 150 l.XML.Transitive(depSets.XML) 151 } 152 return l 153} 154 155func (l LintDepSetsBuilder) Build() LintDepSets { 156 return LintDepSets{ 157 HTML: l.HTML.Build(), 158 Text: l.Text.Build(), 159 XML: l.XML.Build(), 160 } 161} 162 163type lintDatabaseFiles struct { 164 apiVersionsModule string 165 apiVersionsCopiedName string 166 apiVersionsPrebuiltPath string 167 annotationsModule string 168 annotationCopiedName string 169 annotationPrebuiltpath string 170} 171 172var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{ 173 android.SdkPublic: { 174 apiVersionsModule: "api_versions_public", 175 apiVersionsCopiedName: "api_versions_public.xml", 176 apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml", 177 annotationsModule: "sdk-annotations.zip", 178 annotationCopiedName: "annotations-public.zip", 179 annotationPrebuiltpath: "prebuilts/sdk/current/public/data/annotations.zip", 180 }, 181 android.SdkSystem: { 182 apiVersionsModule: "api_versions_system", 183 apiVersionsCopiedName: "api_versions_system.xml", 184 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml", 185 annotationsModule: "sdk-annotations-system.zip", 186 annotationCopiedName: "annotations-system.zip", 187 annotationPrebuiltpath: "prebuilts/sdk/current/system/data/annotations.zip", 188 }, 189 android.SdkModule: { 190 apiVersionsModule: "api_versions_module_lib", 191 apiVersionsCopiedName: "api_versions_module_lib.xml", 192 apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml", 193 annotationsModule: "sdk-annotations-module-lib.zip", 194 annotationCopiedName: "annotations-module-lib.zip", 195 annotationPrebuiltpath: "prebuilts/sdk/current/module-lib/data/annotations.zip", 196 }, 197 android.SdkSystemServer: { 198 apiVersionsModule: "api_versions_system_server", 199 apiVersionsCopiedName: "api_versions_system_server.xml", 200 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml", 201 annotationsModule: "sdk-annotations-system-server.zip", 202 annotationCopiedName: "annotations-system-server.zip", 203 annotationPrebuiltpath: "prebuilts/sdk/current/system-server/data/annotations.zip", 204 }, 205} 206 207func (l *linter) LintDepSets() LintDepSets { 208 return l.outputs.depSets 209} 210 211func (l *linter) GetStrictUpdatabilityLinting() bool { 212 return BoolDefault(l.properties.Lint.Strict_updatability_linting, false) 213} 214 215func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) { 216 l.properties.Lint.Strict_updatability_linting = &strictLinting 217} 218 219var _ LintDepSetsIntf = (*linter)(nil) 220 221var _ lintOutputsIntf = (*linter)(nil) 222 223func (l *linter) lintOutputs() *lintOutputs { 224 return &l.outputs 225} 226 227func (l *linter) enabled() bool { 228 return BoolDefault(l.properties.Lint.Enabled, true) 229} 230 231func (l *linter) deps(ctx android.BottomUpMutatorContext) { 232 if !l.enabled() { 233 return 234 } 235 236 extraCheckModules := l.properties.Lint.Extra_check_modules 237 238 if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" { 239 extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...) 240 } 241 242 ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), 243 extraLintCheckTag, extraCheckModules...) 244} 245 246// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them 247// around. 248type lintPaths struct { 249 projectXML android.WritablePath 250 configXML android.WritablePath 251 cacheDir android.WritablePath 252 homeDir android.WritablePath 253 srcjarDir android.WritablePath 254} 255 256func lintRBEExecStrategy(ctx android.ModuleContext) string { 257 return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy) 258} 259 260func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths { 261 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") 262 // Lint looks for a lint.xml file next to the project.xml file, give it one. 263 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") 264 cacheDir := android.PathForModuleOut(ctx, "lint", "cache") 265 homeDir := android.PathForModuleOut(ctx, "lint", "home") 266 267 srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars") 268 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) 269 270 cmd := rule.Command(). 271 BuiltTool("lint_project_xml"). 272 FlagWithOutput("--project_out ", projectXMLPath). 273 FlagWithOutput("--config_out ", configXMLPath). 274 FlagWithArg("--name ", ctx.ModuleName()) 275 276 if l.library { 277 cmd.Flag("--library") 278 } 279 if proptools.BoolDefault(l.properties.Lint.Test, false) { 280 cmd.Flag("--test") 281 } 282 if l.manifest != nil { 283 cmd.FlagWithInput("--manifest ", l.manifest) 284 } 285 if l.mergedManifest != nil { 286 cmd.FlagWithInput("--merged_manifest ", l.mergedManifest) 287 } 288 289 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to 290 // lint separately. 291 cmd.FlagWithInput("--srcs ", srcsList) 292 293 cmd.FlagWithInput("--generated_srcs ", srcJarList) 294 295 if len(l.resources) > 0 { 296 resourcesList := android.PathForModuleOut(ctx, "lint-resources.list") 297 cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources) 298 } 299 300 if l.classes != nil { 301 cmd.FlagWithInput("--classes ", l.classes) 302 } 303 304 cmd.FlagForEachInput("--classpath ", l.classpath) 305 306 cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars) 307 308 cmd.FlagWithArg("--root_dir ", "$PWD") 309 310 // The cache tag in project.xml is relative to the root dir, or the project.xml file if 311 // the root dir is not set. 312 cmd.FlagWithArg("--cache_dir ", cacheDir.String()) 313 314 cmd.FlagWithInput("@", 315 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt")) 316 317 if l.compileSdkKind == android.SdkPublic { 318 cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors) 319 } else { 320 // TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed. 321 cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors) 322 } 323 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks) 324 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks) 325 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) 326 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) 327 328 // TODO(b/193460475): Re-enable strict updatability linting 329 //if l.GetStrictUpdatabilityLinting() { 330 // // Verify the module does not baseline issues that endanger safe updatability. 331 // if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() { 332 // cmd.FlagWithInput("--baseline ", baselinePath.Path()) 333 // cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks) 334 // } 335 //} 336 337 return lintPaths{ 338 projectXML: projectXMLPath, 339 configXML: configXMLPath, 340 cacheDir: cacheDir, 341 homeDir: homeDir, 342 } 343 344} 345 346// generateManifest adds a command to the rule to write a simple manifest that contains the 347// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest. 348func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath { 349 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml") 350 351 rule.Command().Text("("). 352 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`). 353 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`). 354 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`). 355 Textf(`echo " <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`, 356 l.minSdkVersion, l.targetSdkVersion). 357 Text(`echo "</manifest>"`). 358 Text(") >").Output(manifestPath) 359 360 return manifestPath 361} 362 363func (l *linter) getBaselineFilepath(ctx android.ModuleContext) android.OptionalPath { 364 var lintBaseline android.OptionalPath 365 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" { 366 if String(l.properties.Lint.Baseline_filename) != "" { 367 // if manually specified, we require the file to exist 368 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename)) 369 } else { 370 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename) 371 } 372 } 373 return lintBaseline 374} 375 376func (l *linter) lint(ctx android.ModuleContext) { 377 if !l.enabled() { 378 return 379 } 380 381 if l.minSdkVersion != l.compileSdkVersion { 382 l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...) 383 // Skip lint warning checks for NewApi warnings for libcore where they come from source 384 // files that reference the API they are adding (b/208656169). 385 if !strings.HasPrefix(ctx.ModuleDir(), "libcore") { 386 _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks) 387 388 if len(filtered) != 0 { 389 ctx.PropertyErrorf("lint.warning_checks", 390 "Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered) 391 } 392 } 393 394 _, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks) 395 if len(filtered) != 0 { 396 ctx.PropertyErrorf("lint.disabled_checks", 397 "Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered) 398 } 399 400 // TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController 401 if ctx.ModuleName() == "PermissionController" { 402 l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool { 403 return s != "NewApi" 404 }) 405 l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi") 406 } 407 } 408 409 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag) 410 for _, extraLintCheckModule := range extraLintCheckModules { 411 if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) { 412 dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo) 413 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...) 414 } else { 415 ctx.PropertyErrorf("lint.extra_check_modules", 416 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule)) 417 } 418 } 419 420 l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx, 421 "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar")) 422 423 rule := android.NewRuleBuilder(pctx, ctx). 424 Sbox(android.PathForModuleOut(ctx, "lint"), 425 android.PathForModuleOut(ctx, "lint.sbox.textproto")). 426 SandboxInputs() 427 428 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") { 429 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16") 430 rule.Remoteable(android.RemoteRuleSupports{RBE: true}) 431 rule.Rewrapper(&remoteexec.REParams{ 432 Labels: map[string]string{"type": "tool", "name": "lint"}, 433 ExecStrategy: lintRBEExecStrategy(ctx), 434 ToolchainInputs: []string{config.JavaCmd(ctx).String()}, 435 Platform: map[string]string{remoteexec.PoolKey: pool}, 436 }) 437 } 438 439 if l.manifest == nil { 440 manifest := l.generateManifest(ctx, rule) 441 l.manifest = manifest 442 rule.Temporary(manifest) 443 } 444 445 srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list") 446 srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") 447 rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList) 448 449 lintPaths := l.writeLintProjectXML(ctx, rule, srcsList) 450 451 html := android.PathForModuleOut(ctx, "lint", "lint-report.html") 452 text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") 453 xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") 454 referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") 455 456 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml) 457 458 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) { 459 if depLint, ok := dep.(LintDepSetsIntf); ok { 460 depSetsBuilder.Transitive(depLint.LintDepSets()) 461 } 462 }) 463 464 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 465 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 466 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) 467 468 files, ok := allLintDatabasefiles[l.compileSdkKind] 469 if !ok { 470 files = allLintDatabasefiles[android.SdkPublic] 471 } 472 var annotationsZipPath, apiVersionsXMLPath android.Path 473 if ctx.Config().AlwaysUsePrebuiltSdks() { 474 annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath) 475 apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath) 476 } else { 477 annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName) 478 apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName) 479 } 480 481 cmd := rule.Command() 482 483 cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`). 484 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()). 485 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath). 486 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath) 487 488 cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")). 489 Flag("--quiet"). 490 FlagWithInput("--project ", lintPaths.projectXML). 491 FlagWithInput("--config ", lintPaths.configXML). 492 FlagWithOutput("--html ", html). 493 FlagWithOutput("--text ", text). 494 FlagWithOutput("--xml ", xml). 495 FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)). 496 FlagWithArg("--java-language-level ", l.javaLanguageLevel). 497 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel). 498 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). 499 Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox 500 Flags(l.properties.Lint.Flags). 501 Implicit(annotationsZipPath). 502 Implicit(apiVersionsXMLPath) 503 504 rule.Temporary(lintPaths.projectXML) 505 rule.Temporary(lintPaths.configXML) 506 507 if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" { 508 cmd.Flag("--exitcode") 509 } 510 511 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" { 512 cmd.FlagWithArg("--check ", checkOnly) 513 } 514 515 lintBaseline := l.getBaselineFilepath(ctx) 516 if lintBaseline.Valid() { 517 cmd.FlagWithInput("--baseline ", lintBaseline.Path()) 518 } 519 520 cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline) 521 522 cmd.Text("; EXITCODE=$?; ") 523 524 // The sources in the sandbox may have been modified by --apply-suggestions, zip them up and 525 // export them out of the sandbox. Do this before exiting so that the suggestions exit even after 526 // a fatal error. 527 cmd.BuiltTool("soong_zip"). 528 FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")). 529 FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))). 530 FlagWithInput("-r ", srcsList) 531 532 cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi") 533 534 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 535 536 // The HTML output contains a date, remove it to make the output deterministic. 537 rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html) 538 539 rule.Build("lint", "lint") 540 541 l.outputs = lintOutputs{ 542 html: html, 543 text: text, 544 xml: xml, 545 referenceBaseline: referenceBaseline, 546 547 depSets: depSetsBuilder.Build(), 548 } 549 550 if l.buildModuleReportZip { 551 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets()) 552 } 553} 554 555func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths { 556 htmlList := depSets.HTML.ToSortedList() 557 textList := depSets.Text.ToSortedList() 558 xmlList := depSets.XML.ToSortedList() 559 560 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 { 561 return nil 562 } 563 564 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip") 565 lintZip(ctx, htmlList, htmlZip) 566 567 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip") 568 lintZip(ctx, textList, textZip) 569 570 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip") 571 lintZip(ctx, xmlList, xmlZip) 572 573 return android.Paths{htmlZip, textZip, xmlZip} 574} 575 576type lintSingleton struct { 577 htmlZip android.WritablePath 578 textZip android.WritablePath 579 xmlZip android.WritablePath 580 referenceBaselineZip android.WritablePath 581} 582 583func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) { 584 l.generateLintReportZips(ctx) 585 l.copyLintDependencies(ctx) 586} 587 588func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module { 589 var res android.Module 590 ctx.VisitAllModules(func(m android.Module) { 591 if ctx.ModuleName(m) == moduleName { 592 if res == nil { 593 res = m 594 } else { 595 ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName, 596 ctx.ModuleSubDir(m), ctx.ModuleSubDir(res)) 597 } 598 } 599 }) 600 return res 601} 602 603func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { 604 if ctx.Config().AlwaysUsePrebuiltSdks() { 605 return 606 } 607 608 for _, sdk := range android.SortedKeys(allLintDatabasefiles) { 609 files := allLintDatabasefiles[sdk] 610 apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule) 611 if apiVersionsDb == nil { 612 if !ctx.Config().AllowMissingDependencies() { 613 ctx.Errorf("lint: missing module api_versions_public") 614 } 615 return 616 } 617 618 sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule) 619 if sdkAnnotations == nil { 620 if !ctx.Config().AllowMissingDependencies() { 621 ctx.Errorf("lint: missing module sdk-annotations.zip") 622 } 623 return 624 } 625 626 ctx.Build(pctx, android.BuildParams{ 627 Rule: android.CpIfChanged, 628 Input: android.OutputFileForModule(ctx, sdkAnnotations, ""), 629 Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName), 630 }) 631 632 ctx.Build(pctx, android.BuildParams{ 633 Rule: android.CpIfChanged, 634 Input: android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"), 635 Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName), 636 }) 637 } 638} 639 640func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath { 641 return android.PathForOutput(ctx, "lint", name) 642} 643 644func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { 645 if ctx.Config().UnbundledBuild() { 646 return 647 } 648 649 var outputs []*lintOutputs 650 var dirs []string 651 ctx.VisitAllModules(func(m android.Module) { 652 if ctx.Config().KatiEnabled() && !m.ExportedToMake() { 653 return 654 } 655 656 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() { 657 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo) 658 if apexInfo.IsForPlatform() { 659 // There are stray platform variants of modules in apexes that are not available for 660 // the platform, and they sometimes can't be built. Don't depend on them. 661 return 662 } 663 } 664 665 if l, ok := m.(lintOutputsIntf); ok { 666 outputs = append(outputs, l.lintOutputs()) 667 } 668 }) 669 670 dirs = android.SortedUniqueStrings(dirs) 671 672 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) { 673 var paths android.Paths 674 675 for _, output := range outputs { 676 if p := get(output); p != nil { 677 paths = append(paths, p) 678 } 679 } 680 681 lintZip(ctx, paths, outputPath) 682 } 683 684 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip") 685 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html }) 686 687 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip") 688 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text }) 689 690 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip") 691 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml }) 692 693 l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip") 694 zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline }) 695 696 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip) 697} 698 699func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) { 700 if !ctx.Config().UnbundledBuild() { 701 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip) 702 } 703} 704 705var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil) 706 707func init() { 708 android.RegisterSingletonType("lint", 709 func() android.Singleton { return &lintSingleton{} }) 710 711 registerLintBuildComponents(android.InitRegistrationContext) 712} 713 714func registerLintBuildComponents(ctx android.RegistrationContext) { 715 ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { 716 ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel() 717 }) 718} 719 720func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) { 721 paths = android.SortedUniquePaths(android.CopyOfPaths(paths)) 722 723 sort.Slice(paths, func(i, j int) bool { 724 return paths[i].String() < paths[j].String() 725 }) 726 727 rule := android.NewRuleBuilder(pctx, ctx) 728 729 rule.Command().BuiltTool("soong_zip"). 730 FlagWithOutput("-o ", outputPath). 731 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). 732 FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths) 733 734 rule.Build(outputPath.Base(), outputPath.Base()) 735} 736 737// Enforce the strict updatability linting to all applicable transitive dependencies. 738func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) { 739 m := ctx.Module() 740 if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() { 741 ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { 742 if a, ok := d.(LintDepSetsIntf); ok { 743 a.SetStrictUpdatabilityLinting(true) 744 } 745 }) 746 } 747} 748