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/proptools"
23
24	"android/soong/android"
25	"android/soong/java/config"
26	"android/soong/remoteexec"
27)
28
29// lint checks automatically enforced for modules that have different min_sdk_version than
30// sdk_version
31var updatabilityChecks = []string{"NewApi"}
32
33type LintProperties struct {
34	// Controls for running Android Lint on the module.
35	Lint struct {
36
37		// If true, run Android Lint on the module.  Defaults to true.
38		Enabled *bool
39
40		// Flags to pass to the Android Lint tool.
41		Flags []string
42
43		// Checks that should be treated as fatal.
44		Fatal_checks []string
45
46		// Checks that should be treated as errors.
47		Error_checks []string
48
49		// Checks that should be treated as warnings.
50		Warning_checks []string
51
52		// Checks that should be skipped.
53		Disabled_checks []string
54
55		// Modules that provide extra lint checks
56		Extra_check_modules []string
57
58		// Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
59		Baseline_filename *string
60
61		// If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
62		Strict_updatability_linting *bool
63	}
64}
65
66type linter struct {
67	name                    string
68	manifest                android.Path
69	mergedManifest          android.Path
70	srcs                    android.Paths
71	srcJars                 android.Paths
72	resources               android.Paths
73	classpath               android.Paths
74	classes                 android.Path
75	extraLintCheckJars      android.Paths
76	test                    bool
77	library                 bool
78	minSdkVersion           string
79	targetSdkVersion        string
80	compileSdkVersion       string
81	compileSdkKind          android.SdkKind
82	javaLanguageLevel       string
83	kotlinLanguageLevel     string
84	outputs                 lintOutputs
85	properties              LintProperties
86	extraMainlineLintErrors []string
87
88	reports android.Paths
89
90	buildModuleReportZip bool
91}
92
93type lintOutputs struct {
94	html android.Path
95	text android.Path
96	xml  android.Path
97
98	depSets LintDepSets
99}
100
101type lintOutputsIntf interface {
102	lintOutputs() *lintOutputs
103}
104
105type lintDepSetsIntf interface {
106	LintDepSets() LintDepSets
107
108	// Methods used to propagate strict_updatability_linting values.
109	getStrictUpdatabilityLinting() bool
110	setStrictUpdatabilityLinting(bool)
111}
112
113type LintDepSets struct {
114	HTML, Text, XML *android.DepSet
115}
116
117type LintDepSetsBuilder struct {
118	HTML, Text, XML *android.DepSetBuilder
119}
120
121func NewLintDepSetBuilder() LintDepSetsBuilder {
122	return LintDepSetsBuilder{
123		HTML: android.NewDepSetBuilder(android.POSTORDER),
124		Text: android.NewDepSetBuilder(android.POSTORDER),
125		XML:  android.NewDepSetBuilder(android.POSTORDER),
126	}
127}
128
129func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
130	l.HTML.Direct(html)
131	l.Text.Direct(text)
132	l.XML.Direct(xml)
133	return l
134}
135
136func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
137	if depSets.HTML != nil {
138		l.HTML.Transitive(depSets.HTML)
139	}
140	if depSets.Text != nil {
141		l.Text.Transitive(depSets.Text)
142	}
143	if depSets.XML != nil {
144		l.XML.Transitive(depSets.XML)
145	}
146	return l
147}
148
149func (l LintDepSetsBuilder) Build() LintDepSets {
150	return LintDepSets{
151		HTML: l.HTML.Build(),
152		Text: l.Text.Build(),
153		XML:  l.XML.Build(),
154	}
155}
156
157func (l *linter) LintDepSets() LintDepSets {
158	return l.outputs.depSets
159}
160
161func (l *linter) getStrictUpdatabilityLinting() bool {
162	return BoolDefault(l.properties.Lint.Strict_updatability_linting, false)
163}
164
165func (l *linter) setStrictUpdatabilityLinting(strictLinting bool) {
166	l.properties.Lint.Strict_updatability_linting = &strictLinting
167}
168
169var _ lintDepSetsIntf = (*linter)(nil)
170
171var _ lintOutputsIntf = (*linter)(nil)
172
173func (l *linter) lintOutputs() *lintOutputs {
174	return &l.outputs
175}
176
177func (l *linter) enabled() bool {
178	return BoolDefault(l.properties.Lint.Enabled, true)
179}
180
181func (l *linter) deps(ctx android.BottomUpMutatorContext) {
182	if !l.enabled() {
183		return
184	}
185
186	extraCheckModules := l.properties.Lint.Extra_check_modules
187
188	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
189		if checkOnlyModules := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); checkOnlyModules != "" {
190			extraCheckModules = strings.Split(checkOnlyModules, ",")
191		}
192	}
193
194	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
195		extraLintCheckTag, extraCheckModules...)
196}
197
198// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
199// around.
200type lintPaths struct {
201	projectXML android.WritablePath
202	configXML  android.WritablePath
203	cacheDir   android.WritablePath
204	homeDir    android.WritablePath
205	srcjarDir  android.WritablePath
206}
207
208func lintRBEExecStrategy(ctx android.ModuleContext) string {
209	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
210}
211
212func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
213	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
214	// Lint looks for a lint.xml file next to the project.xml file, give it one.
215	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
216	cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
217	homeDir := android.PathForModuleOut(ctx, "lint", "home")
218
219	srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
220	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
221
222	cmd := rule.Command().
223		BuiltTool("lint_project_xml").
224		FlagWithOutput("--project_out ", projectXMLPath).
225		FlagWithOutput("--config_out ", configXMLPath).
226		FlagWithArg("--name ", ctx.ModuleName())
227
228	if l.library {
229		cmd.Flag("--library")
230	}
231	if l.test {
232		cmd.Flag("--test")
233	}
234	if l.manifest != nil {
235		cmd.FlagWithInput("--manifest ", l.manifest)
236	}
237	if l.mergedManifest != nil {
238		cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
239	}
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	cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
245
246	cmd.FlagWithInput("--generated_srcs ", srcJarList)
247
248	if len(l.resources) > 0 {
249		resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
250		cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
251	}
252
253	if l.classes != nil {
254		cmd.FlagWithInput("--classes ", l.classes)
255	}
256
257	cmd.FlagForEachInput("--classpath ", l.classpath)
258
259	cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
260
261	cmd.FlagWithArg("--root_dir ", "$PWD")
262
263	// The cache tag in project.xml is relative to the root dir, or the project.xml file if
264	// the root dir is not set.
265	cmd.FlagWithArg("--cache_dir ", cacheDir.String())
266
267	cmd.FlagWithInput("@",
268		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
269
270	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
271	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
272	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
273	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
274	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
275
276	if l.getStrictUpdatabilityLinting() {
277		// Verify the module does not baseline issues that endanger safe updatability.
278		if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
279			cmd.FlagWithInput("--baseline ", baselinePath.Path())
280			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
281		}
282	}
283
284	return lintPaths{
285		projectXML: projectXMLPath,
286		configXML:  configXMLPath,
287		cacheDir:   cacheDir,
288		homeDir:    homeDir,
289	}
290
291}
292
293// generateManifest adds a command to the rule to write a simple manifest that contains the
294// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
295func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
296	manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
297
298	rule.Command().Text("(").
299		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
300		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
301		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
302		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
303			l.minSdkVersion, l.targetSdkVersion).
304		Text(`echo "</manifest>"`).
305		Text(") >").Output(manifestPath)
306
307	return manifestPath
308}
309
310func (l *linter) getBaselineFilepath(ctx android.ModuleContext) android.OptionalPath {
311	var lintBaseline android.OptionalPath
312	if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
313		if String(l.properties.Lint.Baseline_filename) != "" {
314			// if manually specified, we require the file to exist
315			lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
316		} else {
317			lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
318		}
319	}
320	return lintBaseline
321}
322
323func (l *linter) lint(ctx android.ModuleContext) {
324	if !l.enabled() {
325		return
326	}
327
328	if l.minSdkVersion != l.compileSdkVersion {
329		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
330		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
331		if len(filtered) != 0 {
332			ctx.PropertyErrorf("lint.warning_checks",
333				"Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
334		}
335		_, filtered = android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
336		if len(filtered) != 0 {
337			ctx.PropertyErrorf("lint.disabled_checks",
338				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
339		}
340	}
341
342	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
343	for _, extraLintCheckModule := range extraLintCheckModules {
344		if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
345			dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
346			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
347		} else {
348			ctx.PropertyErrorf("lint.extra_check_modules",
349				"%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
350		}
351	}
352
353	rule := android.NewRuleBuilder(pctx, ctx).
354		Sbox(android.PathForModuleOut(ctx, "lint"),
355			android.PathForModuleOut(ctx, "lint.sbox.textproto")).
356		SandboxInputs()
357
358	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
359		pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
360		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
361		rule.Rewrapper(&remoteexec.REParams{
362			Labels:          map[string]string{"type": "tool", "name": "lint"},
363			ExecStrategy:    lintRBEExecStrategy(ctx),
364			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
365			Platform:        map[string]string{remoteexec.PoolKey: pool},
366		})
367	}
368
369	if l.manifest == nil {
370		manifest := l.generateManifest(ctx, rule)
371		l.manifest = manifest
372		rule.Temporary(manifest)
373	}
374
375	lintPaths := l.writeLintProjectXML(ctx, rule)
376
377	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
378	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
379	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
380
381	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
382
383	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
384		if depLint, ok := dep.(lintDepSetsIntf); ok {
385			depSetsBuilder.Transitive(depLint.LintDepSets())
386		}
387	})
388
389	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
390	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
391	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
392
393	var apiVersionsName, apiVersionsPrebuilt string
394	if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer {
395		// When compiling an SDK module (or system server) we use the filtered
396		// database because otherwise lint's
397		// NewApi check produces too many false positives; This database excludes information
398		// about classes created in mainline modules hence removing those false positives.
399		apiVersionsName = "api_versions_public_filtered.xml"
400		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml"
401	} else {
402		apiVersionsName = "api_versions.xml"
403		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml"
404	}
405
406	var annotationsZipPath, apiVersionsXMLPath android.Path
407	if ctx.Config().AlwaysUsePrebuiltSdks() {
408		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
409		apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt)
410	} else {
411		annotationsZipPath = copiedAnnotationsZipPath(ctx)
412		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName)
413	}
414
415	cmd := rule.Command()
416
417	cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
418		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
419		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
420		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
421
422	cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
423		Flag("--quiet").
424		FlagWithInput("--project ", lintPaths.projectXML).
425		FlagWithInput("--config ", lintPaths.configXML).
426		FlagWithOutput("--html ", html).
427		FlagWithOutput("--text ", text).
428		FlagWithOutput("--xml ", xml).
429		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
430		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
431		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
432		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
433		Flag("--exitcode").
434		Flags(l.properties.Lint.Flags).
435		Implicit(annotationsZipPath).
436		Implicit(apiVersionsXMLPath)
437
438	rule.Temporary(lintPaths.projectXML)
439	rule.Temporary(lintPaths.configXML)
440
441	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
442		cmd.FlagWithArg("--check ", checkOnly)
443	}
444
445	lintBaseline := l.getBaselineFilepath(ctx)
446	if lintBaseline.Valid() {
447		cmd.FlagWithInput("--baseline ", lintBaseline.Path())
448	}
449
450	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
451
452	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
453
454	// The HTML output contains a date, remove it to make the output deterministic.
455	rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
456
457	rule.Build("lint", "lint")
458
459	l.outputs = lintOutputs{
460		html: html,
461		text: text,
462		xml:  xml,
463
464		depSets: depSetsBuilder.Build(),
465	}
466
467	if l.buildModuleReportZip {
468		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
469	}
470}
471
472func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
473	htmlList := depSets.HTML.ToSortedList()
474	textList := depSets.Text.ToSortedList()
475	xmlList := depSets.XML.ToSortedList()
476
477	if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
478		return nil
479	}
480
481	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
482	lintZip(ctx, htmlList, htmlZip)
483
484	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
485	lintZip(ctx, textList, textZip)
486
487	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
488	lintZip(ctx, xmlList, xmlZip)
489
490	return android.Paths{htmlZip, textZip, xmlZip}
491}
492
493type lintSingleton struct {
494	htmlZip android.WritablePath
495	textZip android.WritablePath
496	xmlZip  android.WritablePath
497}
498
499func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
500	l.generateLintReportZips(ctx)
501	l.copyLintDependencies(ctx)
502}
503
504func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
505	var res android.Module
506	ctx.VisitAllModules(func(m android.Module) {
507		if ctx.ModuleName(m) == moduleName {
508			if res == nil {
509				res = m
510			} else {
511				ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
512					ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
513			}
514		}
515	})
516	return res
517}
518
519func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
520	if ctx.Config().AlwaysUsePrebuiltSdks() {
521		return
522	}
523
524	frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs")
525	if frameworkDocStubs == nil {
526		if !ctx.Config().AllowMissingDependencies() {
527			ctx.Errorf("lint: missing framework-doc-stubs")
528		}
529		return
530	}
531
532	filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered")
533	if filteredDb == nil {
534		if !ctx.Config().AllowMissingDependencies() {
535			ctx.Errorf("lint: missing api-versions-xml-public-filtered")
536		}
537		return
538	}
539
540	ctx.Build(pctx, android.BuildParams{
541		Rule:   android.CpIfChanged,
542		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
543		Output: copiedAnnotationsZipPath(ctx),
544	})
545
546	ctx.Build(pctx, android.BuildParams{
547		Rule:   android.CpIfChanged,
548		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
549		Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
550	})
551
552	ctx.Build(pctx, android.BuildParams{
553		Rule:   android.CpIfChanged,
554		Input:  android.OutputFileForModule(ctx, filteredDb, ""),
555		Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"),
556	})
557}
558
559func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
560	return android.PathForOutput(ctx, "lint", "annotations.zip")
561}
562
563func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath {
564	return android.PathForOutput(ctx, "lint", name)
565}
566
567func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
568	if ctx.Config().UnbundledBuild() {
569		return
570	}
571
572	var outputs []*lintOutputs
573	var dirs []string
574	ctx.VisitAllModules(func(m android.Module) {
575		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
576			return
577		}
578
579		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
580			apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
581			if apexInfo.IsForPlatform() {
582				// There are stray platform variants of modules in apexes that are not available for
583				// the platform, and they sometimes can't be built.  Don't depend on them.
584				return
585			}
586		}
587
588		if l, ok := m.(lintOutputsIntf); ok {
589			outputs = append(outputs, l.lintOutputs())
590		}
591	})
592
593	dirs = android.SortedUniqueStrings(dirs)
594
595	zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
596		var paths android.Paths
597
598		for _, output := range outputs {
599			if p := get(output); p != nil {
600				paths = append(paths, p)
601			}
602		}
603
604		lintZip(ctx, paths, outputPath)
605	}
606
607	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
608	zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
609
610	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
611	zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
612
613	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
614	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
615
616	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
617}
618
619func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
620	if !ctx.Config().UnbundledBuild() {
621		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
622	}
623}
624
625var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
626
627func init() {
628	android.RegisterSingletonType("lint",
629		func() android.Singleton { return &lintSingleton{} })
630
631	registerLintBuildComponents(android.InitRegistrationContext)
632}
633
634func registerLintBuildComponents(ctx android.RegistrationContext) {
635	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
636		ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel()
637	})
638}
639
640func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
641	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
642
643	sort.Slice(paths, func(i, j int) bool {
644		return paths[i].String() < paths[j].String()
645	})
646
647	rule := android.NewRuleBuilder(pctx, ctx)
648
649	rule.Command().BuiltTool("soong_zip").
650		FlagWithOutput("-o ", outputPath).
651		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
652		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
653
654	rule.Build(outputPath.Base(), outputPath.Base())
655}
656
657// Enforce the strict updatability linting to all applicable transitive dependencies.
658func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
659	m := ctx.Module()
660	if d, ok := m.(lintDepSetsIntf); ok && d.getStrictUpdatabilityLinting() {
661		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
662			if a, ok := d.(lintDepSetsIntf); ok {
663				a.setStrictUpdatabilityLinting(true)
664			}
665		})
666	}
667}
668