blob: fccd1a5529ba55269ef2414c8c112ad456d1053b [file] [log] [blame]
Colin Cross014489c2020-06-02 20:09:13 -07001// 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"
Colin Cross988dfcc2020-07-16 17:32:17 -070020 "strings"
Colin Cross014489c2020-06-02 20:09:13 -070021
Pedro Loureiro5d190cc2021-02-15 15:41:33 +000022 "github.com/google/blueprint/proptools"
23
Colin Cross014489c2020-06-02 20:09:13 -070024 "android/soong/android"
Colin Cross31972dc2021-03-04 10:44:12 -080025 "android/soong/java/config"
26 "android/soong/remoteexec"
Colin Cross014489c2020-06-02 20:09:13 -070027)
28
29type LintProperties struct {
30 // Controls for running Android Lint on the module.
31 Lint struct {
32
33 // If true, run Android Lint on the module. Defaults to true.
34 Enabled *bool
35
36 // Flags to pass to the Android Lint tool.
37 Flags []string
38
39 // Checks that should be treated as fatal.
40 Fatal_checks []string
41
42 // Checks that should be treated as errors.
43 Error_checks []string
44
45 // Checks that should be treated as warnings.
46 Warning_checks []string
47
48 // Checks that should be skipped.
49 Disabled_checks []string
Colin Cross92e4b462020-06-18 15:56:48 -070050
51 // Modules that provide extra lint checks
52 Extra_check_modules []string
Pedro Loureiro5d190cc2021-02-15 15:41:33 +000053
54 // Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
55 Baseline_filename *string
Colin Cross014489c2020-06-02 20:09:13 -070056 }
57}
58
59type linter struct {
60 name string
61 manifest android.Path
62 mergedManifest android.Path
63 srcs android.Paths
64 srcJars android.Paths
65 resources android.Paths
66 classpath android.Paths
67 classes android.Path
68 extraLintCheckJars android.Paths
69 test bool
70 library bool
71 minSdkVersion string
72 targetSdkVersion string
73 compileSdkVersion string
74 javaLanguageLevel string
75 kotlinLanguageLevel string
76 outputs lintOutputs
77 properties LintProperties
Colin Crossc0efd1d2020-07-03 11:56:24 -070078
Colin Cross08dca382020-07-21 20:31:17 -070079 reports android.Paths
80
Colin Crossc0efd1d2020-07-03 11:56:24 -070081 buildModuleReportZip bool
Colin Cross014489c2020-06-02 20:09:13 -070082}
83
84type lintOutputs struct {
Colin Cross08dca382020-07-21 20:31:17 -070085 html android.Path
86 text android.Path
87 xml android.Path
Colin Crossc0efd1d2020-07-03 11:56:24 -070088
Colin Cross08dca382020-07-21 20:31:17 -070089 depSets LintDepSets
Colin Crossc0efd1d2020-07-03 11:56:24 -070090}
91
Colin Cross08dca382020-07-21 20:31:17 -070092type lintOutputsIntf interface {
Colin Crossc0efd1d2020-07-03 11:56:24 -070093 lintOutputs() *lintOutputs
94}
95
Colin Cross08dca382020-07-21 20:31:17 -070096type lintDepSetsIntf interface {
97 LintDepSets() LintDepSets
98}
99
100type LintDepSets struct {
101 HTML, Text, XML *android.DepSet
102}
103
104type LintDepSetsBuilder struct {
105 HTML, Text, XML *android.DepSetBuilder
106}
107
108func NewLintDepSetBuilder() LintDepSetsBuilder {
109 return LintDepSetsBuilder{
110 HTML: android.NewDepSetBuilder(android.POSTORDER),
111 Text: android.NewDepSetBuilder(android.POSTORDER),
112 XML: android.NewDepSetBuilder(android.POSTORDER),
113 }
114}
115
116func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
117 l.HTML.Direct(html)
118 l.Text.Direct(text)
119 l.XML.Direct(xml)
120 return l
121}
122
123func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
124 if depSets.HTML != nil {
125 l.HTML.Transitive(depSets.HTML)
126 }
127 if depSets.Text != nil {
128 l.Text.Transitive(depSets.Text)
129 }
130 if depSets.XML != nil {
131 l.XML.Transitive(depSets.XML)
132 }
133 return l
134}
135
136func (l LintDepSetsBuilder) Build() LintDepSets {
137 return LintDepSets{
138 HTML: l.HTML.Build(),
139 Text: l.Text.Build(),
140 XML: l.XML.Build(),
141 }
142}
143
144func (l *linter) LintDepSets() LintDepSets {
145 return l.outputs.depSets
146}
147
148var _ lintDepSetsIntf = (*linter)(nil)
149
150var _ lintOutputsIntf = (*linter)(nil)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700151
152func (l *linter) lintOutputs() *lintOutputs {
153 return &l.outputs
Colin Cross014489c2020-06-02 20:09:13 -0700154}
155
156func (l *linter) enabled() bool {
157 return BoolDefault(l.properties.Lint.Enabled, true)
158}
159
Colin Cross92e4b462020-06-18 15:56:48 -0700160func (l *linter) deps(ctx android.BottomUpMutatorContext) {
161 if !l.enabled() {
162 return
163 }
164
Colin Cross988dfcc2020-07-16 17:32:17 -0700165 extraCheckModules := l.properties.Lint.Extra_check_modules
166
167 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
168 if checkOnlyModules := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); checkOnlyModules != "" {
169 extraCheckModules = strings.Split(checkOnlyModules, ",")
170 }
171 }
172
173 ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
174 extraLintCheckTag, extraCheckModules...)
Colin Cross92e4b462020-06-18 15:56:48 -0700175}
176
Colin Crossad22bc22021-03-10 09:45:40 -0800177// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
178// around.
Colin Cross31972dc2021-03-04 10:44:12 -0800179type lintPaths struct {
180 projectXML android.WritablePath
181 configXML android.WritablePath
182 cacheDir android.WritablePath
183 homeDir android.WritablePath
184 srcjarDir android.WritablePath
185
186 deps android.Paths
187
188 remoteInputs android.Paths
189 remoteRSPInputs android.Paths
190}
191
Colin Cross9b93af42021-03-10 10:40:58 -0800192func lintRBEExecStrategy(ctx android.ModuleContext) string {
193 return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
194}
195
Colin Cross31972dc2021-03-04 10:44:12 -0800196func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
197 var deps android.Paths
198 var remoteInputs android.Paths
199 var remoteRSPInputs android.Paths
200
201 // Paths passed to trackInputDependency will be added as dependencies of the rule that runs
202 // lint and passed as inputs to the remote execution proxy.
203 trackInputDependency := func(paths ...android.Path) {
204 deps = append(deps, paths...)
205 remoteInputs = append(remoteInputs, paths...)
206 }
207
208 // Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
209 // lint, but the RSP file will be used by the remote execution proxy to find the files so that
210 // it doesn't overflow command line limits.
211 trackRSPDependency := func(paths android.Paths, rsp android.Path) {
212 deps = append(deps, paths...)
213 remoteRSPInputs = append(remoteRSPInputs, rsp)
214 }
Colin Cross014489c2020-06-02 20:09:13 -0700215
216 var resourcesList android.WritablePath
217 if len(l.resources) > 0 {
218 // The list of resources may be too long to put on the command line, but
219 // we can't use the rsp file because it is already being used for srcs.
220 // Insert a second rule to write out the list of resources to a file.
221 resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
Colin Crossf1a035e2020-11-16 17:32:30 -0800222 resListRule := android.NewRuleBuilder(pctx, ctx)
Colin Cross70c47412021-03-12 17:48:14 -0800223 resListRule.Command().Text("cp").
224 FlagWithRspFileInputList("", resourcesList.ReplaceExtension(ctx, "rsp"), l.resources).
225 Output(resourcesList)
Colin Crossf1a035e2020-11-16 17:32:30 -0800226 resListRule.Build("lint_resources_list", "lint resources list")
Colin Cross31972dc2021-03-04 10:44:12 -0800227 trackRSPDependency(l.resources, resourcesList)
Colin Cross014489c2020-06-02 20:09:13 -0700228 }
229
Colin Cross31972dc2021-03-04 10:44:12 -0800230 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
Colin Cross014489c2020-06-02 20:09:13 -0700231 // Lint looks for a lint.xml file next to the project.xml file, give it one.
Colin Cross31972dc2021-03-04 10:44:12 -0800232 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
233 cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
234 homeDir := android.PathForModuleOut(ctx, "lint", "home")
Colin Cross014489c2020-06-02 20:09:13 -0700235
236 srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
237 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
Colin Cross31972dc2021-03-04 10:44:12 -0800238 // TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
239 // by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
240 // not the extracted files.
241 trackRSPDependency(l.srcJars, srcJarList)
242
243 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
244 // lint separately.
245 srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
Colin Cross70c47412021-03-12 17:48:14 -0800246 srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
247 rule.Command().Text("cp").
248 FlagWithRspFileInputList("", srcsListRsp, l.srcs).
249 Output(srcsList)
Colin Cross31972dc2021-03-04 10:44:12 -0800250 trackRSPDependency(l.srcs, srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700251
252 cmd := rule.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800253 BuiltTool("lint-project-xml").
Colin Cross014489c2020-06-02 20:09:13 -0700254 FlagWithOutput("--project_out ", projectXMLPath).
255 FlagWithOutput("--config_out ", configXMLPath).
256 FlagWithArg("--name ", ctx.ModuleName())
257
258 if l.library {
259 cmd.Flag("--library")
260 }
261 if l.test {
262 cmd.Flag("--test")
263 }
264 if l.manifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700265 cmd.FlagWithArg("--manifest ", l.manifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800266 trackInputDependency(l.manifest)
Colin Cross014489c2020-06-02 20:09:13 -0700267 }
268 if l.mergedManifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700269 cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800270 trackInputDependency(l.mergedManifest)
Colin Cross014489c2020-06-02 20:09:13 -0700271 }
272
Colin Cross31972dc2021-03-04 10:44:12 -0800273 cmd.FlagWithInput("--srcs ", srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700274
275 cmd.FlagWithInput("--generated_srcs ", srcJarList)
Colin Cross014489c2020-06-02 20:09:13 -0700276
277 if resourcesList != nil {
278 cmd.FlagWithInput("--resources ", resourcesList)
279 }
280
281 if l.classes != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700282 cmd.FlagWithArg("--classes ", l.classes.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800283 trackInputDependency(l.classes)
Colin Cross014489c2020-06-02 20:09:13 -0700284 }
285
286 cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800287 trackInputDependency(l.classpath...)
Colin Cross014489c2020-06-02 20:09:13 -0700288
289 cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800290 trackInputDependency(l.extraLintCheckJars...)
Colin Cross014489c2020-06-02 20:09:13 -0700291
Colin Cross9b93af42021-03-10 10:40:58 -0800292 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") &&
293 lintRBEExecStrategy(ctx) != remoteexec.LocalExecStrategy {
Colin Cross31972dc2021-03-04 10:44:12 -0800294 // TODO(b/181912787): remove these and use "." instead.
295 cmd.FlagWithArg("--root_dir ", "/b/f/w")
296 } else {
297 cmd.FlagWithArg("--root_dir ", "$PWD")
298 }
Colin Crossc31efeb2020-06-23 10:25:26 -0700299
300 // The cache tag in project.xml is relative to the root dir, or the project.xml file if
301 // the root dir is not set.
302 cmd.FlagWithArg("--cache_dir ", cacheDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700303
304 cmd.FlagWithInput("@",
305 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
306
307 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
308 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
309 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
310 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
311
Colin Cross31972dc2021-03-04 10:44:12 -0800312 return lintPaths{
313 projectXML: projectXMLPath,
314 configXML: configXMLPath,
315 cacheDir: cacheDir,
316 homeDir: homeDir,
317
318 deps: deps,
319
320 remoteInputs: remoteInputs,
321 remoteRSPInputs: remoteRSPInputs,
322 }
323
Colin Cross014489c2020-06-02 20:09:13 -0700324}
325
Liz Kammer20ebfb42020-07-28 11:32:07 -0700326// generateManifest adds a command to the rule to write a simple manifest that contains the
Colin Cross014489c2020-06-02 20:09:13 -0700327// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
328func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.Path {
329 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
330
331 rule.Command().Text("(").
332 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
333 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
334 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
335 Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
336 l.minSdkVersion, l.targetSdkVersion).
337 Text(`echo "</manifest>"`).
338 Text(") >").Output(manifestPath)
339
340 return manifestPath
341}
342
343func (l *linter) lint(ctx android.ModuleContext) {
344 if !l.enabled() {
345 return
346 }
347
Colin Cross92e4b462020-06-18 15:56:48 -0700348 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
349 for _, extraLintCheckModule := range extraLintCheckModules {
Colin Crossdcf71b22021-02-01 13:59:03 -0800350 if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
351 dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
352 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
Colin Cross92e4b462020-06-18 15:56:48 -0700353 } else {
354 ctx.PropertyErrorf("lint.extra_check_modules",
355 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
356 }
357 }
358
Colin Crossf1a035e2020-11-16 17:32:30 -0800359 rule := android.NewRuleBuilder(pctx, ctx)
Colin Cross014489c2020-06-02 20:09:13 -0700360
361 if l.manifest == nil {
362 manifest := l.generateManifest(ctx, rule)
363 l.manifest = manifest
364 }
365
Colin Cross31972dc2021-03-04 10:44:12 -0800366 lintPaths := l.writeLintProjectXML(ctx, rule)
Colin Cross014489c2020-06-02 20:09:13 -0700367
Colin Crossc0efd1d2020-07-03 11:56:24 -0700368 html := android.PathForModuleOut(ctx, "lint-report.html")
369 text := android.PathForModuleOut(ctx, "lint-report.txt")
370 xml := android.PathForModuleOut(ctx, "lint-report.xml")
371
Colin Cross08dca382020-07-21 20:31:17 -0700372 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700373
374 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
Colin Cross08dca382020-07-21 20:31:17 -0700375 if depLint, ok := dep.(lintDepSetsIntf); ok {
376 depSetsBuilder.Transitive(depLint.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700377 }
378 })
Colin Cross014489c2020-06-02 20:09:13 -0700379
Colin Cross31972dc2021-03-04 10:44:12 -0800380 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
381 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross5c113d12021-03-04 10:01:34 -0800382 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
Colin Cross014489c2020-06-02 20:09:13 -0700383
Colin Cross8a6ed372020-07-06 11:45:51 -0700384 var annotationsZipPath, apiVersionsXMLPath android.Path
Jeongik Cha816a23a2020-07-08 01:09:23 +0900385 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross8a6ed372020-07-06 11:45:51 -0700386 annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
387 apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
388 } else {
389 annotationsZipPath = copiedAnnotationsZipPath(ctx)
390 apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
391 }
392
Colin Cross31972dc2021-03-04 10:44:12 -0800393 cmd := rule.Command()
394
395 cmd.Flag("JAVA_OPTS=-Xmx3072m").
396 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
Colin Cross8a6ed372020-07-06 11:45:51 -0700397 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
Colin Cross31972dc2021-03-04 10:44:12 -0800398 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
399
400 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
401 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
402 // TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml
403 // is removed.
Colin Cross9b93af42021-03-10 10:40:58 -0800404 execStrategy := lintRBEExecStrategy(ctx)
Colin Cross31972dc2021-03-04 10:44:12 -0800405 labels := map[string]string{"type": "tool", "name": "lint"}
406 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
407 remoteInputs := lintPaths.remoteInputs
408 remoteInputs = append(remoteInputs,
409 lintPaths.projectXML,
410 lintPaths.configXML,
411 lintPaths.homeDir,
412 lintPaths.cacheDir,
413 ctx.Config().HostJavaToolPath(ctx, "lint.jar"),
414 annotationsZipPath,
415 apiVersionsXMLPath,
416 )
417
418 cmd.Text((&remoteexec.REParams{
419 Labels: labels,
420 ExecStrategy: execStrategy,
421 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
422 Inputs: remoteInputs.Strings(),
423 OutputFiles: android.Paths{html, text, xml}.Strings(),
424 RSPFile: strings.Join(lintPaths.remoteRSPInputs.Strings(), ","),
425 EnvironmentVariables: []string{
426 "JAVA_OPTS",
427 "ANDROID_SDK_HOME",
428 "SDK_ANNOTATIONS",
429 "LINT_OPTS",
Colin Cross9c78cb82021-03-10 18:00:51 -0800430 "LANG",
Colin Cross31972dc2021-03-04 10:44:12 -0800431 },
432 Platform: map[string]string{remoteexec.PoolKey: pool},
433 }).NoVarTemplate(ctx.Config()))
434 }
435
436 cmd.BuiltTool("lint").
Colin Cross014489c2020-06-02 20:09:13 -0700437 Flag("--quiet").
Colin Cross31972dc2021-03-04 10:44:12 -0800438 FlagWithInput("--project ", lintPaths.projectXML).
439 FlagWithInput("--config ", lintPaths.configXML).
Colin Crossc0efd1d2020-07-03 11:56:24 -0700440 FlagWithOutput("--html ", html).
441 FlagWithOutput("--text ", text).
442 FlagWithOutput("--xml ", xml).
Colin Cross014489c2020-06-02 20:09:13 -0700443 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
444 FlagWithArg("--java-language-level ", l.javaLanguageLevel).
445 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
446 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
447 Flag("--exitcode").
448 Flags(l.properties.Lint.Flags).
Colin Cross31972dc2021-03-04 10:44:12 -0800449 Implicit(annotationsZipPath).
450 Implicit(apiVersionsXMLPath).
451 Implicits(lintPaths.deps)
Colin Cross988dfcc2020-07-16 17:32:17 -0700452
453 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
454 cmd.FlagWithArg("--check ", checkOnly)
455 }
456
Pedro Loureiro5d190cc2021-02-15 15:41:33 +0000457 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
458 var lintBaseline android.OptionalPath
459 if String(l.properties.Lint.Baseline_filename) != "" {
460 // if manually specified, we require the file to exist
461 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
462 } else {
463 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
464 }
465 if lintBaseline.Valid() {
466 cmd.FlagWithInput("--baseline ", lintBaseline.Path())
467 }
468 }
469
Colin Cross31972dc2021-03-04 10:44:12 -0800470 cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
Colin Cross014489c2020-06-02 20:09:13 -0700471
Colin Cross31972dc2021-03-04 10:44:12 -0800472 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700473
Colin Crossf1a035e2020-11-16 17:32:30 -0800474 rule.Build("lint", "lint")
Colin Cross014489c2020-06-02 20:09:13 -0700475
Colin Crossc0efd1d2020-07-03 11:56:24 -0700476 l.outputs = lintOutputs{
477 html: html,
478 text: text,
479 xml: xml,
Colin Cross014489c2020-06-02 20:09:13 -0700480
Colin Cross08dca382020-07-21 20:31:17 -0700481 depSets: depSetsBuilder.Build(),
Colin Crossc0efd1d2020-07-03 11:56:24 -0700482 }
Colin Cross014489c2020-06-02 20:09:13 -0700483
Colin Crossc0efd1d2020-07-03 11:56:24 -0700484 if l.buildModuleReportZip {
Colin Cross08dca382020-07-21 20:31:17 -0700485 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700486 }
487}
Colin Cross014489c2020-06-02 20:09:13 -0700488
Colin Cross08dca382020-07-21 20:31:17 -0700489func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
490 htmlList := depSets.HTML.ToSortedList()
491 textList := depSets.Text.ToSortedList()
492 xmlList := depSets.XML.ToSortedList()
493
494 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
495 return nil
496 }
497
498 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
499 lintZip(ctx, htmlList, htmlZip)
500
501 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
502 lintZip(ctx, textList, textZip)
503
504 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
505 lintZip(ctx, xmlList, xmlZip)
506
507 return android.Paths{htmlZip, textZip, xmlZip}
508}
509
Colin Cross014489c2020-06-02 20:09:13 -0700510type lintSingleton struct {
511 htmlZip android.WritablePath
512 textZip android.WritablePath
513 xmlZip android.WritablePath
514}
515
516func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
517 l.generateLintReportZips(ctx)
518 l.copyLintDependencies(ctx)
519}
520
521func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
Jeongik Cha816a23a2020-07-08 01:09:23 +0900522 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross014489c2020-06-02 20:09:13 -0700523 return
524 }
525
526 var frameworkDocStubs android.Module
527 ctx.VisitAllModules(func(m android.Module) {
528 if ctx.ModuleName(m) == "framework-doc-stubs" {
529 if frameworkDocStubs == nil {
530 frameworkDocStubs = m
531 } else {
532 ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
533 ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
534 }
535 }
536 })
537
538 if frameworkDocStubs == nil {
539 if !ctx.Config().AllowMissingDependencies() {
540 ctx.Errorf("lint: missing framework-doc-stubs")
541 }
542 return
543 }
544
545 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800546 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700547 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700548 Output: copiedAnnotationsZipPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700549 })
550
551 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800552 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700553 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700554 Output: copiedAPIVersionsXmlPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700555 })
556}
557
Colin Cross8a6ed372020-07-06 11:45:51 -0700558func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700559 return android.PathForOutput(ctx, "lint", "annotations.zip")
560}
561
Colin Cross8a6ed372020-07-06 11:45:51 -0700562func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700563 return android.PathForOutput(ctx, "lint", "api_versions.xml")
564}
565
566func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700567 if ctx.Config().UnbundledBuild() {
568 return
569 }
570
Colin Cross014489c2020-06-02 20:09:13 -0700571 var outputs []*lintOutputs
572 var dirs []string
573 ctx.VisitAllModules(func(m android.Module) {
Jingwen Chencda22c92020-11-23 00:22:30 -0500574 if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
Colin Cross014489c2020-06-02 20:09:13 -0700575 return
576 }
577
Colin Cross56a83212020-09-15 18:30:11 -0700578 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
579 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
580 if apexInfo.IsForPlatform() {
581 // There are stray platform variants of modules in apexes that are not available for
582 // the platform, and they sometimes can't be built. Don't depend on them.
583 return
584 }
Colin Cross014489c2020-06-02 20:09:13 -0700585 }
586
Colin Cross08dca382020-07-21 20:31:17 -0700587 if l, ok := m.(lintOutputsIntf); ok {
Colin Cross014489c2020-06-02 20:09:13 -0700588 outputs = append(outputs, l.lintOutputs())
589 }
590 })
591
592 dirs = android.SortedUniqueStrings(dirs)
593
594 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
595 var paths android.Paths
596
597 for _, output := range outputs {
Colin Cross08dca382020-07-21 20:31:17 -0700598 if p := get(output); p != nil {
599 paths = append(paths, p)
600 }
Colin Cross014489c2020-06-02 20:09:13 -0700601 }
602
Colin Crossc0efd1d2020-07-03 11:56:24 -0700603 lintZip(ctx, paths, outputPath)
Colin Cross014489c2020-06-02 20:09:13 -0700604 }
605
606 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
607 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
608
609 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
610 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
611
612 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
613 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
614
615 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
616}
617
618func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700619 if !ctx.Config().UnbundledBuild() {
620 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
621 }
Colin Cross014489c2020-06-02 20:09:13 -0700622}
623
624var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
625
626func init() {
627 android.RegisterSingletonType("lint",
628 func() android.Singleton { return &lintSingleton{} })
629}
Colin Crossc0efd1d2020-07-03 11:56:24 -0700630
631func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
632 paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
633
634 sort.Slice(paths, func(i, j int) bool {
635 return paths[i].String() < paths[j].String()
636 })
637
Colin Crossf1a035e2020-11-16 17:32:30 -0800638 rule := android.NewRuleBuilder(pctx, ctx)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700639
Colin Crossf1a035e2020-11-16 17:32:30 -0800640 rule.Command().BuiltTool("soong_zip").
Colin Crossc0efd1d2020-07-03 11:56:24 -0700641 FlagWithOutput("-o ", outputPath).
642 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
Colin Cross70c47412021-03-12 17:48:14 -0800643 FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700644
Colin Crossf1a035e2020-11-16 17:32:30 -0800645 rule.Build(outputPath.Base(), outputPath.Base())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700646}