blob: aa308e66956b862bd2692eb949574b5cf062a822 [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 {
Pedro Loureirof4a88b12021-02-25 16:23:22 +000060 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
78 extraMainlineLintErrors []string
Colin Crossc0efd1d2020-07-03 11:56:24 -070079
Colin Cross08dca382020-07-21 20:31:17 -070080 reports android.Paths
81
Colin Crossc0efd1d2020-07-03 11:56:24 -070082 buildModuleReportZip bool
Colin Cross014489c2020-06-02 20:09:13 -070083}
84
85type lintOutputs struct {
Colin Cross08dca382020-07-21 20:31:17 -070086 html android.Path
87 text android.Path
88 xml android.Path
Colin Crossc0efd1d2020-07-03 11:56:24 -070089
Colin Cross08dca382020-07-21 20:31:17 -070090 depSets LintDepSets
Colin Crossc0efd1d2020-07-03 11:56:24 -070091}
92
Colin Cross08dca382020-07-21 20:31:17 -070093type lintOutputsIntf interface {
Colin Crossc0efd1d2020-07-03 11:56:24 -070094 lintOutputs() *lintOutputs
95}
96
Colin Cross08dca382020-07-21 20:31:17 -070097type lintDepSetsIntf interface {
98 LintDepSets() LintDepSets
99}
100
101type LintDepSets struct {
102 HTML, Text, XML *android.DepSet
103}
104
105type LintDepSetsBuilder struct {
106 HTML, Text, XML *android.DepSetBuilder
107}
108
109func NewLintDepSetBuilder() LintDepSetsBuilder {
110 return LintDepSetsBuilder{
111 HTML: android.NewDepSetBuilder(android.POSTORDER),
112 Text: android.NewDepSetBuilder(android.POSTORDER),
113 XML: android.NewDepSetBuilder(android.POSTORDER),
114 }
115}
116
117func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
118 l.HTML.Direct(html)
119 l.Text.Direct(text)
120 l.XML.Direct(xml)
121 return l
122}
123
124func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
125 if depSets.HTML != nil {
126 l.HTML.Transitive(depSets.HTML)
127 }
128 if depSets.Text != nil {
129 l.Text.Transitive(depSets.Text)
130 }
131 if depSets.XML != nil {
132 l.XML.Transitive(depSets.XML)
133 }
134 return l
135}
136
137func (l LintDepSetsBuilder) Build() LintDepSets {
138 return LintDepSets{
139 HTML: l.HTML.Build(),
140 Text: l.Text.Build(),
141 XML: l.XML.Build(),
142 }
143}
144
145func (l *linter) LintDepSets() LintDepSets {
146 return l.outputs.depSets
147}
148
149var _ lintDepSetsIntf = (*linter)(nil)
150
151var _ lintOutputsIntf = (*linter)(nil)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700152
153func (l *linter) lintOutputs() *lintOutputs {
154 return &l.outputs
Colin Cross014489c2020-06-02 20:09:13 -0700155}
156
157func (l *linter) enabled() bool {
158 return BoolDefault(l.properties.Lint.Enabled, true)
159}
160
Colin Cross92e4b462020-06-18 15:56:48 -0700161func (l *linter) deps(ctx android.BottomUpMutatorContext) {
162 if !l.enabled() {
163 return
164 }
165
Colin Cross988dfcc2020-07-16 17:32:17 -0700166 extraCheckModules := l.properties.Lint.Extra_check_modules
167
168 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
169 if checkOnlyModules := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); checkOnlyModules != "" {
170 extraCheckModules = strings.Split(checkOnlyModules, ",")
171 }
172 }
173
174 ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
175 extraLintCheckTag, extraCheckModules...)
Colin Cross92e4b462020-06-18 15:56:48 -0700176}
177
Colin Crossad22bc22021-03-10 09:45:40 -0800178// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
179// around.
Colin Cross31972dc2021-03-04 10:44:12 -0800180type lintPaths struct {
181 projectXML android.WritablePath
182 configXML android.WritablePath
183 cacheDir android.WritablePath
184 homeDir android.WritablePath
185 srcjarDir android.WritablePath
Colin Cross31972dc2021-03-04 10:44:12 -0800186}
187
Colin Cross9b93af42021-03-10 10:40:58 -0800188func lintRBEExecStrategy(ctx android.ModuleContext) string {
189 return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
190}
191
Colin Cross31972dc2021-03-04 10:44:12 -0800192func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
Colin Cross31972dc2021-03-04 10:44:12 -0800193 projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
Colin Cross014489c2020-06-02 20:09:13 -0700194 // Lint looks for a lint.xml file next to the project.xml file, give it one.
Colin Cross31972dc2021-03-04 10:44:12 -0800195 configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
196 cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
197 homeDir := android.PathForModuleOut(ctx, "lint", "home")
Colin Cross014489c2020-06-02 20:09:13 -0700198
Colin Cross1661aff2021-03-12 17:56:51 -0800199 srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
Colin Cross014489c2020-06-02 20:09:13 -0700200 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
201
202 cmd := rule.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800203 BuiltTool("lint-project-xml").
Colin Cross014489c2020-06-02 20:09:13 -0700204 FlagWithOutput("--project_out ", projectXMLPath).
205 FlagWithOutput("--config_out ", configXMLPath).
206 FlagWithArg("--name ", ctx.ModuleName())
207
208 if l.library {
209 cmd.Flag("--library")
210 }
211 if l.test {
212 cmd.Flag("--test")
213 }
214 if l.manifest != nil {
Colin Cross5bedfa22021-03-23 17:07:14 -0700215 cmd.FlagWithInput("--manifest ", l.manifest)
Colin Cross014489c2020-06-02 20:09:13 -0700216 }
217 if l.mergedManifest != nil {
Colin Cross5bedfa22021-03-23 17:07:14 -0700218 cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
Colin Cross014489c2020-06-02 20:09:13 -0700219 }
220
Colin Cross5bedfa22021-03-23 17:07:14 -0700221 // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
222 // lint separately.
223 srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
224 cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
Colin Cross014489c2020-06-02 20:09:13 -0700225
226 cmd.FlagWithInput("--generated_srcs ", srcJarList)
Colin Cross014489c2020-06-02 20:09:13 -0700227
Colin Cross5bedfa22021-03-23 17:07:14 -0700228 if len(l.resources) > 0 {
229 resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
230 cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
Colin Cross014489c2020-06-02 20:09:13 -0700231 }
232
233 if l.classes != nil {
Colin Cross5bedfa22021-03-23 17:07:14 -0700234 cmd.FlagWithInput("--classes ", l.classes)
Colin Cross014489c2020-06-02 20:09:13 -0700235 }
236
Colin Cross5bedfa22021-03-23 17:07:14 -0700237 cmd.FlagForEachInput("--classpath ", l.classpath)
Colin Cross014489c2020-06-02 20:09:13 -0700238
Colin Cross5bedfa22021-03-23 17:07:14 -0700239 cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
Colin Cross014489c2020-06-02 20:09:13 -0700240
Colin Cross1661aff2021-03-12 17:56:51 -0800241 cmd.FlagWithArg("--root_dir ", "$PWD")
Colin Crossc31efeb2020-06-23 10:25:26 -0700242
243 // The cache tag in project.xml is relative to the root dir, or the project.xml file if
244 // the root dir is not set.
245 cmd.FlagWithArg("--cache_dir ", cacheDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700246
247 cmd.FlagWithInput("@",
248 android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
249
Pedro Loureirof4a88b12021-02-25 16:23:22 +0000250 cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
Colin Cross014489c2020-06-02 20:09:13 -0700251 cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
252 cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
253 cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
254 cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
255
Colin Cross31972dc2021-03-04 10:44:12 -0800256 return lintPaths{
257 projectXML: projectXMLPath,
258 configXML: configXMLPath,
259 cacheDir: cacheDir,
260 homeDir: homeDir,
Colin Cross31972dc2021-03-04 10:44:12 -0800261 }
262
Colin Cross014489c2020-06-02 20:09:13 -0700263}
264
Liz Kammer20ebfb42020-07-28 11:32:07 -0700265// generateManifest adds a command to the rule to write a simple manifest that contains the
Colin Cross014489c2020-06-02 20:09:13 -0700266// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
Colin Cross1661aff2021-03-12 17:56:51 -0800267func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700268 manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
269
270 rule.Command().Text("(").
271 Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
272 Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
273 Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
274 Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
275 l.minSdkVersion, l.targetSdkVersion).
276 Text(`echo "</manifest>"`).
277 Text(") >").Output(manifestPath)
278
279 return manifestPath
280}
281
282func (l *linter) lint(ctx android.ModuleContext) {
283 if !l.enabled() {
284 return
285 }
286
Pedro Loureirof4a88b12021-02-25 16:23:22 +0000287 if l.minSdkVersion != l.compileSdkVersion {
288 l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, "NewApi")
289 }
290
Colin Cross92e4b462020-06-18 15:56:48 -0700291 extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
292 for _, extraLintCheckModule := range extraLintCheckModules {
Colin Crossdcf71b22021-02-01 13:59:03 -0800293 if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
294 dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
295 l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
Colin Cross92e4b462020-06-18 15:56:48 -0700296 } else {
297 ctx.PropertyErrorf("lint.extra_check_modules",
298 "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
299 }
300 }
301
Colin Cross1661aff2021-03-12 17:56:51 -0800302 rule := android.NewRuleBuilder(pctx, ctx).
303 Sbox(android.PathForModuleOut(ctx, "lint"),
304 android.PathForModuleOut(ctx, "lint.sbox.textproto")).
305 SandboxInputs()
306
307 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
308 pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
309 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
310 rule.Rewrapper(&remoteexec.REParams{
311 Labels: map[string]string{"type": "tool", "name": "lint"},
312 ExecStrategy: lintRBEExecStrategy(ctx),
313 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
314 EnvironmentVariables: []string{
315 "LANG",
316 },
317 Platform: map[string]string{remoteexec.PoolKey: pool},
318 })
319 }
Colin Cross014489c2020-06-02 20:09:13 -0700320
321 if l.manifest == nil {
322 manifest := l.generateManifest(ctx, rule)
323 l.manifest = manifest
Colin Cross1661aff2021-03-12 17:56:51 -0800324 rule.Temporary(manifest)
Colin Cross014489c2020-06-02 20:09:13 -0700325 }
326
Colin Cross31972dc2021-03-04 10:44:12 -0800327 lintPaths := l.writeLintProjectXML(ctx, rule)
Colin Cross014489c2020-06-02 20:09:13 -0700328
Colin Cross1661aff2021-03-12 17:56:51 -0800329 html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
330 text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
331 xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
Colin Crossc0efd1d2020-07-03 11:56:24 -0700332
Colin Cross08dca382020-07-21 20:31:17 -0700333 depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700334
335 ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
Colin Cross08dca382020-07-21 20:31:17 -0700336 if depLint, ok := dep.(lintDepSetsIntf); ok {
337 depSetsBuilder.Transitive(depLint.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700338 }
339 })
Colin Cross014489c2020-06-02 20:09:13 -0700340
Colin Cross31972dc2021-03-04 10:44:12 -0800341 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
342 rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross5c113d12021-03-04 10:01:34 -0800343 rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
Colin Cross014489c2020-06-02 20:09:13 -0700344
Colin Cross8a6ed372020-07-06 11:45:51 -0700345 var annotationsZipPath, apiVersionsXMLPath android.Path
Jeongik Cha816a23a2020-07-08 01:09:23 +0900346 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross8a6ed372020-07-06 11:45:51 -0700347 annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
348 apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
349 } else {
350 annotationsZipPath = copiedAnnotationsZipPath(ctx)
351 apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
352 }
353
Colin Cross31972dc2021-03-04 10:44:12 -0800354 cmd := rule.Command()
355
Pedro Loureiro70acc3d2021-04-06 17:49:19 +0000356 cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
Colin Cross31972dc2021-03-04 10:44:12 -0800357 FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
Colin Cross8a6ed372020-07-06 11:45:51 -0700358 FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
Colin Cross31972dc2021-03-04 10:44:12 -0800359 FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
360
Colin Cross1661aff2021-03-12 17:56:51 -0800361 cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
Colin Cross014489c2020-06-02 20:09:13 -0700362 Flag("--quiet").
Colin Cross31972dc2021-03-04 10:44:12 -0800363 FlagWithInput("--project ", lintPaths.projectXML).
364 FlagWithInput("--config ", lintPaths.configXML).
Colin Crossc0efd1d2020-07-03 11:56:24 -0700365 FlagWithOutput("--html ", html).
366 FlagWithOutput("--text ", text).
367 FlagWithOutput("--xml ", xml).
Colin Cross014489c2020-06-02 20:09:13 -0700368 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
369 FlagWithArg("--java-language-level ", l.javaLanguageLevel).
370 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
371 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
372 Flag("--exitcode").
373 Flags(l.properties.Lint.Flags).
Colin Cross31972dc2021-03-04 10:44:12 -0800374 Implicit(annotationsZipPath).
Colin Cross5bedfa22021-03-23 17:07:14 -0700375 Implicit(apiVersionsXMLPath)
Colin Cross988dfcc2020-07-16 17:32:17 -0700376
Colin Cross1661aff2021-03-12 17:56:51 -0800377 rule.Temporary(lintPaths.projectXML)
378 rule.Temporary(lintPaths.configXML)
379
Colin Cross988dfcc2020-07-16 17:32:17 -0700380 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
381 cmd.FlagWithArg("--check ", checkOnly)
382 }
383
Pedro Loureiro5d190cc2021-02-15 15:41:33 +0000384 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
385 var lintBaseline android.OptionalPath
386 if String(l.properties.Lint.Baseline_filename) != "" {
387 // if manually specified, we require the file to exist
388 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
389 } else {
390 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
391 }
392 if lintBaseline.Valid() {
393 cmd.FlagWithInput("--baseline ", lintBaseline.Path())
394 }
395 }
396
Colin Cross31972dc2021-03-04 10:44:12 -0800397 cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
Colin Cross014489c2020-06-02 20:09:13 -0700398
Colin Cross31972dc2021-03-04 10:44:12 -0800399 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700400
Colin Crossee4a8b72021-04-05 18:38:05 -0700401 // The HTML output contains a date, remove it to make the output deterministic.
402 rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
403
Colin Crossf1a035e2020-11-16 17:32:30 -0800404 rule.Build("lint", "lint")
Colin Cross014489c2020-06-02 20:09:13 -0700405
Colin Crossc0efd1d2020-07-03 11:56:24 -0700406 l.outputs = lintOutputs{
407 html: html,
408 text: text,
409 xml: xml,
Colin Cross014489c2020-06-02 20:09:13 -0700410
Colin Cross08dca382020-07-21 20:31:17 -0700411 depSets: depSetsBuilder.Build(),
Colin Crossc0efd1d2020-07-03 11:56:24 -0700412 }
Colin Cross014489c2020-06-02 20:09:13 -0700413
Colin Crossc0efd1d2020-07-03 11:56:24 -0700414 if l.buildModuleReportZip {
Colin Cross08dca382020-07-21 20:31:17 -0700415 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700416 }
417}
Colin Cross014489c2020-06-02 20:09:13 -0700418
Colin Cross08dca382020-07-21 20:31:17 -0700419func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
420 htmlList := depSets.HTML.ToSortedList()
421 textList := depSets.Text.ToSortedList()
422 xmlList := depSets.XML.ToSortedList()
423
424 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
425 return nil
426 }
427
428 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
429 lintZip(ctx, htmlList, htmlZip)
430
431 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
432 lintZip(ctx, textList, textZip)
433
434 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
435 lintZip(ctx, xmlList, xmlZip)
436
437 return android.Paths{htmlZip, textZip, xmlZip}
438}
439
Colin Cross014489c2020-06-02 20:09:13 -0700440type lintSingleton struct {
441 htmlZip android.WritablePath
442 textZip android.WritablePath
443 xmlZip android.WritablePath
444}
445
446func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
447 l.generateLintReportZips(ctx)
448 l.copyLintDependencies(ctx)
449}
450
451func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
Jeongik Cha816a23a2020-07-08 01:09:23 +0900452 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross014489c2020-06-02 20:09:13 -0700453 return
454 }
455
456 var frameworkDocStubs android.Module
457 ctx.VisitAllModules(func(m android.Module) {
458 if ctx.ModuleName(m) == "framework-doc-stubs" {
459 if frameworkDocStubs == nil {
460 frameworkDocStubs = m
461 } else {
462 ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
463 ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
464 }
465 }
466 })
467
468 if frameworkDocStubs == nil {
469 if !ctx.Config().AllowMissingDependencies() {
470 ctx.Errorf("lint: missing framework-doc-stubs")
471 }
472 return
473 }
474
475 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800476 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700477 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700478 Output: copiedAnnotationsZipPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700479 })
480
481 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800482 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700483 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700484 Output: copiedAPIVersionsXmlPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700485 })
486}
487
Colin Cross8a6ed372020-07-06 11:45:51 -0700488func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700489 return android.PathForOutput(ctx, "lint", "annotations.zip")
490}
491
Colin Cross8a6ed372020-07-06 11:45:51 -0700492func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700493 return android.PathForOutput(ctx, "lint", "api_versions.xml")
494}
495
496func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700497 if ctx.Config().UnbundledBuild() {
498 return
499 }
500
Colin Cross014489c2020-06-02 20:09:13 -0700501 var outputs []*lintOutputs
502 var dirs []string
503 ctx.VisitAllModules(func(m android.Module) {
Jingwen Chencda22c92020-11-23 00:22:30 -0500504 if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
Colin Cross014489c2020-06-02 20:09:13 -0700505 return
506 }
507
Colin Cross56a83212020-09-15 18:30:11 -0700508 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
509 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
510 if apexInfo.IsForPlatform() {
511 // There are stray platform variants of modules in apexes that are not available for
512 // the platform, and they sometimes can't be built. Don't depend on them.
513 return
514 }
Colin Cross014489c2020-06-02 20:09:13 -0700515 }
516
Colin Cross08dca382020-07-21 20:31:17 -0700517 if l, ok := m.(lintOutputsIntf); ok {
Colin Cross014489c2020-06-02 20:09:13 -0700518 outputs = append(outputs, l.lintOutputs())
519 }
520 })
521
522 dirs = android.SortedUniqueStrings(dirs)
523
524 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
525 var paths android.Paths
526
527 for _, output := range outputs {
Colin Cross08dca382020-07-21 20:31:17 -0700528 if p := get(output); p != nil {
529 paths = append(paths, p)
530 }
Colin Cross014489c2020-06-02 20:09:13 -0700531 }
532
Colin Crossc0efd1d2020-07-03 11:56:24 -0700533 lintZip(ctx, paths, outputPath)
Colin Cross014489c2020-06-02 20:09:13 -0700534 }
535
536 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
537 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
538
539 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
540 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
541
542 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
543 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
544
545 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
546}
547
548func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700549 if !ctx.Config().UnbundledBuild() {
550 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
551 }
Colin Cross014489c2020-06-02 20:09:13 -0700552}
553
554var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
555
556func init() {
557 android.RegisterSingletonType("lint",
558 func() android.Singleton { return &lintSingleton{} })
559}
Colin Crossc0efd1d2020-07-03 11:56:24 -0700560
561func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
562 paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
563
564 sort.Slice(paths, func(i, j int) bool {
565 return paths[i].String() < paths[j].String()
566 })
567
Colin Crossf1a035e2020-11-16 17:32:30 -0800568 rule := android.NewRuleBuilder(pctx, ctx)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700569
Colin Crossf1a035e2020-11-16 17:32:30 -0800570 rule.Command().BuiltTool("soong_zip").
Colin Crossc0efd1d2020-07-03 11:56:24 -0700571 FlagWithOutput("-o ", outputPath).
572 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
Colin Cross70c47412021-03-12 17:48:14 -0800573 FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700574
Colin Crossf1a035e2020-11-16 17:32:30 -0800575 rule.Build(outputPath.Base(), outputPath.Base())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700576}