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" 23 "github.com/google/blueprint/depset" 24 "github.com/google/blueprint/proptools" 25 26 "android/soong/android" 27 "android/soong/java/config" 28 "android/soong/remoteexec" 29) 30 31// lint checks automatically enforced for modules that have different min_sdk_version than 32// sdk_version 33var updatabilityChecks = []string{"NewApi"} 34 35type LintProperties struct { 36 // Controls for running Android Lint on the module. 37 Lint struct { 38 39 // If true, run Android Lint on the module. Defaults to true. 40 Enabled *bool 41 42 // Flags to pass to the Android Lint tool. 43 Flags []string 44 45 // Checks that should be treated as fatal. 46 Fatal_checks []string 47 48 // Checks that should be treated as errors. 49 Error_checks []string 50 51 // Checks that should be treated as warnings. 52 Warning_checks []string 53 54 // Checks that should be skipped. 55 Disabled_checks []string 56 57 // Modules that provide extra lint checks 58 Extra_check_modules []string 59 60 // The lint baseline file to use. If specified, lint warnings listed in this file will be 61 // suppressed during lint checks. 62 Baseline_filename *string 63 64 // If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false. 65 Strict_updatability_linting *bool 66 67 // Treat the code in this module as test code for @VisibleForTesting enforcement. 68 // This will be true by default for test module types, false otherwise. 69 // If soong gets support for testonly, this flag should be replaced with that. 70 Test *bool 71 72 // Whether to ignore the exit code of Android lint. This is the --exit_code 73 // option. Defaults to false. 74 Suppress_exit_code *bool 75 } 76} 77 78type linter struct { 79 name string 80 manifest android.Path 81 mergedManifest android.Path 82 srcs android.Paths 83 srcJars android.Paths 84 resources android.Paths 85 classpath android.Paths 86 classes android.Path 87 extraLintCheckJars android.Paths 88 library bool 89 minSdkVersion android.ApiLevel 90 targetSdkVersion android.ApiLevel 91 compileSdkVersion android.ApiLevel 92 compileSdkKind android.SdkKind 93 javaLanguageLevel string 94 kotlinLanguageLevel string 95 properties LintProperties 96 extraMainlineLintErrors []string 97 compile_data android.Paths 98 99 reports android.Paths 100 101 buildModuleReportZip bool 102} 103 104type LintDepSets struct { 105 HTML, Text, XML, Baseline depset.DepSet[android.Path] 106} 107 108type LintDepSetsBuilder struct { 109 HTML, Text, XML, Baseline *depset.Builder[android.Path] 110} 111 112func NewLintDepSetBuilder() LintDepSetsBuilder { 113 return LintDepSetsBuilder{ 114 HTML: depset.NewBuilder[android.Path](depset.POSTORDER), 115 Text: depset.NewBuilder[android.Path](depset.POSTORDER), 116 XML: depset.NewBuilder[android.Path](depset.POSTORDER), 117 Baseline: depset.NewBuilder[android.Path](depset.POSTORDER), 118 } 119} 120 121func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder { 122 l.HTML.Direct(html) 123 l.Text.Direct(text) 124 l.XML.Direct(xml) 125 if baseline.Valid() { 126 l.Baseline.Direct(baseline.Path()) 127 } 128 return l 129} 130 131func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder { 132 l.HTML.Transitive(info.TransitiveHTML) 133 l.Text.Transitive(info.TransitiveText) 134 l.XML.Transitive(info.TransitiveXML) 135 l.Baseline.Transitive(info.TransitiveBaseline) 136 return l 137} 138 139func (l LintDepSetsBuilder) Build() LintDepSets { 140 return LintDepSets{ 141 HTML: l.HTML.Build(), 142 Text: l.Text.Build(), 143 XML: l.XML.Build(), 144 Baseline: l.Baseline.Build(), 145 } 146} 147 148type lintDatabaseFiles struct { 149 apiVersionsModule string 150 apiVersionsCopiedName string 151 apiVersionsPrebuiltPath string 152 annotationsModule string 153 annotationCopiedName string 154 annotationPrebuiltpath string 155} 156 157var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{ 158 android.SdkPublic: { 159 apiVersionsModule: "api_versions_public", 160 apiVersionsCopiedName: "api_versions_public.xml", 161 apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml", 162 annotationsModule: "sdk-annotations.zip", 163 annotationCopiedName: "annotations-public.zip", 164 annotationPrebuiltpath: "prebuilts/sdk/current/public/data/annotations.zip", 165 }, 166 android.SdkSystem: { 167 apiVersionsModule: "api_versions_system", 168 apiVersionsCopiedName: "api_versions_system.xml", 169 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml", 170 annotationsModule: "sdk-annotations-system.zip", 171 annotationCopiedName: "annotations-system.zip", 172 annotationPrebuiltpath: "prebuilts/sdk/current/system/data/annotations.zip", 173 }, 174 android.SdkModule: { 175 apiVersionsModule: "api_versions_module_lib", 176 apiVersionsCopiedName: "api_versions_module_lib.xml", 177 apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml", 178 annotationsModule: "sdk-annotations-module-lib.zip", 179 annotationCopiedName: "annotations-module-lib.zip", 180 annotationPrebuiltpath: "prebuilts/sdk/current/module-lib/data/annotations.zip", 181 }, 182 android.SdkSystemServer: { 183 apiVersionsModule: "api_versions_system_server", 184 apiVersionsCopiedName: "api_versions_system_server.xml", 185 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml", 186 annotationsModule: "sdk-annotations-system-server.zip", 187 annotationCopiedName: "annotations-system-server.zip", 188 annotationPrebuiltpath: "prebuilts/sdk/current/system-server/data/annotations.zip", 189 }, 190} 191 192var LintProvider = blueprint.NewProvider[*LintInfo]() 193 194type LintInfo struct { 195 HTML android.Path 196 Text android.Path 197 XML android.Path 198 ReferenceBaseline android.Path 199 200 TransitiveHTML depset.DepSet[android.Path] 201 TransitiveText depset.DepSet[android.Path] 202 TransitiveXML depset.DepSet[android.Path] 203 TransitiveBaseline depset.DepSet[android.Path] 204} 205 206func (l *linter) enabled() bool { 207 return BoolDefault(l.properties.Lint.Enabled, true) 208} 209 210func (l *linter) deps(ctx android.BottomUpMutatorContext) { 211 if !l.enabled() { 212 return 213 } 214 215 extraCheckModules := l.properties.Lint.Extra_check_modules 216 217 if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" { 218 extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...) 219 } 220 221 ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), 222 extraLintCheckTag, extraCheckModules...) 223} 224 225// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them 226// around. 227type lintPaths struct { 228 projectXML android.WritablePath 229 configXML android.WritablePath 230 cacheDir android.WritablePath 231 homeDir android.WritablePath 232 srcjarDir android.WritablePath 233} 234 235func lintRBEExecStrategy(ctx android.ModuleContext) string { 236 return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy) 237} 238 239func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path, 240 baselines android.Paths) lintPaths { 241 242 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") 243 // Lint looks for a lint.xml file next to the project.xml file, give it one. 244 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") 245 cacheDir := android.PathForModuleOut(ctx, "lint", "cache") 246 homeDir := android.PathForModuleOut(ctx, "lint", "home") 247 248 srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars") 249 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) 250 251 cmd := rule.Command(). 252 BuiltTool("lint_project_xml"). 253 FlagWithOutput("--project_out ", projectXMLPath). 254 FlagWithOutput("--config_out ", configXMLPath). 255 FlagWithArg("--name ", ctx.ModuleName()) 256 257 if l.library { 258 cmd.Flag("--library") 259 } 260 if proptools.BoolDefault(l.properties.Lint.Test, false) { 261 cmd.Flag("--test") 262 } 263 if l.manifest != nil { 264 cmd.FlagWithInput("--manifest ", l.manifest) 265 } 266 if l.mergedManifest != nil { 267 cmd.FlagWithInput("--merged_manifest ", l.mergedManifest) 268 } 269 270 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to 271 // lint separately. 272 cmd.FlagWithInput("--srcs ", srcsList) 273 274 cmd.FlagWithInput("--generated_srcs ", srcJarList) 275 276 if len(l.resources) > 0 { 277 resourcesList := android.PathForModuleOut(ctx, "lint-resources.list") 278 cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources) 279 } 280 281 if l.classes != nil { 282 cmd.FlagWithInput("--classes ", l.classes) 283 } 284 285 cmd.FlagForEachInput("--classpath ", l.classpath) 286 287 cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars) 288 289 cmd.FlagWithArg("--root_dir ", "$PWD") 290 291 // The cache tag in project.xml is relative to the root dir, or the project.xml file if 292 // the root dir is not set. 293 cmd.FlagWithArg("--cache_dir ", cacheDir.String()) 294 295 cmd.FlagWithInput("@", 296 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt")) 297 298 cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors) 299 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks) 300 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks) 301 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) 302 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) 303 304 if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 { 305 // Verify the module does not baseline issues that endanger safe updatability. 306 strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines) 307 cmd.Validation(strictUpdatabilityChecksOutputFile) 308 } 309 310 return lintPaths{ 311 projectXML: projectXMLPath, 312 configXML: configXMLPath, 313 cacheDir: cacheDir, 314 homeDir: homeDir, 315 } 316 317} 318 319func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path { 320 rule := android.NewRuleBuilder(pctx, ctx) 321 baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp") 322 outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp") 323 rule.Command().Text("rm -f").Output(outputFile) 324 rule.Command(). 325 BuiltTool("lint_strict_updatability_checks"). 326 FlagWithArg("--name ", ctx.ModuleName()). 327 FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines). 328 FlagForEachArg("--disallowed_issues ", updatabilityChecks) 329 rule.Command().Text("touch").Output(outputFile) 330 rule.Build("lint_strict_updatability_checks", "lint strict updatability checks") 331 332 return outputFile 333} 334 335// generateManifest adds a command to the rule to write a simple manifest that contains the 336// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest. 337func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath { 338 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml") 339 340 rule.Command().Text("("). 341 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`). 342 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`). 343 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`). 344 Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`, 345 l.minSdkVersion.String(), l.targetSdkVersion.String()). 346 Text(`echo "</manifest>"`). 347 Text(") >").Output(manifestPath) 348 349 return manifestPath 350} 351 352func (l *linter) lint(ctx android.ModuleContext) { 353 if !l.enabled() { 354 return 355 } 356 357 for _, flag := range l.properties.Lint.Flags { 358 if strings.Contains(flag, "--disable") || strings.Contains(flag, "--enable") || strings.Contains(flag, "--check") { 359 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") 360 } 361 } 362 363 if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 { 364 l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...) 365 // Skip lint warning checks for NewApi warnings for libcore where they come from source 366 // files that reference the API they are adding (b/208656169). 367 if !strings.HasPrefix(ctx.ModuleDir(), "libcore") { 368 _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks) 369 370 if len(filtered) != 0 { 371 ctx.PropertyErrorf("lint.warning_checks", 372 "Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered) 373 } 374 } 375 376 _, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks) 377 if len(filtered) != 0 { 378 ctx.PropertyErrorf("lint.disabled_checks", 379 "Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered) 380 } 381 382 // TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController 383 if ctx.ModuleName() == "PermissionController" { 384 l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool { 385 return s != "NewApi" 386 }) 387 l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi") 388 } 389 } 390 391 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag) 392 for _, extraLintCheckModule := range extraLintCheckModules { 393 if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok { 394 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...) 395 } else { 396 ctx.PropertyErrorf("lint.extra_check_modules", 397 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule)) 398 } 399 } 400 401 l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx, 402 "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar")) 403 404 var baseline android.OptionalPath 405 if l.properties.Lint.Baseline_filename != nil { 406 baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) 407 } 408 409 html := android.PathForModuleOut(ctx, "lint", "lint-report.html") 410 text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") 411 xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") 412 referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") 413 414 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline) 415 416 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) { 417 if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok { 418 depSetsBuilder.Transitive(info) 419 } 420 }) 421 422 depSets := depSetsBuilder.Build() 423 424 rule := android.NewRuleBuilder(pctx, ctx). 425 Sbox(android.PathForModuleOut(ctx, "lint"), 426 android.PathForModuleOut(ctx, "lint.sbox.textproto")). 427 SandboxInputs() 428 429 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") { 430 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16") 431 rule.Remoteable(android.RemoteRuleSupports{RBE: true}) 432 rule.Rewrapper(&remoteexec.REParams{ 433 Labels: map[string]string{"type": "tool", "name": "lint"}, 434 ExecStrategy: lintRBEExecStrategy(ctx), 435 ToolchainInputs: []string{config.JavaCmd(ctx).String()}, 436 Platform: map[string]string{remoteexec.PoolKey: pool}, 437 }) 438 } 439 440 if l.manifest == nil { 441 manifest := l.generateManifest(ctx, rule) 442 l.manifest = manifest 443 rule.Temporary(manifest) 444 } 445 446 srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list") 447 srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") 448 rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data) 449 450 baselines := depSets.Baseline.ToList() 451 452 lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines) 453 454 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 455 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 456 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) 457 458 files, ok := allLintDatabasefiles[l.compileSdkKind] 459 if !ok { 460 files = allLintDatabasefiles[android.SdkPublic] 461 } 462 var annotationsZipPath, apiVersionsXMLPath android.Path 463 if ctx.Config().AlwaysUsePrebuiltSdks() { 464 annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath) 465 apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath) 466 } else { 467 annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName) 468 apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName) 469 } 470 471 cmd := rule.Command() 472 473 cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`). 474 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()). 475 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath). 476 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath) 477 478 cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")). 479 Flag("--quiet"). 480 Flag("--include-aosp-issues"). 481 FlagWithInput("--project ", lintPaths.projectXML). 482 FlagWithInput("--config ", lintPaths.configXML). 483 FlagWithOutput("--html ", html). 484 FlagWithOutput("--text ", text). 485 FlagWithOutput("--xml ", xml). 486 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()). 487 FlagWithArg("--java-language-level ", l.javaLanguageLevel). 488 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel). 489 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). 490 Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox 491 Flags(l.properties.Lint.Flags). 492 Implicit(annotationsZipPath). 493 Implicit(apiVersionsXMLPath) 494 495 rule.Temporary(lintPaths.projectXML) 496 rule.Temporary(lintPaths.configXML) 497 498 suppressExitCode := BoolDefault(l.properties.Lint.Suppress_exit_code, false) 499 if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" && !suppressExitCode { 500 cmd.Flag("--exitcode") 501 } 502 503 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" { 504 cmd.FlagWithArg("--check ", checkOnly) 505 } 506 507 if baseline.Valid() { 508 cmd.FlagWithInput("--baseline ", baseline.Path()) 509 } 510 511 cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline) 512 513 cmd.Text("; EXITCODE=$?; ") 514 515 // The sources in the sandbox may have been modified by --apply-suggestions, zip them up and 516 // export them out of the sandbox. Do this before exiting so that the suggestions exit even after 517 // a fatal error. 518 cmd.BuiltTool("soong_zip"). 519 FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")). 520 FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))). 521 FlagWithInput("-r ", srcsList) 522 523 cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi") 524 525 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 526 527 // The HTML output contains a date, remove it to make the output deterministic. 528 rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html) 529 530 rule.Build("lint", "lint") 531 532 android.SetProvider(ctx, LintProvider, &LintInfo{ 533 HTML: html, 534 Text: text, 535 XML: xml, 536 ReferenceBaseline: referenceBaseline, 537 538 TransitiveHTML: depSets.HTML, 539 TransitiveText: depSets.Text, 540 TransitiveXML: depSets.XML, 541 TransitiveBaseline: depSets.Baseline, 542 }) 543 544 if l.buildModuleReportZip { 545 l.reports = BuildModuleLintReportZips(ctx, depSets, nil) 546 } 547 548 // Create a per-module phony target to run the lint check. 549 phonyName := ctx.ModuleName() + "-lint" 550 ctx.Phony(phonyName, xml) 551 552 ctx.SetOutputFiles(android.Paths{xml}, ".lint") 553} 554 555func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths { 556 htmlList := android.SortedUniquePaths(depSets.HTML.ToList()) 557 textList := android.SortedUniquePaths(depSets.Text.ToList()) 558 xmlList := android.SortedUniquePaths(depSets.XML.ToList()) 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, validations) 566 567 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip") 568 lintZip(ctx, textList, textZip, validations) 569 570 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip") 571 lintZip(ctx, xmlList, xmlZip, validations) 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 %s", files.apiVersionsModule) 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 %s", files.annotationsModule) 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 []*LintInfo 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, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) 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 lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok { 666 outputs = append(outputs, lintInfo) 667 } 668 }) 669 670 dirs = android.SortedUniqueStrings(dirs) 671 672 zip := func(outputPath android.WritablePath, get func(*LintInfo) 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, nil) 682 } 683 684 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip") 685 zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML }) 686 687 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip") 688 zip(l.textZip, func(l *LintInfo) android.Path { return l.Text }) 689 690 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip") 691 zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML }) 692 693 l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip") 694 zip(l.referenceBaselineZip, func(l *LintInfo) 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.RegisterParallelSingletonType("lint", 709 func() android.Singleton { return &lintSingleton{} }) 710} 711 712func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) { 713 paths = android.SortedUniquePaths(android.CopyOfPaths(paths)) 714 715 sort.Slice(paths, func(i, j int) bool { 716 return paths[i].String() < paths[j].String() 717 }) 718 719 rule := android.NewRuleBuilder(pctx, ctx) 720 721 rule.Command().BuiltTool("soong_zip"). 722 FlagWithOutput("-o ", outputPath). 723 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). 724 FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths). 725 Validations(validations) 726 727 rule.Build(outputPath.Base(), outputPath.Base()) 728} 729