blob: 9f677db3a8ad87754e7712b6dad1c2e900710108 [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 Cross014489c2020-06-02 20:09:13 -0700223 resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
Colin Crossf1a035e2020-11-16 17:32:30 -0800224 resListRule.Build("lint_resources_list", "lint resources list")
Colin Cross31972dc2021-03-04 10:44:12 -0800225 trackRSPDependency(l.resources, resourcesList)
Colin Cross014489c2020-06-02 20:09:13 -0700226 }
227
Colin Cross31972dc2021-03-04 10:44:12 -0800228 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
Colin Cross014489c2020-06-02 20:09:13 -0700229 // Lint looks for a lint.xml file next to the project.xml file, give it one.
Colin Cross31972dc2021-03-04 10:44:12 -0800230 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
231 cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
232 homeDir := android.PathForModuleOut(ctx, "lint", "home")
Colin Cross014489c2020-06-02 20:09:13 -0700233
234 srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
235 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
Colin Cross31972dc2021-03-04 10:44:12 -0800236 // TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
237 // by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
238 // not the extracted files.
239 trackRSPDependency(l.srcJars, srcJarList)
240
241 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
242 // lint separately.
243 srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
244 rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList)
245 trackRSPDependency(l.srcs, srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700246
247 cmd := rule.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800248 BuiltTool("lint-project-xml").
Colin Cross014489c2020-06-02 20:09:13 -0700249 FlagWithOutput("--project_out ", projectXMLPath).
250 FlagWithOutput("--config_out ", configXMLPath).
251 FlagWithArg("--name ", ctx.ModuleName())
252
253 if l.library {
254 cmd.Flag("--library")
255 }
256 if l.test {
257 cmd.Flag("--test")
258 }
259 if l.manifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700260 cmd.FlagWithArg("--manifest ", l.manifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800261 trackInputDependency(l.manifest)
Colin Cross014489c2020-06-02 20:09:13 -0700262 }
263 if l.mergedManifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700264 cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800265 trackInputDependency(l.mergedManifest)
Colin Cross014489c2020-06-02 20:09:13 -0700266 }
267
Colin Cross31972dc2021-03-04 10:44:12 -0800268 cmd.FlagWithInput("--srcs ", srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700269
270 cmd.FlagWithInput("--generated_srcs ", srcJarList)
Colin Cross014489c2020-06-02 20:09:13 -0700271
272 if resourcesList != nil {
273 cmd.FlagWithInput("--resources ", resourcesList)
274 }
275
276 if l.classes != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700277 cmd.FlagWithArg("--classes ", l.classes.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800278 trackInputDependency(l.classes)
Colin Cross014489c2020-06-02 20:09:13 -0700279 }
280
281 cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800282 trackInputDependency(l.classpath...)
Colin Cross014489c2020-06-02 20:09:13 -0700283
284 cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800285 trackInputDependency(l.extraLintCheckJars...)
Colin Cross014489c2020-06-02 20:09:13 -0700286
Colin Cross9b93af42021-03-10 10:40:58 -0800287 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") &&
288 lintRBEExecStrategy(ctx) != remoteexec.LocalExecStrategy {
Colin Cross31972dc2021-03-04 10:44:12 -0800289 // TODO(b/181912787): remove these and use "." instead.
290 cmd.FlagWithArg("--root_dir ", "/b/f/w")
291 } else {
292 cmd.FlagWithArg("--root_dir ", "$PWD")
293 }
Colin Crossc31efeb2020-06-23 10:25:26 -0700294
295 // The cache tag in project.xml is relative to the root dir, or the project.xml file if
296 // the root dir is not set.
297 cmd.FlagWithArg("--cache_dir ", cacheDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700298
299 cmd.FlagWithInput("@",
300 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
301
302 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
303 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
304 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
305 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
306
Colin Cross31972dc2021-03-04 10:44:12 -0800307 return lintPaths{
308 projectXML: projectXMLPath,
309 configXML: configXMLPath,
310 cacheDir: cacheDir,
311 homeDir: homeDir,
312
313 deps: deps,
314
315 remoteInputs: remoteInputs,
316 remoteRSPInputs: remoteRSPInputs,
317 }
318
Colin Cross014489c2020-06-02 20:09:13 -0700319}
320
Liz Kammer20ebfb42020-07-28 11:32:07 -0700321// generateManifest adds a command to the rule to write a simple manifest that contains the
Colin Cross014489c2020-06-02 20:09:13 -0700322// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
323func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.Path {
324 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
325
326 rule.Command().Text("(").
327 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
328 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
329 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
330 Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
331 l.minSdkVersion, l.targetSdkVersion).
332 Text(`echo "</manifest>"`).
333 Text(") >").Output(manifestPath)
334
335 return manifestPath
336}
337
338func (l *linter) lint(ctx android.ModuleContext) {
339 if !l.enabled() {
340 return
341 }
342
Colin Cross92e4b462020-06-18 15:56:48 -0700343 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
344 for _, extraLintCheckModule := range extraLintCheckModules {
Colin Crossdcf71b22021-02-01 13:59:03 -0800345 if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
346 dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
347 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
Colin Cross92e4b462020-06-18 15:56:48 -0700348 } else {
349 ctx.PropertyErrorf("lint.extra_check_modules",
350 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
351 }
352 }
353
Colin Crossf1a035e2020-11-16 17:32:30 -0800354 rule := android.NewRuleBuilder(pctx, ctx)
Colin Cross014489c2020-06-02 20:09:13 -0700355
356 if l.manifest == nil {
357 manifest := l.generateManifest(ctx, rule)
358 l.manifest = manifest
359 }
360
Colin Cross31972dc2021-03-04 10:44:12 -0800361 lintPaths := l.writeLintProjectXML(ctx, rule)
Colin Cross014489c2020-06-02 20:09:13 -0700362
Colin Crossc0efd1d2020-07-03 11:56:24 -0700363 html := android.PathForModuleOut(ctx, "lint-report.html")
364 text := android.PathForModuleOut(ctx, "lint-report.txt")
365 xml := android.PathForModuleOut(ctx, "lint-report.xml")
366
Colin Cross08dca382020-07-21 20:31:17 -0700367 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700368
369 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
Colin Cross08dca382020-07-21 20:31:17 -0700370 if depLint, ok := dep.(lintDepSetsIntf); ok {
371 depSetsBuilder.Transitive(depLint.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700372 }
373 })
Colin Cross014489c2020-06-02 20:09:13 -0700374
Colin Cross31972dc2021-03-04 10:44:12 -0800375 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
376 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross5c113d12021-03-04 10:01:34 -0800377 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
Colin Cross014489c2020-06-02 20:09:13 -0700378
Colin Cross8a6ed372020-07-06 11:45:51 -0700379 var annotationsZipPath, apiVersionsXMLPath android.Path
Jeongik Cha816a23a2020-07-08 01:09:23 +0900380 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross8a6ed372020-07-06 11:45:51 -0700381 annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
382 apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
383 } else {
384 annotationsZipPath = copiedAnnotationsZipPath(ctx)
385 apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
386 }
387
Colin Cross31972dc2021-03-04 10:44:12 -0800388 cmd := rule.Command()
389
390 cmd.Flag("JAVA_OPTS=-Xmx3072m").
391 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
Colin Cross8a6ed372020-07-06 11:45:51 -0700392 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
Colin Cross31972dc2021-03-04 10:44:12 -0800393 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
394
395 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
396 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
397 // TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml
398 // is removed.
Colin Cross9b93af42021-03-10 10:40:58 -0800399 execStrategy := lintRBEExecStrategy(ctx)
Colin Cross31972dc2021-03-04 10:44:12 -0800400 labels := map[string]string{"type": "tool", "name": "lint"}
401 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
402 remoteInputs := lintPaths.remoteInputs
403 remoteInputs = append(remoteInputs,
404 lintPaths.projectXML,
405 lintPaths.configXML,
406 lintPaths.homeDir,
407 lintPaths.cacheDir,
408 ctx.Config().HostJavaToolPath(ctx, "lint.jar"),
409 annotationsZipPath,
410 apiVersionsXMLPath,
411 )
412
413 cmd.Text((&remoteexec.REParams{
414 Labels: labels,
415 ExecStrategy: execStrategy,
416 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
417 Inputs: remoteInputs.Strings(),
418 OutputFiles: android.Paths{html, text, xml}.Strings(),
419 RSPFile: strings.Join(lintPaths.remoteRSPInputs.Strings(), ","),
420 EnvironmentVariables: []string{
421 "JAVA_OPTS",
422 "ANDROID_SDK_HOME",
423 "SDK_ANNOTATIONS",
424 "LINT_OPTS",
Colin Cross9c78cb82021-03-10 18:00:51 -0800425 "LANG",
Colin Cross31972dc2021-03-04 10:44:12 -0800426 },
427 Platform: map[string]string{remoteexec.PoolKey: pool},
428 }).NoVarTemplate(ctx.Config()))
429 }
430
431 cmd.BuiltTool("lint").
Colin Cross014489c2020-06-02 20:09:13 -0700432 Flag("--quiet").
Colin Cross31972dc2021-03-04 10:44:12 -0800433 FlagWithInput("--project ", lintPaths.projectXML).
434 FlagWithInput("--config ", lintPaths.configXML).
Colin Crossc0efd1d2020-07-03 11:56:24 -0700435 FlagWithOutput("--html ", html).
436 FlagWithOutput("--text ", text).
437 FlagWithOutput("--xml ", xml).
Colin Cross014489c2020-06-02 20:09:13 -0700438 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
439 FlagWithArg("--java-language-level ", l.javaLanguageLevel).
440 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
441 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
442 Flag("--exitcode").
443 Flags(l.properties.Lint.Flags).
Colin Cross31972dc2021-03-04 10:44:12 -0800444 Implicit(annotationsZipPath).
445 Implicit(apiVersionsXMLPath).
446 Implicits(lintPaths.deps)
Colin Cross988dfcc2020-07-16 17:32:17 -0700447
448 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
449 cmd.FlagWithArg("--check ", checkOnly)
450 }
451
Pedro Loureiro5d190cc2021-02-15 15:41:33 +0000452 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
453 var lintBaseline android.OptionalPath
454 if String(l.properties.Lint.Baseline_filename) != "" {
455 // if manually specified, we require the file to exist
456 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
457 } else {
458 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
459 }
460 if lintBaseline.Valid() {
461 cmd.FlagWithInput("--baseline ", lintBaseline.Path())
462 }
463 }
464
Colin Cross31972dc2021-03-04 10:44:12 -0800465 cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
Colin Cross014489c2020-06-02 20:09:13 -0700466
Colin Cross31972dc2021-03-04 10:44:12 -0800467 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700468
Colin Crossf1a035e2020-11-16 17:32:30 -0800469 rule.Build("lint", "lint")
Colin Cross014489c2020-06-02 20:09:13 -0700470
Colin Crossc0efd1d2020-07-03 11:56:24 -0700471 l.outputs = lintOutputs{
472 html: html,
473 text: text,
474 xml: xml,
Colin Cross014489c2020-06-02 20:09:13 -0700475
Colin Cross08dca382020-07-21 20:31:17 -0700476 depSets: depSetsBuilder.Build(),
Colin Crossc0efd1d2020-07-03 11:56:24 -0700477 }
Colin Cross014489c2020-06-02 20:09:13 -0700478
Colin Crossc0efd1d2020-07-03 11:56:24 -0700479 if l.buildModuleReportZip {
Colin Cross08dca382020-07-21 20:31:17 -0700480 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700481 }
482}
Colin Cross014489c2020-06-02 20:09:13 -0700483
Colin Cross08dca382020-07-21 20:31:17 -0700484func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
485 htmlList := depSets.HTML.ToSortedList()
486 textList := depSets.Text.ToSortedList()
487 xmlList := depSets.XML.ToSortedList()
488
489 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
490 return nil
491 }
492
493 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
494 lintZip(ctx, htmlList, htmlZip)
495
496 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
497 lintZip(ctx, textList, textZip)
498
499 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
500 lintZip(ctx, xmlList, xmlZip)
501
502 return android.Paths{htmlZip, textZip, xmlZip}
503}
504
Colin Cross014489c2020-06-02 20:09:13 -0700505type lintSingleton struct {
506 htmlZip android.WritablePath
507 textZip android.WritablePath
508 xmlZip android.WritablePath
509}
510
511func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
512 l.generateLintReportZips(ctx)
513 l.copyLintDependencies(ctx)
514}
515
516func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
Jeongik Cha816a23a2020-07-08 01:09:23 +0900517 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross014489c2020-06-02 20:09:13 -0700518 return
519 }
520
521 var frameworkDocStubs android.Module
522 ctx.VisitAllModules(func(m android.Module) {
523 if ctx.ModuleName(m) == "framework-doc-stubs" {
524 if frameworkDocStubs == nil {
525 frameworkDocStubs = m
526 } else {
527 ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
528 ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
529 }
530 }
531 })
532
533 if frameworkDocStubs == nil {
534 if !ctx.Config().AllowMissingDependencies() {
535 ctx.Errorf("lint: missing framework-doc-stubs")
536 }
537 return
538 }
539
540 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800541 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700542 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700543 Output: copiedAnnotationsZipPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700544 })
545
546 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800547 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700548 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700549 Output: copiedAPIVersionsXmlPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700550 })
551}
552
Colin Cross8a6ed372020-07-06 11:45:51 -0700553func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700554 return android.PathForOutput(ctx, "lint", "annotations.zip")
555}
556
Colin Cross8a6ed372020-07-06 11:45:51 -0700557func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700558 return android.PathForOutput(ctx, "lint", "api_versions.xml")
559}
560
561func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700562 if ctx.Config().UnbundledBuild() {
563 return
564 }
565
Colin Cross014489c2020-06-02 20:09:13 -0700566 var outputs []*lintOutputs
567 var dirs []string
568 ctx.VisitAllModules(func(m android.Module) {
Jingwen Chencda22c92020-11-23 00:22:30 -0500569 if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
Colin Cross014489c2020-06-02 20:09:13 -0700570 return
571 }
572
Colin Cross56a83212020-09-15 18:30:11 -0700573 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
574 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
575 if apexInfo.IsForPlatform() {
576 // There are stray platform variants of modules in apexes that are not available for
577 // the platform, and they sometimes can't be built. Don't depend on them.
578 return
579 }
Colin Cross014489c2020-06-02 20:09:13 -0700580 }
581
Colin Cross08dca382020-07-21 20:31:17 -0700582 if l, ok := m.(lintOutputsIntf); ok {
Colin Cross014489c2020-06-02 20:09:13 -0700583 outputs = append(outputs, l.lintOutputs())
584 }
585 })
586
587 dirs = android.SortedUniqueStrings(dirs)
588
589 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
590 var paths android.Paths
591
592 for _, output := range outputs {
Colin Cross08dca382020-07-21 20:31:17 -0700593 if p := get(output); p != nil {
594 paths = append(paths, p)
595 }
Colin Cross014489c2020-06-02 20:09:13 -0700596 }
597
Colin Crossc0efd1d2020-07-03 11:56:24 -0700598 lintZip(ctx, paths, outputPath)
Colin Cross014489c2020-06-02 20:09:13 -0700599 }
600
601 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
602 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
603
604 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
605 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
606
607 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
608 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
609
610 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
611}
612
613func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700614 if !ctx.Config().UnbundledBuild() {
615 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
616 }
Colin Cross014489c2020-06-02 20:09:13 -0700617}
618
619var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
620
621func init() {
622 android.RegisterSingletonType("lint",
623 func() android.Singleton { return &lintSingleton{} })
624}
Colin Crossc0efd1d2020-07-03 11:56:24 -0700625
626func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
627 paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
628
629 sort.Slice(paths, func(i, j int) bool {
630 return paths[i].String() < paths[j].String()
631 })
632
Colin Crossf1a035e2020-11-16 17:32:30 -0800633 rule := android.NewRuleBuilder(pctx, ctx)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700634
Colin Crossf1a035e2020-11-16 17:32:30 -0800635 rule.Command().BuiltTool("soong_zip").
Colin Crossc0efd1d2020-07-03 11:56:24 -0700636 FlagWithOutput("-o ", outputPath).
637 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
Colin Cross053fca12020-08-19 13:51:47 -0700638 FlagWithRspFileInputList("-r ", paths)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700639
Colin Crossf1a035e2020-11-16 17:32:30 -0800640 rule.Build(outputPath.Base(), outputPath.Base())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700641}