blob: 5940eac6108ae978b84524e05ad141e0b426e963 [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
192func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
193 var deps android.Paths
194 var remoteInputs android.Paths
195 var remoteRSPInputs android.Paths
196
197 // Paths passed to trackInputDependency will be added as dependencies of the rule that runs
198 // lint and passed as inputs to the remote execution proxy.
199 trackInputDependency := func(paths ...android.Path) {
200 deps = append(deps, paths...)
201 remoteInputs = append(remoteInputs, paths...)
202 }
203
204 // Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
205 // lint, but the RSP file will be used by the remote execution proxy to find the files so that
206 // it doesn't overflow command line limits.
207 trackRSPDependency := func(paths android.Paths, rsp android.Path) {
208 deps = append(deps, paths...)
209 remoteRSPInputs = append(remoteRSPInputs, rsp)
210 }
Colin Cross014489c2020-06-02 20:09:13 -0700211
212 var resourcesList android.WritablePath
213 if len(l.resources) > 0 {
214 // The list of resources may be too long to put on the command line, but
215 // we can't use the rsp file because it is already being used for srcs.
216 // Insert a second rule to write out the list of resources to a file.
217 resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
Colin Crossf1a035e2020-11-16 17:32:30 -0800218 resListRule := android.NewRuleBuilder(pctx, ctx)
Colin Cross014489c2020-06-02 20:09:13 -0700219 resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
Colin Crossf1a035e2020-11-16 17:32:30 -0800220 resListRule.Build("lint_resources_list", "lint resources list")
Colin Cross31972dc2021-03-04 10:44:12 -0800221 trackRSPDependency(l.resources, resourcesList)
Colin Cross014489c2020-06-02 20:09:13 -0700222 }
223
Colin Cross31972dc2021-03-04 10:44:12 -0800224 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
Colin Cross014489c2020-06-02 20:09:13 -0700225 // Lint looks for a lint.xml file next to the project.xml file, give it one.
Colin Cross31972dc2021-03-04 10:44:12 -0800226 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
227 cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
228 homeDir := android.PathForModuleOut(ctx, "lint", "home")
Colin Cross014489c2020-06-02 20:09:13 -0700229
230 srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
231 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
Colin Cross31972dc2021-03-04 10:44:12 -0800232 // TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
233 // by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
234 // not the extracted files.
235 trackRSPDependency(l.srcJars, srcJarList)
236
237 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
238 // lint separately.
239 srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
240 rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList)
241 trackRSPDependency(l.srcs, srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700242
243 cmd := rule.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800244 BuiltTool("lint-project-xml").
Colin Cross014489c2020-06-02 20:09:13 -0700245 FlagWithOutput("--project_out ", projectXMLPath).
246 FlagWithOutput("--config_out ", configXMLPath).
247 FlagWithArg("--name ", ctx.ModuleName())
248
249 if l.library {
250 cmd.Flag("--library")
251 }
252 if l.test {
253 cmd.Flag("--test")
254 }
255 if l.manifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700256 cmd.FlagWithArg("--manifest ", l.manifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800257 trackInputDependency(l.manifest)
Colin Cross014489c2020-06-02 20:09:13 -0700258 }
259 if l.mergedManifest != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700260 cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800261 trackInputDependency(l.mergedManifest)
Colin Cross014489c2020-06-02 20:09:13 -0700262 }
263
Colin Cross31972dc2021-03-04 10:44:12 -0800264 cmd.FlagWithInput("--srcs ", srcsList)
Colin Cross014489c2020-06-02 20:09:13 -0700265
266 cmd.FlagWithInput("--generated_srcs ", srcJarList)
Colin Cross014489c2020-06-02 20:09:13 -0700267
268 if resourcesList != nil {
269 cmd.FlagWithInput("--resources ", resourcesList)
270 }
271
272 if l.classes != nil {
Colin Cross014489c2020-06-02 20:09:13 -0700273 cmd.FlagWithArg("--classes ", l.classes.String())
Colin Cross31972dc2021-03-04 10:44:12 -0800274 trackInputDependency(l.classes)
Colin Cross014489c2020-06-02 20:09:13 -0700275 }
276
277 cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800278 trackInputDependency(l.classpath...)
Colin Cross014489c2020-06-02 20:09:13 -0700279
280 cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
Colin Cross31972dc2021-03-04 10:44:12 -0800281 trackInputDependency(l.extraLintCheckJars...)
Colin Cross014489c2020-06-02 20:09:13 -0700282
Colin Cross31972dc2021-03-04 10:44:12 -0800283 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
284 // TODO(b/181912787): remove these and use "." instead.
285 cmd.FlagWithArg("--root_dir ", "/b/f/w")
286 } else {
287 cmd.FlagWithArg("--root_dir ", "$PWD")
288 }
Colin Crossc31efeb2020-06-23 10:25:26 -0700289
290 // The cache tag in project.xml is relative to the root dir, or the project.xml file if
291 // the root dir is not set.
292 cmd.FlagWithArg("--cache_dir ", cacheDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700293
294 cmd.FlagWithInput("@",
295 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
296
297 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
298 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
299 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
300 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
301
Colin Cross31972dc2021-03-04 10:44:12 -0800302 return lintPaths{
303 projectXML: projectXMLPath,
304 configXML: configXMLPath,
305 cacheDir: cacheDir,
306 homeDir: homeDir,
307
308 deps: deps,
309
310 remoteInputs: remoteInputs,
311 remoteRSPInputs: remoteRSPInputs,
312 }
313
Colin Cross014489c2020-06-02 20:09:13 -0700314}
315
Liz Kammer20ebfb42020-07-28 11:32:07 -0700316// generateManifest adds a command to the rule to write a simple manifest that contains the
Colin Cross014489c2020-06-02 20:09:13 -0700317// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
318func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.Path {
319 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
320
321 rule.Command().Text("(").
322 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
323 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
324 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
325 Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
326 l.minSdkVersion, l.targetSdkVersion).
327 Text(`echo "</manifest>"`).
328 Text(") >").Output(manifestPath)
329
330 return manifestPath
331}
332
333func (l *linter) lint(ctx android.ModuleContext) {
334 if !l.enabled() {
335 return
336 }
337
Colin Cross92e4b462020-06-18 15:56:48 -0700338 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
339 for _, extraLintCheckModule := range extraLintCheckModules {
Colin Crossdcf71b22021-02-01 13:59:03 -0800340 if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
341 dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
342 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
Colin Cross92e4b462020-06-18 15:56:48 -0700343 } else {
344 ctx.PropertyErrorf("lint.extra_check_modules",
345 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
346 }
347 }
348
Colin Crossf1a035e2020-11-16 17:32:30 -0800349 rule := android.NewRuleBuilder(pctx, ctx)
Colin Cross014489c2020-06-02 20:09:13 -0700350
351 if l.manifest == nil {
352 manifest := l.generateManifest(ctx, rule)
353 l.manifest = manifest
354 }
355
Colin Cross31972dc2021-03-04 10:44:12 -0800356 lintPaths := l.writeLintProjectXML(ctx, rule)
Colin Cross014489c2020-06-02 20:09:13 -0700357
Colin Crossc0efd1d2020-07-03 11:56:24 -0700358 html := android.PathForModuleOut(ctx, "lint-report.html")
359 text := android.PathForModuleOut(ctx, "lint-report.txt")
360 xml := android.PathForModuleOut(ctx, "lint-report.xml")
361
Colin Cross08dca382020-07-21 20:31:17 -0700362 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700363
364 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
Colin Cross08dca382020-07-21 20:31:17 -0700365 if depLint, ok := dep.(lintDepSetsIntf); ok {
366 depSetsBuilder.Transitive(depLint.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700367 }
368 })
Colin Cross014489c2020-06-02 20:09:13 -0700369
Colin Cross31972dc2021-03-04 10:44:12 -0800370 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
371 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross5c113d12021-03-04 10:01:34 -0800372 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
Colin Cross014489c2020-06-02 20:09:13 -0700373
Colin Cross8a6ed372020-07-06 11:45:51 -0700374 var annotationsZipPath, apiVersionsXMLPath android.Path
Jeongik Cha816a23a2020-07-08 01:09:23 +0900375 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross8a6ed372020-07-06 11:45:51 -0700376 annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
377 apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
378 } else {
379 annotationsZipPath = copiedAnnotationsZipPath(ctx)
380 apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
381 }
382
Colin Cross31972dc2021-03-04 10:44:12 -0800383 cmd := rule.Command()
384
385 cmd.Flag("JAVA_OPTS=-Xmx3072m").
386 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
Colin Cross8a6ed372020-07-06 11:45:51 -0700387 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
Colin Cross31972dc2021-03-04 10:44:12 -0800388 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
389
390 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
391 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
392 // TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml
393 // is removed.
394 execStrategy := ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.RemoteExecStrategy)
395 labels := map[string]string{"type": "tool", "name": "lint"}
396 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
397 remoteInputs := lintPaths.remoteInputs
398 remoteInputs = append(remoteInputs,
399 lintPaths.projectXML,
400 lintPaths.configXML,
401 lintPaths.homeDir,
402 lintPaths.cacheDir,
403 ctx.Config().HostJavaToolPath(ctx, "lint.jar"),
404 annotationsZipPath,
405 apiVersionsXMLPath,
406 )
407
408 cmd.Text((&remoteexec.REParams{
409 Labels: labels,
410 ExecStrategy: execStrategy,
411 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
412 Inputs: remoteInputs.Strings(),
413 OutputFiles: android.Paths{html, text, xml}.Strings(),
414 RSPFile: strings.Join(lintPaths.remoteRSPInputs.Strings(), ","),
415 EnvironmentVariables: []string{
416 "JAVA_OPTS",
417 "ANDROID_SDK_HOME",
418 "SDK_ANNOTATIONS",
419 "LINT_OPTS",
420 },
421 Platform: map[string]string{remoteexec.PoolKey: pool},
422 }).NoVarTemplate(ctx.Config()))
423 }
424
425 cmd.BuiltTool("lint").
Colin Cross014489c2020-06-02 20:09:13 -0700426 Flag("--quiet").
Colin Cross31972dc2021-03-04 10:44:12 -0800427 FlagWithInput("--project ", lintPaths.projectXML).
428 FlagWithInput("--config ", lintPaths.configXML).
Colin Crossc0efd1d2020-07-03 11:56:24 -0700429 FlagWithOutput("--html ", html).
430 FlagWithOutput("--text ", text).
431 FlagWithOutput("--xml ", xml).
Colin Cross014489c2020-06-02 20:09:13 -0700432 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
433 FlagWithArg("--java-language-level ", l.javaLanguageLevel).
434 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
435 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
436 Flag("--exitcode").
437 Flags(l.properties.Lint.Flags).
Colin Cross31972dc2021-03-04 10:44:12 -0800438 Implicit(annotationsZipPath).
439 Implicit(apiVersionsXMLPath).
440 Implicits(lintPaths.deps)
Colin Cross988dfcc2020-07-16 17:32:17 -0700441
442 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
443 cmd.FlagWithArg("--check ", checkOnly)
444 }
445
Pedro Loureiro5d190cc2021-02-15 15:41:33 +0000446 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
447 var lintBaseline android.OptionalPath
448 if String(l.properties.Lint.Baseline_filename) != "" {
449 // if manually specified, we require the file to exist
450 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
451 } else {
452 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
453 }
454 if lintBaseline.Valid() {
455 cmd.FlagWithInput("--baseline ", lintBaseline.Path())
456 }
457 }
458
Colin Cross31972dc2021-03-04 10:44:12 -0800459 cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
Colin Cross014489c2020-06-02 20:09:13 -0700460
Colin Cross31972dc2021-03-04 10:44:12 -0800461 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700462
Colin Crossf1a035e2020-11-16 17:32:30 -0800463 rule.Build("lint", "lint")
Colin Cross014489c2020-06-02 20:09:13 -0700464
Colin Crossc0efd1d2020-07-03 11:56:24 -0700465 l.outputs = lintOutputs{
466 html: html,
467 text: text,
468 xml: xml,
Colin Cross014489c2020-06-02 20:09:13 -0700469
Colin Cross08dca382020-07-21 20:31:17 -0700470 depSets: depSetsBuilder.Build(),
Colin Crossc0efd1d2020-07-03 11:56:24 -0700471 }
Colin Cross014489c2020-06-02 20:09:13 -0700472
Colin Crossc0efd1d2020-07-03 11:56:24 -0700473 if l.buildModuleReportZip {
Colin Cross08dca382020-07-21 20:31:17 -0700474 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700475 }
476}
Colin Cross014489c2020-06-02 20:09:13 -0700477
Colin Cross08dca382020-07-21 20:31:17 -0700478func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
479 htmlList := depSets.HTML.ToSortedList()
480 textList := depSets.Text.ToSortedList()
481 xmlList := depSets.XML.ToSortedList()
482
483 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
484 return nil
485 }
486
487 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
488 lintZip(ctx, htmlList, htmlZip)
489
490 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
491 lintZip(ctx, textList, textZip)
492
493 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
494 lintZip(ctx, xmlList, xmlZip)
495
496 return android.Paths{htmlZip, textZip, xmlZip}
497}
498
Colin Cross014489c2020-06-02 20:09:13 -0700499type lintSingleton struct {
500 htmlZip android.WritablePath
501 textZip android.WritablePath
502 xmlZip android.WritablePath
503}
504
505func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
506 l.generateLintReportZips(ctx)
507 l.copyLintDependencies(ctx)
508}
509
510func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
Jeongik Cha816a23a2020-07-08 01:09:23 +0900511 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross014489c2020-06-02 20:09:13 -0700512 return
513 }
514
515 var frameworkDocStubs android.Module
516 ctx.VisitAllModules(func(m android.Module) {
517 if ctx.ModuleName(m) == "framework-doc-stubs" {
518 if frameworkDocStubs == nil {
519 frameworkDocStubs = m
520 } else {
521 ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
522 ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
523 }
524 }
525 })
526
527 if frameworkDocStubs == nil {
528 if !ctx.Config().AllowMissingDependencies() {
529 ctx.Errorf("lint: missing framework-doc-stubs")
530 }
531 return
532 }
533
534 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800535 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700536 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700537 Output: copiedAnnotationsZipPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700538 })
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, ".api_versions.xml"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700543 Output: copiedAPIVersionsXmlPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700544 })
545}
546
Colin Cross8a6ed372020-07-06 11:45:51 -0700547func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700548 return android.PathForOutput(ctx, "lint", "annotations.zip")
549}
550
Colin Cross8a6ed372020-07-06 11:45:51 -0700551func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700552 return android.PathForOutput(ctx, "lint", "api_versions.xml")
553}
554
555func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700556 if ctx.Config().UnbundledBuild() {
557 return
558 }
559
Colin Cross014489c2020-06-02 20:09:13 -0700560 var outputs []*lintOutputs
561 var dirs []string
562 ctx.VisitAllModules(func(m android.Module) {
Jingwen Chencda22c92020-11-23 00:22:30 -0500563 if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
Colin Cross014489c2020-06-02 20:09:13 -0700564 return
565 }
566
Colin Cross56a83212020-09-15 18:30:11 -0700567 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
568 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
569 if apexInfo.IsForPlatform() {
570 // There are stray platform variants of modules in apexes that are not available for
571 // the platform, and they sometimes can't be built. Don't depend on them.
572 return
573 }
Colin Cross014489c2020-06-02 20:09:13 -0700574 }
575
Colin Cross08dca382020-07-21 20:31:17 -0700576 if l, ok := m.(lintOutputsIntf); ok {
Colin Cross014489c2020-06-02 20:09:13 -0700577 outputs = append(outputs, l.lintOutputs())
578 }
579 })
580
581 dirs = android.SortedUniqueStrings(dirs)
582
583 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
584 var paths android.Paths
585
586 for _, output := range outputs {
Colin Cross08dca382020-07-21 20:31:17 -0700587 if p := get(output); p != nil {
588 paths = append(paths, p)
589 }
Colin Cross014489c2020-06-02 20:09:13 -0700590 }
591
Colin Crossc0efd1d2020-07-03 11:56:24 -0700592 lintZip(ctx, paths, outputPath)
Colin Cross014489c2020-06-02 20:09:13 -0700593 }
594
595 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
596 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
597
598 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
599 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
600
601 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
602 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
603
604 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
605}
606
607func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700608 if !ctx.Config().UnbundledBuild() {
609 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
610 }
Colin Cross014489c2020-06-02 20:09:13 -0700611}
612
613var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
614
615func init() {
616 android.RegisterSingletonType("lint",
617 func() android.Singleton { return &lintSingleton{} })
618}
Colin Crossc0efd1d2020-07-03 11:56:24 -0700619
620func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
621 paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
622
623 sort.Slice(paths, func(i, j int) bool {
624 return paths[i].String() < paths[j].String()
625 })
626
Colin Crossf1a035e2020-11-16 17:32:30 -0800627 rule := android.NewRuleBuilder(pctx, ctx)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700628
Colin Crossf1a035e2020-11-16 17:32:30 -0800629 rule.Command().BuiltTool("soong_zip").
Colin Crossc0efd1d2020-07-03 11:56:24 -0700630 FlagWithOutput("-o ", outputPath).
631 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
Colin Cross053fca12020-08-19 13:51:47 -0700632 FlagWithRspFileInputList("-r ", paths)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700633
Colin Crossf1a035e2020-11-16 17:32:30 -0800634 rule.Build(outputPath.Base(), outputPath.Base())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700635}