• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/depset"
24	"github.com/google/blueprint/proptools"
25
26	"android/soong/android"
27	"android/soong/java/config"
28	"android/soong/remoteexec"
29)
30
31// lint checks automatically enforced for modules that have different min_sdk_version than
32// sdk_version
33var updatabilityChecks = []string{"NewApi"}
34
35type LintProperties struct {
36	// Controls for running Android Lint on the module.
37	Lint struct {
38
39		// If true, run Android Lint on the module.  Defaults to true.
40		Enabled *bool
41
42		// Flags to pass to the Android Lint tool.
43		Flags []string
44
45		// Checks that should be treated as fatal.
46		Fatal_checks []string
47
48		// Checks that should be treated as errors.
49		Error_checks []string
50
51		// Checks that should be treated as warnings.
52		Warning_checks []string
53
54		// Checks that should be skipped.
55		Disabled_checks []string
56
57		// Modules that provide extra lint checks
58		Extra_check_modules []string
59
60		// The lint baseline file to use. If specified, lint warnings listed in this file will be
61		// suppressed during lint checks.
62		Baseline_filename *string
63
64		// If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
65		Strict_updatability_linting *bool
66
67		// Treat the code in this module as test code for @VisibleForTesting enforcement.
68		// This will be true by default for test module types, false otherwise.
69		// If soong gets support for testonly, this flag should be replaced with that.
70		Test *bool
71
72		// Same as the regular Test property, but set by internal soong code based on if the module
73		// type is a test module type. This will act as the default value for the test property,
74		// but can be overridden by the user.
75		Test_module_type *bool `blueprint:"mutated"`
76
77		// Whether to ignore the exit code of Android lint. This is the --exit_code
78		// option. Defaults to false.
79		Suppress_exit_code *bool
80	}
81}
82
83type linter struct {
84	name                    string
85	manifest                android.Path
86	mergedManifest          android.Path
87	srcs                    android.Paths
88	srcJars                 android.Paths
89	resources               android.Paths
90	classpath               android.Paths
91	classes                 android.Path
92	extraLintCheckJars      android.Paths
93	library                 bool
94	minSdkVersion           android.ApiLevel
95	targetSdkVersion        android.ApiLevel
96	compileSdkVersion       android.ApiLevel
97	compileSdkKind          android.SdkKind
98	javaLanguageLevel       string
99	kotlinLanguageLevel     string
100	properties              LintProperties
101	extraMainlineLintErrors []string
102	compile_data            android.Paths
103
104	reports android.Paths
105
106	buildModuleReportZip bool
107}
108
109type LintDepSets struct {
110	HTML, Text, XML, Baseline depset.DepSet[android.Path]
111}
112
113type LintDepSetsBuilder struct {
114	HTML, Text, XML, Baseline *depset.Builder[android.Path]
115}
116
117func NewLintDepSetBuilder() LintDepSetsBuilder {
118	return LintDepSetsBuilder{
119		HTML:     depset.NewBuilder[android.Path](depset.POSTORDER),
120		Text:     depset.NewBuilder[android.Path](depset.POSTORDER),
121		XML:      depset.NewBuilder[android.Path](depset.POSTORDER),
122		Baseline: depset.NewBuilder[android.Path](depset.POSTORDER),
123	}
124}
125
126func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder {
127	l.HTML.Direct(html)
128	l.Text.Direct(text)
129	l.XML.Direct(xml)
130	if baseline.Valid() {
131		l.Baseline.Direct(baseline.Path())
132	}
133	return l
134}
135
136func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder {
137	l.HTML.Transitive(info.TransitiveHTML)
138	l.Text.Transitive(info.TransitiveText)
139	l.XML.Transitive(info.TransitiveXML)
140	l.Baseline.Transitive(info.TransitiveBaseline)
141	return l
142}
143
144func (l LintDepSetsBuilder) Build() LintDepSets {
145	return LintDepSets{
146		HTML:     l.HTML.Build(),
147		Text:     l.Text.Build(),
148		XML:      l.XML.Build(),
149		Baseline: l.Baseline.Build(),
150	}
151}
152
153type lintDatabaseFiles struct {
154	apiVersionsModule       string
155	apiVersionsCopiedName   string
156	apiVersionsPrebuiltPath string
157	annotationsModule       string
158	annotationCopiedName    string
159	annotationPrebuiltpath  string
160}
161
162var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{
163	android.SdkPublic: {
164		apiVersionsModule:       "api_versions_public",
165		apiVersionsCopiedName:   "api_versions_public.xml",
166		apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml",
167		annotationsModule:       "sdk-annotations.zip",
168		annotationCopiedName:    "annotations-public.zip",
169		annotationPrebuiltpath:  "prebuilts/sdk/current/public/data/annotations.zip",
170	},
171	android.SdkSystem: {
172		apiVersionsModule:       "api_versions_system",
173		apiVersionsCopiedName:   "api_versions_system.xml",
174		apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml",
175		annotationsModule:       "sdk-annotations-system.zip",
176		annotationCopiedName:    "annotations-system.zip",
177		annotationPrebuiltpath:  "prebuilts/sdk/current/system/data/annotations.zip",
178	},
179	android.SdkModule: {
180		apiVersionsModule:       "api_versions_module_lib",
181		apiVersionsCopiedName:   "api_versions_module_lib.xml",
182		apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml",
183		annotationsModule:       "sdk-annotations-module-lib.zip",
184		annotationCopiedName:    "annotations-module-lib.zip",
185		annotationPrebuiltpath:  "prebuilts/sdk/current/module-lib/data/annotations.zip",
186	},
187	android.SdkSystemServer: {
188		apiVersionsModule:       "api_versions_system_server",
189		apiVersionsCopiedName:   "api_versions_system_server.xml",
190		apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml",
191		annotationsModule:       "sdk-annotations-system-server.zip",
192		annotationCopiedName:    "annotations-system-server.zip",
193		annotationPrebuiltpath:  "prebuilts/sdk/current/system-server/data/annotations.zip",
194	},
195}
196
197var LintProvider = blueprint.NewProvider[*LintInfo]()
198
199type LintInfo struct {
200	HTML              android.Path
201	Text              android.Path
202	XML               android.Path
203	ReferenceBaseline android.Path
204
205	TransitiveHTML     depset.DepSet[android.Path]
206	TransitiveText     depset.DepSet[android.Path]
207	TransitiveXML      depset.DepSet[android.Path]
208	TransitiveBaseline depset.DepSet[android.Path]
209}
210
211func (l *linter) enabled() bool {
212	return BoolDefault(l.properties.Lint.Enabled, true)
213}
214
215func (l *linter) deps(ctx android.BottomUpMutatorContext) {
216	if !l.enabled() {
217		return
218	}
219
220	extraCheckModules := l.properties.Lint.Extra_check_modules
221
222	if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" {
223		extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...)
224	}
225
226	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
227		extraLintCheckTag, extraCheckModules...)
228}
229
230// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
231// around.
232type lintPaths struct {
233	projectXML android.WritablePath
234	configXML  android.WritablePath
235	cacheDir   android.WritablePath
236	homeDir    android.WritablePath
237	srcjarDir  android.WritablePath
238}
239
240func lintRBEExecStrategy(ctx android.ModuleContext) string {
241	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
242}
243
244func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path,
245	baselines android.Paths) lintPaths {
246
247	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
248	// Lint looks for a lint.xml file next to the project.xml file, give it one.
249	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
250	cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
251	homeDir := android.PathForModuleOut(ctx, "lint", "home")
252
253	srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
254	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
255
256	cmd := rule.Command().
257		BuiltTool("lint_project_xml").
258		FlagWithOutput("--project_out ", projectXMLPath).
259		FlagWithOutput("--config_out ", configXMLPath).
260		FlagWithArg("--name ", ctx.ModuleName())
261
262	if l.library {
263		cmd.Flag("--library")
264	}
265
266	test := proptools.BoolDefault(l.properties.Lint.Test_module_type, false)
267	if l.properties.Lint.Test != nil {
268		test = *l.properties.Lint.Test
269	}
270	if test {
271		cmd.Flag("--test")
272	}
273	if l.manifest != nil {
274		cmd.FlagWithInput("--manifest ", l.manifest)
275	}
276	if l.mergedManifest != nil {
277		cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
278	}
279
280	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
281	// lint separately.
282	cmd.FlagWithInput("--srcs ", srcsList)
283
284	cmd.FlagWithInput("--generated_srcs ", srcJarList)
285
286	if len(l.resources) > 0 {
287		resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
288		cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
289	}
290
291	if l.classes != nil {
292		cmd.FlagWithInput("--classes ", l.classes)
293	}
294
295	cmd.FlagForEachInput("--classpath ", l.classpath)
296
297	cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
298
299	cmd.FlagWithArg("--root_dir ", "$PWD")
300
301	// The cache tag in project.xml is relative to the root dir, or the project.xml file if
302	// the root dir is not set.
303	cmd.FlagWithArg("--cache_dir ", cacheDir.String())
304
305	cmd.FlagWithInput("@",
306		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
307
308	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
309	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
310	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
311	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
312	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
313
314	if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 {
315		// Verify the module does not baseline issues that endanger safe updatability.
316		strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines)
317		cmd.Validation(strictUpdatabilityChecksOutputFile)
318	}
319
320	return lintPaths{
321		projectXML: projectXMLPath,
322		configXML:  configXMLPath,
323		cacheDir:   cacheDir,
324		homeDir:    homeDir,
325	}
326
327}
328
329func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path {
330	rule := android.NewRuleBuilder(pctx, ctx)
331	baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp")
332	outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp")
333	rule.Command().Text("rm -f").Output(outputFile)
334	rule.Command().
335		BuiltTool("lint_strict_updatability_checks").
336		FlagWithArg("--name ", ctx.ModuleName()).
337		FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines).
338		FlagForEachArg("--disallowed_issues ", updatabilityChecks)
339	rule.Command().Text("touch").Output(outputFile)
340	rule.Build("lint_strict_updatability_checks", "lint strict updatability checks")
341
342	return outputFile
343}
344
345// generateManifest adds a command to the rule to write a simple manifest that contains the
346// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
347func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
348	manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
349
350	rule.Command().Text("(").
351		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
352		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
353		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
354		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
355			l.minSdkVersion.String(), l.targetSdkVersion.String()).
356		Text(`echo "</manifest>"`).
357		Text(") >").Output(manifestPath)
358
359	return manifestPath
360}
361
362func (l *linter) lint(ctx android.ModuleContext) {
363	if !l.enabled() {
364		return
365	}
366
367	for _, flag := range l.properties.Lint.Flags {
368		if strings.Contains(flag, "--disable") || strings.Contains(flag, "--enable") || strings.Contains(flag, "--check") {
369			ctx.PropertyErrorf("lint.flags", "Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields")
370		}
371	}
372
373	if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
374		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
375		// Skip lint warning checks for NewApi warnings for libcore where they come from source
376		// files that reference the API they are adding (b/208656169).
377		if !strings.HasPrefix(ctx.ModuleDir(), "libcore") {
378			_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
379
380			if len(filtered) != 0 {
381				ctx.PropertyErrorf("lint.warning_checks",
382					"Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
383			}
384		}
385
386		_, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
387		if len(filtered) != 0 {
388			ctx.PropertyErrorf("lint.disabled_checks",
389				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
390		}
391
392		// TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController
393		if ctx.ModuleName() == "PermissionController" {
394			l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool {
395				return s != "NewApi"
396			})
397			l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi")
398		}
399	}
400
401	extraLintCheckModules := ctx.GetDirectDepsProxyWithTag(extraLintCheckTag)
402	for _, extraLintCheckModule := range extraLintCheckModules {
403		if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok {
404			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
405		} else {
406			ctx.PropertyErrorf("lint.extra_check_modules",
407				"%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
408		}
409	}
410
411	l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx,
412		"prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar"))
413
414	var baseline android.OptionalPath
415	if l.properties.Lint.Baseline_filename != nil {
416		baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
417	}
418
419	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
420	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
421	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
422	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
423
424	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline)
425
426	ctx.VisitDirectDepsProxyWithTag(staticLibTag, func(dep android.ModuleProxy) {
427		if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok {
428			depSetsBuilder.Transitive(info)
429		}
430	})
431
432	depSets := depSetsBuilder.Build()
433
434	rule := android.NewRuleBuilder(pctx, ctx).
435		Sbox(android.PathForModuleOut(ctx, "lint"),
436			android.PathForModuleOut(ctx, "lint.sbox.textproto")).
437		SandboxInputs()
438
439	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
440		pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
441		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
442		rule.Rewrapper(&remoteexec.REParams{
443			Labels:          map[string]string{"type": "tool", "name": "lint"},
444			ExecStrategy:    lintRBEExecStrategy(ctx),
445			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
446			Platform:        map[string]string{remoteexec.PoolKey: pool},
447		})
448	}
449
450	if l.manifest == nil {
451		manifest := l.generateManifest(ctx, rule)
452		l.manifest = manifest
453		rule.Temporary(manifest)
454	}
455
456	srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list")
457	srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
458	rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data)
459
460	baselines := depSets.Baseline.ToList()
461
462	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines)
463
464	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
465	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
466	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
467
468	files, ok := allLintDatabasefiles[l.compileSdkKind]
469	if !ok {
470		files = allLintDatabasefiles[android.SdkPublic]
471	}
472	var annotationsZipPath, apiVersionsXMLPath android.Path
473	if ctx.Config().AlwaysUsePrebuiltSdks() {
474		annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath)
475		apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath)
476	} else {
477		annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName)
478		apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName)
479	}
480
481	cmd := rule.Command()
482
483	cmd.Flag(`JAVA_OPTS="-Xmx4096m --add-opens java.base/java.util=ALL-UNNAMED"`).
484		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
485		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
486		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
487
488	cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
489		Flag("--quiet").
490		Flag("--include-aosp-issues").
491		FlagWithInput("--project ", lintPaths.projectXML).
492		FlagWithInput("--config ", lintPaths.configXML).
493		FlagWithOutput("--html ", html).
494		FlagWithOutput("--text ", text).
495		FlagWithOutput("--xml ", xml).
496		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
497		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
498		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
499		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
500		Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox
501		Flags(l.properties.Lint.Flags).
502		Implicit(annotationsZipPath).
503		Implicit(apiVersionsXMLPath)
504
505	rule.Temporary(lintPaths.projectXML)
506	rule.Temporary(lintPaths.configXML)
507
508	suppressExitCode := BoolDefault(l.properties.Lint.Suppress_exit_code, false)
509	if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" && !suppressExitCode {
510		cmd.Flag("--exitcode")
511	}
512
513	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
514		cmd.FlagWithArg("--check ", checkOnly)
515	}
516
517	if baseline.Valid() {
518		cmd.FlagWithInput("--baseline ", baseline.Path())
519	}
520
521	cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
522
523	cmd.Text("; EXITCODE=$?; ")
524
525	// The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
526	// export them out of the sandbox.  Do this before exiting so that the suggestions exit even after
527	// a fatal error.
528	cmd.BuiltTool("soong_zip").
529		FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
530		FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
531		FlagWithInput("-r ", srcsList)
532
533	cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi")
534
535	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
536
537	// The HTML output contains a date, remove it to make the output deterministic.
538	rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
539
540	rule.Build("lint", "lint")
541
542	android.SetProvider(ctx, LintProvider, &LintInfo{
543		HTML:              html,
544		Text:              text,
545		XML:               xml,
546		ReferenceBaseline: referenceBaseline,
547
548		TransitiveHTML:     depSets.HTML,
549		TransitiveText:     depSets.Text,
550		TransitiveXML:      depSets.XML,
551		TransitiveBaseline: depSets.Baseline,
552	})
553
554	if l.buildModuleReportZip {
555		l.reports = BuildModuleLintReportZips(ctx, depSets, nil)
556	}
557
558	// Create a per-module phony target to run the lint check.
559	phonyName := ctx.ModuleName() + "-lint"
560	ctx.Phony(phonyName, xml)
561
562	ctx.SetOutputFiles(android.Paths{xml}, ".lint")
563}
564
565func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths {
566	htmlList := android.SortedUniquePaths(depSets.HTML.ToList())
567	textList := android.SortedUniquePaths(depSets.Text.ToList())
568	xmlList := android.SortedUniquePaths(depSets.XML.ToList())
569
570	if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
571		return nil
572	}
573
574	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
575	lintZip(ctx, htmlList, htmlZip, validations)
576
577	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
578	lintZip(ctx, textList, textZip, validations)
579
580	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
581	lintZip(ctx, xmlList, xmlZip, validations)
582
583	return android.Paths{htmlZip, textZip, xmlZip}
584}
585
586type lintSingleton struct {
587	htmlZip              android.WritablePath
588	textZip              android.WritablePath
589	xmlZip               android.WritablePath
590	referenceBaselineZip android.WritablePath
591}
592
593func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
594	l.generateLintReportZips(ctx)
595	l.copyLintDependencies(ctx)
596}
597
598func findModuleOrErr(ctx android.SingletonContext, moduleName string) *android.ModuleProxy {
599	var res *android.ModuleProxy
600	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
601		if ctx.ModuleName(m) == moduleName {
602			if res == nil {
603				res = &m
604			} else {
605				ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
606					ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
607			}
608		}
609	})
610	return res
611}
612
613func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
614	if ctx.Config().AlwaysUsePrebuiltSdks() {
615		return
616	}
617
618	for _, sdk := range android.SortedKeys(allLintDatabasefiles) {
619		files := allLintDatabasefiles[sdk]
620		apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule)
621		if apiVersionsDb == nil {
622			if !ctx.Config().AllowMissingDependencies() {
623				ctx.Errorf("lint: missing module %s", files.apiVersionsModule)
624			}
625			return
626		}
627
628		sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule)
629		if sdkAnnotations == nil {
630			if !ctx.Config().AllowMissingDependencies() {
631				ctx.Errorf("lint: missing module %s", files.annotationsModule)
632			}
633			return
634		}
635
636		ctx.Build(pctx, android.BuildParams{
637			Rule:   android.CpIfChanged,
638			Input:  android.OutputFileForModule(ctx, *sdkAnnotations, ""),
639			Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
640		})
641
642		ctx.Build(pctx, android.BuildParams{
643			Rule:   android.CpIfChanged,
644			Input:  android.OutputFileForModule(ctx, *apiVersionsDb, ".api_versions.xml"),
645			Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
646		})
647	}
648}
649
650func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath {
651	return android.PathForOutput(ctx, "lint", name)
652}
653
654func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
655	if ctx.Config().UnbundledBuild() {
656		return
657	}
658
659	var outputs []*LintInfo
660	var dirs []string
661	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
662		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider)
663		if ctx.Config().KatiEnabled() && !commonInfo.ExportedToMake {
664			return
665		}
666
667		if commonInfo.IsApexModule && commonInfo.NotAvailableForPlatform {
668			apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
669			if apexInfo.IsForPlatform() {
670				// There are stray platform variants of modules in apexes that are not available for
671				// the platform, and they sometimes can't be built.  Don't depend on them.
672				return
673			}
674		}
675
676		if lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok {
677			outputs = append(outputs, lintInfo)
678		}
679	})
680
681	dirs = android.SortedUniqueStrings(dirs)
682
683	zip := func(outputPath android.WritablePath, get func(*LintInfo) android.Path) {
684		var paths android.Paths
685
686		for _, output := range outputs {
687			if p := get(output); p != nil {
688				paths = append(paths, p)
689			}
690		}
691
692		lintZip(ctx, paths, outputPath, nil)
693	}
694
695	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
696	zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML })
697
698	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
699	zip(l.textZip, func(l *LintInfo) android.Path { return l.Text })
700
701	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
702	zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML })
703
704	l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
705	zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline })
706
707	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
708
709	if !ctx.Config().UnbundledBuild() {
710		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
711	}
712}
713
714func init() {
715	android.RegisterParallelSingletonType("lint",
716		func() android.Singleton { return &lintSingleton{} })
717}
718
719func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) {
720	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
721
722	sort.Slice(paths, func(i, j int) bool {
723		return paths[i].String() < paths[j].String()
724	})
725
726	rule := android.NewRuleBuilder(pctx, ctx)
727
728	rule.Command().BuiltTool("soong_zip").
729		FlagWithOutput("-o ", outputPath).
730		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
731		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths).
732		Validations(validations)
733
734	rule.Build(outputPath.Base(), outputPath.Base())
735}
736