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