• 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/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           android.ApiLevel
79	targetSdkVersion        android.ApiLevel
80	compileSdkVersion       android.ApiLevel
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.String(), l.targetSdkVersion.String()).
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.CompareTo(l.compileSdkVersion) == -1 {
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	baseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
381
382	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
383
384	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
385		if depLint, ok := dep.(LintDepSetsIntf); ok {
386			depSetsBuilder.Transitive(depLint.LintDepSets())
387		}
388	})
389
390	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
391	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
392	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
393
394	var apiVersionsName, apiVersionsPrebuilt string
395	if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer {
396		// When compiling an SDK module (or system server) we use the filtered
397		// database because otherwise lint's
398		// NewApi check produces too many false positives; This database excludes information
399		// about classes created in mainline modules hence removing those false positives.
400		apiVersionsName = "api_versions_public_filtered.xml"
401		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml"
402	} else {
403		apiVersionsName = "api_versions.xml"
404		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml"
405	}
406
407	var annotationsZipPath, apiVersionsXMLPath android.Path
408	if ctx.Config().AlwaysUsePrebuiltSdks() {
409		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
410		apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt)
411	} else {
412		annotationsZipPath = copiedAnnotationsZipPath(ctx)
413		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName)
414	}
415
416	cmd := rule.Command()
417
418	cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
419		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
420		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
421		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
422
423	cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
424		Flag("--quiet").
425		FlagWithInput("--project ", lintPaths.projectXML).
426		FlagWithInput("--config ", lintPaths.configXML).
427		FlagWithOutput("--html ", html).
428		FlagWithOutput("--text ", text).
429		FlagWithOutput("--xml ", xml).
430		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
431		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
432		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
433		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
434		Flag("--exitcode").
435		Flags(l.properties.Lint.Flags).
436		Implicit(annotationsZipPath).
437		Implicit(apiVersionsXMLPath)
438
439	rule.Temporary(lintPaths.projectXML)
440	rule.Temporary(lintPaths.configXML)
441
442	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
443		cmd.FlagWithArg("--check ", checkOnly)
444	}
445
446	lintBaseline := l.getBaselineFilepath(ctx)
447	if lintBaseline.Valid() {
448		cmd.FlagWithInput("--baseline ", lintBaseline.Path())
449	}
450
451	cmd.FlagWithOutput("--write-reference-baseline ", baseline)
452
453	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
454
455	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
456
457	// The HTML output contains a date, remove it to make the output deterministic.
458	rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
459
460	rule.Build("lint", "lint")
461
462	l.outputs = lintOutputs{
463		html: html,
464		text: text,
465		xml:  xml,
466
467		depSets: depSetsBuilder.Build(),
468	}
469
470	if l.buildModuleReportZip {
471		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
472	}
473}
474
475func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
476	htmlList := depSets.HTML.ToSortedList()
477	textList := depSets.Text.ToSortedList()
478	xmlList := depSets.XML.ToSortedList()
479
480	if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
481		return nil
482	}
483
484	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
485	lintZip(ctx, htmlList, htmlZip)
486
487	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
488	lintZip(ctx, textList, textZip)
489
490	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
491	lintZip(ctx, xmlList, xmlZip)
492
493	return android.Paths{htmlZip, textZip, xmlZip}
494}
495
496type lintSingleton struct {
497	htmlZip android.WritablePath
498	textZip android.WritablePath
499	xmlZip  android.WritablePath
500}
501
502func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
503	l.generateLintReportZips(ctx)
504	l.copyLintDependencies(ctx)
505}
506
507func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
508	var res android.Module
509	ctx.VisitAllModules(func(m android.Module) {
510		if ctx.ModuleName(m) == moduleName {
511			if res == nil {
512				res = m
513			} else {
514				ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
515					ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
516			}
517		}
518	})
519	return res
520}
521
522func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
523	if ctx.Config().AlwaysUsePrebuiltSdks() {
524		return
525	}
526
527	frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs")
528	if frameworkDocStubs == nil {
529		if !ctx.Config().AllowMissingDependencies() {
530			ctx.Errorf("lint: missing framework-doc-stubs")
531		}
532		return
533	}
534
535	filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered")
536	if filteredDb == nil {
537		if !ctx.Config().AllowMissingDependencies() {
538			ctx.Errorf("lint: missing api-versions-xml-public-filtered")
539		}
540		return
541	}
542
543	ctx.Build(pctx, android.BuildParams{
544		Rule:   android.CpIfChanged,
545		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
546		Output: copiedAnnotationsZipPath(ctx),
547	})
548
549	ctx.Build(pctx, android.BuildParams{
550		Rule:   android.CpIfChanged,
551		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
552		Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
553	})
554
555	ctx.Build(pctx, android.BuildParams{
556		Rule:   android.CpIfChanged,
557		Input:  android.OutputFileForModule(ctx, filteredDb, ""),
558		Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"),
559	})
560}
561
562func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
563	return android.PathForOutput(ctx, "lint", "annotations.zip")
564}
565
566func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath {
567	return android.PathForOutput(ctx, "lint", name)
568}
569
570func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
571	if ctx.Config().UnbundledBuild() {
572		return
573	}
574
575	var outputs []*lintOutputs
576	var dirs []string
577	ctx.VisitAllModules(func(m android.Module) {
578		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
579			return
580		}
581
582		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
583			apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
584			if apexInfo.IsForPlatform() {
585				// There are stray platform variants of modules in apexes that are not available for
586				// the platform, and they sometimes can't be built.  Don't depend on them.
587				return
588			}
589		}
590
591		if l, ok := m.(lintOutputsIntf); ok {
592			outputs = append(outputs, l.lintOutputs())
593		}
594	})
595
596	dirs = android.SortedUniqueStrings(dirs)
597
598	zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
599		var paths android.Paths
600
601		for _, output := range outputs {
602			if p := get(output); p != nil {
603				paths = append(paths, p)
604			}
605		}
606
607		lintZip(ctx, paths, outputPath)
608	}
609
610	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
611	zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
612
613	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
614	zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
615
616	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
617	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
618
619	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
620}
621
622func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
623	if !ctx.Config().UnbundledBuild() {
624		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
625	}
626}
627
628var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
629
630func init() {
631	android.RegisterSingletonType("lint",
632		func() android.Singleton { return &lintSingleton{} })
633
634	registerLintBuildComponents(android.InitRegistrationContext)
635}
636
637func registerLintBuildComponents(ctx android.RegistrationContext) {
638	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
639		ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel()
640	})
641}
642
643func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
644	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
645
646	sort.Slice(paths, func(i, j int) bool {
647		return paths[i].String() < paths[j].String()
648	})
649
650	rule := android.NewRuleBuilder(pctx, ctx)
651
652	rule.Command().BuiltTool("soong_zip").
653		FlagWithOutput("-o ", outputPath).
654		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
655		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
656
657	rule.Build(outputPath.Base(), outputPath.Base())
658}
659
660// Enforce the strict updatability linting to all applicable transitive dependencies.
661func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
662	m := ctx.Module()
663	if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() {
664		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
665			if a, ok := d.(LintDepSetsIntf); ok {
666				a.SetStrictUpdatabilityLinting(true)
667			}
668		})
669	}
670}
671