blob: f8a51805430408b583b01c199a30d169ea18321c [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",
425 },
426 Platform: map[string]string{remoteexec.PoolKey: pool},
427 }).NoVarTemplate(ctx.Config()))
428 }
429
430 cmd.BuiltTool("lint").
Colin Cross014489c2020-06-02 20:09:13 -0700431 Flag("--quiet").
Colin Cross31972dc2021-03-04 10:44:12 -0800432 FlagWithInput("--project ", lintPaths.projectXML).
433 FlagWithInput("--config ", lintPaths.configXML).
Colin Crossc0efd1d2020-07-03 11:56:24 -0700434 FlagWithOutput("--html ", html).
435 FlagWithOutput("--text ", text).
436 FlagWithOutput("--xml ", xml).
Colin Cross014489c2020-06-02 20:09:13 -0700437 FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
438 FlagWithArg("--java-language-level ", l.javaLanguageLevel).
439 FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
440 FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
441 Flag("--exitcode").
442 Flags(l.properties.Lint.Flags).
Colin Cross31972dc2021-03-04 10:44:12 -0800443 Implicit(annotationsZipPath).
444 Implicit(apiVersionsXMLPath).
445 Implicits(lintPaths.deps)
Colin Cross988dfcc2020-07-16 17:32:17 -0700446
447 if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
448 cmd.FlagWithArg("--check ", checkOnly)
449 }
450
Pedro Loureiro5d190cc2021-02-15 15:41:33 +0000451 if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
452 var lintBaseline android.OptionalPath
453 if String(l.properties.Lint.Baseline_filename) != "" {
454 // if manually specified, we require the file to exist
455 lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
456 } else {
457 lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
458 }
459 if lintBaseline.Valid() {
460 cmd.FlagWithInput("--baseline ", lintBaseline.Path())
461 }
462 }
463
Colin Cross31972dc2021-03-04 10:44:12 -0800464 cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
Colin Cross014489c2020-06-02 20:09:13 -0700465
Colin Cross31972dc2021-03-04 10:44:12 -0800466 rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
Colin Cross014489c2020-06-02 20:09:13 -0700467
Colin Crossf1a035e2020-11-16 17:32:30 -0800468 rule.Build("lint", "lint")
Colin Cross014489c2020-06-02 20:09:13 -0700469
Colin Crossc0efd1d2020-07-03 11:56:24 -0700470 l.outputs = lintOutputs{
471 html: html,
472 text: text,
473 xml: xml,
Colin Cross014489c2020-06-02 20:09:13 -0700474
Colin Cross08dca382020-07-21 20:31:17 -0700475 depSets: depSetsBuilder.Build(),
Colin Crossc0efd1d2020-07-03 11:56:24 -0700476 }
Colin Cross014489c2020-06-02 20:09:13 -0700477
Colin Crossc0efd1d2020-07-03 11:56:24 -0700478 if l.buildModuleReportZip {
Colin Cross08dca382020-07-21 20:31:17 -0700479 l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700480 }
481}
Colin Cross014489c2020-06-02 20:09:13 -0700482
Colin Cross08dca382020-07-21 20:31:17 -0700483func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
484 htmlList := depSets.HTML.ToSortedList()
485 textList := depSets.Text.ToSortedList()
486 xmlList := depSets.XML.ToSortedList()
487
488 if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
489 return nil
490 }
491
492 htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
493 lintZip(ctx, htmlList, htmlZip)
494
495 textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
496 lintZip(ctx, textList, textZip)
497
498 xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
499 lintZip(ctx, xmlList, xmlZip)
500
501 return android.Paths{htmlZip, textZip, xmlZip}
502}
503
Colin Cross014489c2020-06-02 20:09:13 -0700504type lintSingleton struct {
505 htmlZip android.WritablePath
506 textZip android.WritablePath
507 xmlZip android.WritablePath
508}
509
510func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
511 l.generateLintReportZips(ctx)
512 l.copyLintDependencies(ctx)
513}
514
515func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
Jeongik Cha816a23a2020-07-08 01:09:23 +0900516 if ctx.Config().AlwaysUsePrebuiltSdks() {
Colin Cross014489c2020-06-02 20:09:13 -0700517 return
518 }
519
520 var frameworkDocStubs android.Module
521 ctx.VisitAllModules(func(m android.Module) {
522 if ctx.ModuleName(m) == "framework-doc-stubs" {
523 if frameworkDocStubs == nil {
524 frameworkDocStubs = m
525 } else {
526 ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
527 ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
528 }
529 }
530 })
531
532 if frameworkDocStubs == nil {
533 if !ctx.Config().AllowMissingDependencies() {
534 ctx.Errorf("lint: missing framework-doc-stubs")
535 }
536 return
537 }
538
539 ctx.Build(pctx, android.BuildParams{
Colin Cross00d93b12021-03-04 10:00:09 -0800540 Rule: android.CpIfChanged,
Colin Cross014489c2020-06-02 20:09:13 -0700541 Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700542 Output: copiedAnnotationsZipPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700543 })
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, ".api_versions.xml"),
Colin Cross8a6ed372020-07-06 11:45:51 -0700548 Output: copiedAPIVersionsXmlPath(ctx),
Colin Cross014489c2020-06-02 20:09:13 -0700549 })
550}
551
Colin Cross8a6ed372020-07-06 11:45:51 -0700552func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700553 return android.PathForOutput(ctx, "lint", "annotations.zip")
554}
555
Colin Cross8a6ed372020-07-06 11:45:51 -0700556func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
Colin Cross014489c2020-06-02 20:09:13 -0700557 return android.PathForOutput(ctx, "lint", "api_versions.xml")
558}
559
560func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700561 if ctx.Config().UnbundledBuild() {
562 return
563 }
564
Colin Cross014489c2020-06-02 20:09:13 -0700565 var outputs []*lintOutputs
566 var dirs []string
567 ctx.VisitAllModules(func(m android.Module) {
Jingwen Chencda22c92020-11-23 00:22:30 -0500568 if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
Colin Cross014489c2020-06-02 20:09:13 -0700569 return
570 }
571
Colin Cross56a83212020-09-15 18:30:11 -0700572 if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
573 apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
574 if apexInfo.IsForPlatform() {
575 // There are stray platform variants of modules in apexes that are not available for
576 // the platform, and they sometimes can't be built. Don't depend on them.
577 return
578 }
Colin Cross014489c2020-06-02 20:09:13 -0700579 }
580
Colin Cross08dca382020-07-21 20:31:17 -0700581 if l, ok := m.(lintOutputsIntf); ok {
Colin Cross014489c2020-06-02 20:09:13 -0700582 outputs = append(outputs, l.lintOutputs())
583 }
584 })
585
586 dirs = android.SortedUniqueStrings(dirs)
587
588 zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
589 var paths android.Paths
590
591 for _, output := range outputs {
Colin Cross08dca382020-07-21 20:31:17 -0700592 if p := get(output); p != nil {
593 paths = append(paths, p)
594 }
Colin Cross014489c2020-06-02 20:09:13 -0700595 }
596
Colin Crossc0efd1d2020-07-03 11:56:24 -0700597 lintZip(ctx, paths, outputPath)
Colin Cross014489c2020-06-02 20:09:13 -0700598 }
599
600 l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
601 zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
602
603 l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
604 zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
605
606 l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
607 zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
608
609 ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
610}
611
612func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
Colin Cross8a6ed372020-07-06 11:45:51 -0700613 if !ctx.Config().UnbundledBuild() {
614 ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
615 }
Colin Cross014489c2020-06-02 20:09:13 -0700616}
617
618var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
619
620func init() {
621 android.RegisterSingletonType("lint",
622 func() android.Singleton { return &lintSingleton{} })
623}
Colin Crossc0efd1d2020-07-03 11:56:24 -0700624
625func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
626 paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
627
628 sort.Slice(paths, func(i, j int) bool {
629 return paths[i].String() < paths[j].String()
630 })
631
Colin Crossf1a035e2020-11-16 17:32:30 -0800632 rule := android.NewRuleBuilder(pctx, ctx)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700633
Colin Crossf1a035e2020-11-16 17:32:30 -0800634 rule.Command().BuiltTool("soong_zip").
Colin Crossc0efd1d2020-07-03 11:56:24 -0700635 FlagWithOutput("-o ", outputPath).
636 FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
Colin Cross053fca12020-08-19 13:51:47 -0700637 FlagWithRspFileInputList("-r ", paths)
Colin Crossc0efd1d2020-07-03 11:56:24 -0700638
Colin Crossf1a035e2020-11-16 17:32:30 -0800639 rule.Build(outputPath.Base(), outputPath.Base())
Colin Crossc0efd1d2020-07-03 11:56:24 -0700640}