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 // Same as the regular Test property, but set by internal soong code based on if the module 73 // type is a test module type. This will act as the default value for the test property, 74 // but can be overridden by the user. 75 Test_module_type *bool `blueprint:"mutated"` 76 77 // Whether to ignore the exit code of Android lint. This is the --exit_code 78 // option. Defaults to false. 79 Suppress_exit_code *bool 80 } 81} 82 83type linter struct { 84 name string 85 manifest android.Path 86 mergedManifest android.Path 87 srcs android.Paths 88 srcJars android.Paths 89 resources android.Paths 90 classpath android.Paths 91 classes android.Path 92 extraLintCheckJars android.Paths 93 library bool 94 minSdkVersion android.ApiLevel 95 targetSdkVersion android.ApiLevel 96 compileSdkVersion android.ApiLevel 97 compileSdkKind android.SdkKind 98 javaLanguageLevel string 99 kotlinLanguageLevel string 100 properties LintProperties 101 extraMainlineLintErrors []string 102 compile_data android.Paths 103 104 reports android.Paths 105 106 buildModuleReportZip bool 107} 108 109type LintDepSets struct { 110 HTML, Text, XML, Baseline depset.DepSet[android.Path] 111} 112 113type LintDepSetsBuilder struct { 114 HTML, Text, XML, Baseline *depset.Builder[android.Path] 115} 116 117func NewLintDepSetBuilder() LintDepSetsBuilder { 118 return LintDepSetsBuilder{ 119 HTML: depset.NewBuilder[android.Path](depset.POSTORDER), 120 Text: depset.NewBuilder[android.Path](depset.POSTORDER), 121 XML: depset.NewBuilder[android.Path](depset.POSTORDER), 122 Baseline: depset.NewBuilder[android.Path](depset.POSTORDER), 123 } 124} 125 126func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder { 127 l.HTML.Direct(html) 128 l.Text.Direct(text) 129 l.XML.Direct(xml) 130 if baseline.Valid() { 131 l.Baseline.Direct(baseline.Path()) 132 } 133 return l 134} 135 136func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder { 137 l.HTML.Transitive(info.TransitiveHTML) 138 l.Text.Transitive(info.TransitiveText) 139 l.XML.Transitive(info.TransitiveXML) 140 l.Baseline.Transitive(info.TransitiveBaseline) 141 return l 142} 143 144func (l LintDepSetsBuilder) Build() LintDepSets { 145 return LintDepSets{ 146 HTML: l.HTML.Build(), 147 Text: l.Text.Build(), 148 XML: l.XML.Build(), 149 Baseline: l.Baseline.Build(), 150 } 151} 152 153type lintDatabaseFiles struct { 154 apiVersionsModule string 155 apiVersionsCopiedName string 156 apiVersionsPrebuiltPath string 157 annotationsModule string 158 annotationCopiedName string 159 annotationPrebuiltpath string 160} 161 162var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{ 163 android.SdkPublic: { 164 apiVersionsModule: "api_versions_public", 165 apiVersionsCopiedName: "api_versions_public.xml", 166 apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml", 167 annotationsModule: "sdk-annotations.zip", 168 annotationCopiedName: "annotations-public.zip", 169 annotationPrebuiltpath: "prebuilts/sdk/current/public/data/annotations.zip", 170 }, 171 android.SdkSystem: { 172 apiVersionsModule: "api_versions_system", 173 apiVersionsCopiedName: "api_versions_system.xml", 174 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml", 175 annotationsModule: "sdk-annotations-system.zip", 176 annotationCopiedName: "annotations-system.zip", 177 annotationPrebuiltpath: "prebuilts/sdk/current/system/data/annotations.zip", 178 }, 179 android.SdkModule: { 180 apiVersionsModule: "api_versions_module_lib", 181 apiVersionsCopiedName: "api_versions_module_lib.xml", 182 apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml", 183 annotationsModule: "sdk-annotations-module-lib.zip", 184 annotationCopiedName: "annotations-module-lib.zip", 185 annotationPrebuiltpath: "prebuilts/sdk/current/module-lib/data/annotations.zip", 186 }, 187 android.SdkSystemServer: { 188 apiVersionsModule: "api_versions_system_server", 189 apiVersionsCopiedName: "api_versions_system_server.xml", 190 apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml", 191 annotationsModule: "sdk-annotations-system-server.zip", 192 annotationCopiedName: "annotations-system-server.zip", 193 annotationPrebuiltpath: "prebuilts/sdk/current/system-server/data/annotations.zip", 194 }, 195} 196 197var LintProvider = blueprint.NewProvider[*LintInfo]() 198 199type LintInfo struct { 200 HTML android.Path 201 Text android.Path 202 XML android.Path 203 ReferenceBaseline android.Path 204 205 TransitiveHTML depset.DepSet[android.Path] 206 TransitiveText depset.DepSet[android.Path] 207 TransitiveXML depset.DepSet[android.Path] 208 TransitiveBaseline depset.DepSet[android.Path] 209} 210 211func (l *linter) enabled() bool { 212 return BoolDefault(l.properties.Lint.Enabled, true) 213} 214 215func (l *linter) deps(ctx android.BottomUpMutatorContext) { 216 if !l.enabled() { 217 return 218 } 219 220 extraCheckModules := l.properties.Lint.Extra_check_modules 221 222 if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" { 223 extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...) 224 } 225 226 ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), 227 extraLintCheckTag, extraCheckModules...) 228} 229 230// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them 231// around. 232type lintPaths struct { 233 projectXML android.WritablePath 234 configXML android.WritablePath 235 cacheDir android.WritablePath 236 homeDir android.WritablePath 237 srcjarDir android.WritablePath 238} 239 240func lintRBEExecStrategy(ctx android.ModuleContext) string { 241 return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy) 242} 243 244func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path, 245 baselines android.Paths) lintPaths { 246 247 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") 248 // Lint looks for a lint.xml file next to the project.xml file, give it one. 249 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") 250 cacheDir := android.PathForModuleOut(ctx, "lint", "cache") 251 homeDir := android.PathForModuleOut(ctx, "lint", "home") 252 253 srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars") 254 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) 255 256 cmd := rule.Command(). 257 BuiltTool("lint_project_xml"). 258 FlagWithOutput("--project_out ", projectXMLPath). 259 FlagWithOutput("--config_out ", configXMLPath). 260 FlagWithArg("--name ", ctx.ModuleName()) 261 262 if l.library { 263 cmd.Flag("--library") 264 } 265 266 test := proptools.BoolDefault(l.properties.Lint.Test_module_type, false) 267 if l.properties.Lint.Test != nil { 268 test = *l.properties.Lint.Test 269 } 270 if test { 271 cmd.Flag("--test") 272 } 273 if l.manifest != nil { 274 cmd.FlagWithInput("--manifest ", l.manifest) 275 } 276 if l.mergedManifest != nil { 277 cmd.FlagWithInput("--merged_manifest ", l.mergedManifest) 278 } 279 280 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to 281 // lint separately. 282 cmd.FlagWithInput("--srcs ", srcsList) 283 284 cmd.FlagWithInput("--generated_srcs ", srcJarList) 285 286 if len(l.resources) > 0 { 287 resourcesList := android.PathForModuleOut(ctx, "lint-resources.list") 288 cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources) 289 } 290 291 if l.classes != nil { 292 cmd.FlagWithInput("--classes ", l.classes) 293 } 294 295 cmd.FlagForEachInput("--classpath ", l.classpath) 296 297 cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars) 298 299 cmd.FlagWithArg("--root_dir ", "$PWD") 300 301 // The cache tag in project.xml is relative to the root dir, or the project.xml file if 302 // the root dir is not set. 303 cmd.FlagWithArg("--cache_dir ", cacheDir.String()) 304 305 cmd.FlagWithInput("@", 306 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt")) 307 308 cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors) 309 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks) 310 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks) 311 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) 312 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) 313 314 if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 { 315 // Verify the module does not baseline issues that endanger safe updatability. 316 strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines) 317 cmd.Validation(strictUpdatabilityChecksOutputFile) 318 } 319 320 return lintPaths{ 321 projectXML: projectXMLPath, 322 configXML: configXMLPath, 323 cacheDir: cacheDir, 324 homeDir: homeDir, 325 } 326 327} 328 329func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path { 330 rule := android.NewRuleBuilder(pctx, ctx) 331 baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp") 332 outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp") 333 rule.Command().Text("rm -f").Output(outputFile) 334 rule.Command(). 335 BuiltTool("lint_strict_updatability_checks"). 336 FlagWithArg("--name ", ctx.ModuleName()). 337 FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines). 338 FlagForEachArg("--disallowed_issues ", updatabilityChecks) 339 rule.Command().Text("touch").Output(outputFile) 340 rule.Build("lint_strict_updatability_checks", "lint strict updatability checks") 341 342 return outputFile 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.GetDirectDepsProxyWithTag(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 var baseline android.OptionalPath 415 if l.properties.Lint.Baseline_filename != nil { 416 baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) 417 } 418 419 html := android.PathForModuleOut(ctx, "lint", "lint-report.html") 420 text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") 421 xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") 422 referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") 423 424 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline) 425 426 ctx.VisitDirectDepsProxyWithTag(staticLibTag, func(dep android.ModuleProxy) { 427 if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok { 428 depSetsBuilder.Transitive(info) 429 } 430 }) 431 432 depSets := depSetsBuilder.Build() 433 434 rule := android.NewRuleBuilder(pctx, ctx). 435 Sbox(android.PathForModuleOut(ctx, "lint"), 436 android.PathForModuleOut(ctx, "lint.sbox.textproto")). 437 SandboxInputs() 438 439 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") { 440 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16") 441 rule.Remoteable(android.RemoteRuleSupports{RBE: true}) 442 rule.Rewrapper(&remoteexec.REParams{ 443 Labels: map[string]string{"type": "tool", "name": "lint"}, 444 ExecStrategy: lintRBEExecStrategy(ctx), 445 ToolchainInputs: []string{config.JavaCmd(ctx).String()}, 446 Platform: map[string]string{remoteexec.PoolKey: pool}, 447 }) 448 } 449 450 if l.manifest == nil { 451 manifest := l.generateManifest(ctx, rule) 452 l.manifest = manifest 453 rule.Temporary(manifest) 454 } 455 456 srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list") 457 srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") 458 rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data) 459 460 baselines := depSets.Baseline.ToList() 461 462 lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines) 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="-Xmx4096m --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 Flag("--include-aosp-issues"). 491 FlagWithInput("--project ", lintPaths.projectXML). 492 FlagWithInput("--config ", lintPaths.configXML). 493 FlagWithOutput("--html ", html). 494 FlagWithOutput("--text ", text). 495 FlagWithOutput("--xml ", xml). 496 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()). 497 FlagWithArg("--java-language-level ", l.javaLanguageLevel). 498 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel). 499 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). 500 Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox 501 Flags(l.properties.Lint.Flags). 502 Implicit(annotationsZipPath). 503 Implicit(apiVersionsXMLPath) 504 505 rule.Temporary(lintPaths.projectXML) 506 rule.Temporary(lintPaths.configXML) 507 508 suppressExitCode := BoolDefault(l.properties.Lint.Suppress_exit_code, false) 509 if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" && !suppressExitCode { 510 cmd.Flag("--exitcode") 511 } 512 513 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" { 514 cmd.FlagWithArg("--check ", checkOnly) 515 } 516 517 if baseline.Valid() { 518 cmd.FlagWithInput("--baseline ", baseline.Path()) 519 } 520 521 cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline) 522 523 cmd.Text("; EXITCODE=$?; ") 524 525 // The sources in the sandbox may have been modified by --apply-suggestions, zip them up and 526 // export them out of the sandbox. Do this before exiting so that the suggestions exit even after 527 // a fatal error. 528 cmd.BuiltTool("soong_zip"). 529 FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")). 530 FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))). 531 FlagWithInput("-r ", srcsList) 532 533 cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi") 534 535 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) 536 537 // The HTML output contains a date, remove it to make the output deterministic. 538 rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html) 539 540 rule.Build("lint", "lint") 541 542 android.SetProvider(ctx, LintProvider, &LintInfo{ 543 HTML: html, 544 Text: text, 545 XML: xml, 546 ReferenceBaseline: referenceBaseline, 547 548 TransitiveHTML: depSets.HTML, 549 TransitiveText: depSets.Text, 550 TransitiveXML: depSets.XML, 551 TransitiveBaseline: depSets.Baseline, 552 }) 553 554 if l.buildModuleReportZip { 555 l.reports = BuildModuleLintReportZips(ctx, depSets, nil) 556 } 557 558 // Create a per-module phony target to run the lint check. 559 phonyName := ctx.ModuleName() + "-lint" 560 ctx.Phony(phonyName, xml) 561 562 ctx.SetOutputFiles(android.Paths{xml}, ".lint") 563} 564 565func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths { 566 htmlList := android.SortedUniquePaths(depSets.HTML.ToList()) 567 textList := android.SortedUniquePaths(depSets.Text.ToList()) 568 xmlList := android.SortedUniquePaths(depSets.XML.ToList()) 569 570 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 { 571 return nil 572 } 573 574 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip") 575 lintZip(ctx, htmlList, htmlZip, validations) 576 577 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip") 578 lintZip(ctx, textList, textZip, validations) 579 580 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip") 581 lintZip(ctx, xmlList, xmlZip, validations) 582 583 return android.Paths{htmlZip, textZip, xmlZip} 584} 585 586type lintSingleton struct { 587 htmlZip android.WritablePath 588 textZip android.WritablePath 589 xmlZip android.WritablePath 590 referenceBaselineZip android.WritablePath 591} 592 593func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) { 594 l.generateLintReportZips(ctx) 595 l.copyLintDependencies(ctx) 596} 597 598func findModuleOrErr(ctx android.SingletonContext, moduleName string) *android.ModuleProxy { 599 var res *android.ModuleProxy 600 ctx.VisitAllModuleProxies(func(m android.ModuleProxy) { 601 if ctx.ModuleName(m) == moduleName { 602 if res == nil { 603 res = &m 604 } else { 605 ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName, 606 ctx.ModuleSubDir(m), ctx.ModuleSubDir(res)) 607 } 608 } 609 }) 610 return res 611} 612 613func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { 614 if ctx.Config().AlwaysUsePrebuiltSdks() { 615 return 616 } 617 618 for _, sdk := range android.SortedKeys(allLintDatabasefiles) { 619 files := allLintDatabasefiles[sdk] 620 apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule) 621 if apiVersionsDb == nil { 622 if !ctx.Config().AllowMissingDependencies() { 623 ctx.Errorf("lint: missing module %s", files.apiVersionsModule) 624 } 625 return 626 } 627 628 sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule) 629 if sdkAnnotations == nil { 630 if !ctx.Config().AllowMissingDependencies() { 631 ctx.Errorf("lint: missing module %s", files.annotationsModule) 632 } 633 return 634 } 635 636 ctx.Build(pctx, android.BuildParams{ 637 Rule: android.CpIfChanged, 638 Input: android.OutputFileForModule(ctx, *sdkAnnotations, ""), 639 Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName), 640 }) 641 642 ctx.Build(pctx, android.BuildParams{ 643 Rule: android.CpIfChanged, 644 Input: android.OutputFileForModule(ctx, *apiVersionsDb, ".api_versions.xml"), 645 Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName), 646 }) 647 } 648} 649 650func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath { 651 return android.PathForOutput(ctx, "lint", name) 652} 653 654func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { 655 if ctx.Config().UnbundledBuild() { 656 return 657 } 658 659 var outputs []*LintInfo 660 var dirs []string 661 ctx.VisitAllModuleProxies(func(m android.ModuleProxy) { 662 commonInfo := android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider) 663 if ctx.Config().KatiEnabled() && !commonInfo.ExportedToMake { 664 return 665 } 666 667 if commonInfo.IsApexModule && commonInfo.NotAvailableForPlatform { 668 apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) 669 if apexInfo.IsForPlatform() { 670 // There are stray platform variants of modules in apexes that are not available for 671 // the platform, and they sometimes can't be built. Don't depend on them. 672 return 673 } 674 } 675 676 if lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok { 677 outputs = append(outputs, lintInfo) 678 } 679 }) 680 681 dirs = android.SortedUniqueStrings(dirs) 682 683 zip := func(outputPath android.WritablePath, get func(*LintInfo) android.Path) { 684 var paths android.Paths 685 686 for _, output := range outputs { 687 if p := get(output); p != nil { 688 paths = append(paths, p) 689 } 690 } 691 692 lintZip(ctx, paths, outputPath, nil) 693 } 694 695 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip") 696 zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML }) 697 698 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip") 699 zip(l.textZip, func(l *LintInfo) android.Path { return l.Text }) 700 701 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip") 702 zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML }) 703 704 l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip") 705 zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline }) 706 707 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip) 708 709 if !ctx.Config().UnbundledBuild() { 710 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip) 711 } 712} 713 714func init() { 715 android.RegisterParallelSingletonType("lint", 716 func() android.Singleton { return &lintSingleton{} }) 717} 718 719func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) { 720 paths = android.SortedUniquePaths(android.CopyOfPaths(paths)) 721 722 sort.Slice(paths, func(i, j int) bool { 723 return paths[i].String() < paths[j].String() 724 }) 725 726 rule := android.NewRuleBuilder(pctx, ctx) 727 728 rule.Command().BuiltTool("soong_zip"). 729 FlagWithOutput("-o ", outputPath). 730 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). 731 FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths). 732 Validations(validations) 733 734 rule.Build(outputPath.Base(), outputPath.Base()) 735} 736