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