• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 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	"path/filepath"
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// The values allowed for Droidstubs' Api_levels_sdk_type
30var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"}
31
32func init() {
33	RegisterStubsBuildComponents(android.InitRegistrationContext)
34}
35
36func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
37	ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
38
39	ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
40	ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
41
42	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
43}
44
45//
46// Droidstubs
47//
48type Droidstubs struct {
49	Javadoc
50	android.SdkBase
51
52	properties              DroidstubsProperties
53	apiFile                 android.WritablePath
54	apiXmlFile              android.WritablePath
55	lastReleasedApiXmlFile  android.WritablePath
56	privateApiFile          android.WritablePath
57	removedApiFile          android.WritablePath
58	nullabilityWarningsFile android.WritablePath
59
60	checkCurrentApiTimestamp      android.WritablePath
61	updateCurrentApiTimestamp     android.WritablePath
62	checkLastReleasedApiTimestamp android.WritablePath
63	apiLintTimestamp              android.WritablePath
64	apiLintReport                 android.WritablePath
65
66	checkNullabilityWarningsTimestamp android.WritablePath
67
68	annotationsZip android.WritablePath
69	apiVersionsXml android.WritablePath
70
71	apiFilePath        android.Path
72	removedApiFilePath android.Path
73
74	metadataZip android.WritablePath
75	metadataDir android.WritablePath
76}
77
78type DroidstubsProperties struct {
79	// The generated public API filename by Metalava, defaults to <module>_api.txt
80	Api_filename *string
81
82	// the generated removed API filename by Metalava, defaults to <module>_removed.txt
83	Removed_api_filename *string
84
85	Check_api struct {
86		Last_released ApiToCheck
87
88		Current ApiToCheck
89
90		Api_lint struct {
91			Enabled *bool
92
93			// If set, performs api_lint on any new APIs not found in the given signature file
94			New_since *string `android:"path"`
95
96			// If not blank, path to the baseline txt file for approved API lint violations.
97			Baseline_file *string `android:"path"`
98		}
99	}
100
101	// user can specify the version of previous released API file in order to do compatibility check.
102	Previous_api *string `android:"path"`
103
104	// is set to true, Metalava will allow framework SDK to contain annotations.
105	Annotations_enabled *bool
106
107	// a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
108	Merge_annotations_dirs []string
109
110	// a list of top-level directories containing Java stub files to merge show/hide annotations from.
111	Merge_inclusion_annotations_dirs []string
112
113	// a file containing a list of classes to do nullability validation for.
114	Validate_nullability_from_list *string
115
116	// a file containing expected warnings produced by validation of nullability annotations.
117	Check_nullability_warnings *string
118
119	// if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
120	Create_doc_stubs *bool
121
122	// if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
123	// Has no effect if create_doc_stubs: true.
124	Output_javadoc_comments *bool
125
126	// if set to false then do not write out stubs. Defaults to true.
127	//
128	// TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
129	Generate_stubs *bool
130
131	// if set to true, provides a hint to the build system that this rule uses a lot of memory,
132	// whicih can be used for scheduling purposes
133	High_mem *bool
134
135	// if set to true, Metalava will allow framework SDK to contain API levels annotations.
136	Api_levels_annotations_enabled *bool
137
138	// the dirs which Metalava extracts API levels annotations from.
139	Api_levels_annotations_dirs []string
140
141	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public.
142	Api_levels_sdk_type *string
143
144	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
145	Api_levels_jar_filename *string
146
147	// if set to true, collect the values used by the Dev tools and
148	// write them in files packaged with the SDK. Defaults to false.
149	Write_sdk_values *bool
150}
151
152// Used by xsd_config
153type ApiFilePath interface {
154	ApiFilePath() android.Path
155}
156
157type ApiStubsSrcProvider interface {
158	StubsSrcJar() android.Path
159}
160
161// Provider of information about API stubs, used by java_sdk_library.
162type ApiStubsProvider interface {
163	AnnotationsZip() android.Path
164	ApiFilePath
165	RemovedApiFilePath() android.Path
166
167	ApiStubsSrcProvider
168}
169
170// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
171// documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
172// a droiddoc module to generate documentation.
173func DroidstubsFactory() android.Module {
174	module := &Droidstubs{}
175
176	module.AddProperties(&module.properties,
177		&module.Javadoc.properties)
178
179	InitDroiddocModule(module, android.HostAndDeviceSupported)
180	android.InitSdkAwareModule(module)
181	return module
182}
183
184// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
185// to be documented, filtering out hidden classes and methods.  The resulting .java files are intended to be
186// passed to a droiddoc_host module to generate documentation.  Use a droidstubs_host instead of a droidstubs
187// module when symbols needed by the source files are provided by java_library_host modules.
188func DroidstubsHostFactory() android.Module {
189	module := &Droidstubs{}
190
191	module.AddProperties(&module.properties,
192		&module.Javadoc.properties)
193
194	InitDroiddocModule(module, android.HostSupported)
195	return module
196}
197
198func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
199	switch tag {
200	case "":
201		return android.Paths{d.stubsSrcJar}, nil
202	case ".docs.zip":
203		return android.Paths{d.docZip}, nil
204	case ".api.txt", android.DefaultDistTag:
205		// This is the default dist path for dist properties that have no tag property.
206		return android.Paths{d.apiFilePath}, nil
207	case ".removed-api.txt":
208		return android.Paths{d.removedApiFilePath}, nil
209	case ".annotations.zip":
210		return android.Paths{d.annotationsZip}, nil
211	case ".api_versions.xml":
212		return android.Paths{d.apiVersionsXml}, nil
213	default:
214		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
215	}
216}
217
218func (d *Droidstubs) AnnotationsZip() android.Path {
219	return d.annotationsZip
220}
221
222func (d *Droidstubs) ApiFilePath() android.Path {
223	return d.apiFilePath
224}
225
226func (d *Droidstubs) RemovedApiFilePath() android.Path {
227	return d.removedApiFilePath
228}
229
230func (d *Droidstubs) StubsSrcJar() android.Path {
231	return d.stubsSrcJar
232}
233
234var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
235var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
236var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
237
238func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
239	d.Javadoc.addDeps(ctx)
240
241	if len(d.properties.Merge_annotations_dirs) != 0 {
242		for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
243			ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
244		}
245	}
246
247	if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
248		for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
249			ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
250		}
251	}
252
253	if len(d.properties.Api_levels_annotations_dirs) != 0 {
254		for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
255			ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
256		}
257	}
258}
259
260func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
261	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
262		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
263		String(d.properties.Api_filename) != "" {
264		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
265		d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
266		cmd.FlagWithOutput("--api ", d.apiFile)
267		d.apiFilePath = d.apiFile
268	} else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
269		// If check api is disabled then make the source file available for export.
270		d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
271	}
272
273	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
274		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
275		String(d.properties.Removed_api_filename) != "" {
276		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
277		d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
278		cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
279		d.removedApiFilePath = d.removedApiFile
280	} else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
281		// If check api is disabled then make the source removed api file available for export.
282		d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
283	}
284
285	if Bool(d.properties.Write_sdk_values) {
286		d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
287		cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
288	}
289
290	if stubsDir.Valid() {
291		if Bool(d.properties.Create_doc_stubs) {
292			cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
293		} else {
294			cmd.FlagWithArg("--stubs ", stubsDir.String())
295			if !Bool(d.properties.Output_javadoc_comments) {
296				cmd.Flag("--exclude-documentation-from-stubs")
297			}
298		}
299	}
300}
301
302func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
303	if Bool(d.properties.Annotations_enabled) {
304		cmd.Flag("--include-annotations")
305
306		cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi")
307
308		validatingNullability :=
309			strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
310				String(d.properties.Validate_nullability_from_list) != ""
311
312		migratingNullability := String(d.properties.Previous_api) != ""
313		if migratingNullability {
314			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
315			cmd.FlagWithInput("--migrate-nullness ", previousApi)
316		}
317
318		if s := String(d.properties.Validate_nullability_from_list); s != "" {
319			cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
320		}
321
322		if validatingNullability {
323			d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
324			cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
325		}
326
327		d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
328		cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
329
330		if len(d.properties.Merge_annotations_dirs) != 0 {
331			d.mergeAnnoDirFlags(ctx, cmd)
332		}
333
334		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
335		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
336			FlagWithArg("--hide ", "SuperfluousPrefix").
337			FlagWithArg("--hide ", "AnnotationExtraction").
338			// b/222738070
339			FlagWithArg("--hide ", "BannedThrow").
340			// b/223382732
341			FlagWithArg("--hide ", "ChangedDefault")
342	}
343}
344
345func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
346	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
347		if t, ok := m.(*ExportedDroiddocDir); ok {
348			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
349		} else {
350			ctx.PropertyErrorf("merge_annotations_dirs",
351				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
352		}
353	})
354}
355
356func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
357	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
358		if t, ok := m.(*ExportedDroiddocDir); ok {
359			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
360		} else {
361			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
362				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
363		}
364	})
365}
366
367func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
368	if !Bool(d.properties.Api_levels_annotations_enabled) {
369		return
370	}
371
372	d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
373
374	if len(d.properties.Api_levels_annotations_dirs) == 0 {
375		ctx.PropertyErrorf("api_levels_annotations_dirs",
376			"has to be non-empty if api levels annotations was enabled!")
377	}
378
379	cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
380	cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
381	cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
382	cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
383
384	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
385
386	var dirs []string
387	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
388		if t, ok := m.(*ExportedDroiddocDir); ok {
389			for _, dep := range t.deps {
390				if dep.Base() == filename {
391					cmd.Implicit(dep)
392				}
393				if filename != "android.jar" && dep.Base() == "android.jar" {
394					// Metalava implicitly searches these patterns:
395					//  prebuilts/tools/common/api-versions/android-%/android.jar
396					//  prebuilts/sdk/%/public/android.jar
397					// Add android.jar files from the api_levels_annotations_dirs directories to try
398					// to satisfy these patterns.  If Metalava can't find a match for an API level
399					// between 1 and 28 in at least one pattern it will fail.
400					cmd.Implicit(dep)
401				}
402			}
403
404			dirs = append(dirs, t.dir.String())
405		} else {
406			ctx.PropertyErrorf("api_levels_annotations_dirs",
407				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
408		}
409	})
410
411	// Add all relevant --android-jar-pattern patterns for Metalava.
412	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
413	// an actual file present on disk (in the order the patterns were passed). For system APIs for
414	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
415	// for older releases. Similarly, module-lib falls back to system API.
416	var sdkDirs []string
417	switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") {
418	case "module-lib":
419		sdkDirs = []string{"module-lib", "system", "public"}
420	case "system":
421		sdkDirs = []string{"system", "public"}
422	case "public":
423		sdkDirs = []string{"public"}
424	default:
425		ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
426		return
427	}
428
429	for _, sdkDir := range sdkDirs {
430		for _, dir := range dirs {
431			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
432		}
433	}
434}
435
436func metalavaUseRbe(ctx android.ModuleContext) bool {
437	return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA")
438}
439
440func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
441	srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
442	rule.Command().Text("rm -rf").Flag(homeDir.String())
443	rule.Command().Text("mkdir -p").Flag(homeDir.String())
444
445	cmd := rule.Command()
446	cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
447
448	if metalavaUseRbe(ctx) {
449		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
450		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
451		labels := map[string]string{"type": "tool", "name": "metalava"}
452		// TODO: metalava pool rejects these jobs
453		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
454		rule.Rewrapper(&remoteexec.REParams{
455			Labels:          labels,
456			ExecStrategy:    execStrategy,
457			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
458			Platform:        map[string]string{remoteexec.PoolKey: pool},
459		})
460	}
461
462	cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
463		Flag(config.JavacVmFlags).
464		Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
465		FlagWithArg("-encoding ", "UTF-8").
466		FlagWithArg("-source ", javaVersion.String()).
467		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
468		FlagWithInput("@", srcJarList)
469
470	if len(bootclasspath) > 0 {
471		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
472	}
473
474	if len(classpath) > 0 {
475		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
476	}
477
478	cmd.Flag("--no-banner").
479		Flag("--color").
480		Flag("--quiet").
481		Flag("--format=v2").
482		FlagWithArg("--repeat-errors-max ", "10").
483		FlagWithArg("--hide ", "UnresolvedImport").
484		FlagWithArg("--hide ", "InvalidNullabilityOverride").
485		// b/223382732
486		FlagWithArg("--hide ", "ChangedDefault")
487
488	return cmd
489}
490
491func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
492	deps := d.Javadoc.collectDeps(ctx)
493
494	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
495
496	// Create rule for metalava
497
498	srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
499
500	rule := android.NewRuleBuilder(pctx, ctx)
501
502	rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
503		android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
504		SandboxInputs()
505
506	if BoolDefault(d.properties.High_mem, false) {
507		// This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
508		rule.HighMem()
509	}
510
511	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
512	var stubsDir android.OptionalPath
513	if generateStubs {
514		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
515		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
516		rule.Command().Text("rm -rf").Text(stubsDir.String())
517		rule.Command().Text("mkdir -p").Text(stubsDir.String())
518	}
519
520	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
521
522	homeDir := android.PathForModuleOut(ctx, "metalava", "home")
523	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
524		deps.bootClasspath, deps.classpath, homeDir)
525	cmd.Implicits(d.Javadoc.implicits)
526
527	d.stubsFlags(ctx, cmd, stubsDir)
528
529	d.annotationsFlags(ctx, cmd)
530	d.inclusionAnnotationsFlags(ctx, cmd)
531	d.apiLevelsAnnotationsFlags(ctx, cmd)
532
533	d.expandArgs(ctx, cmd)
534
535	for _, o := range d.Javadoc.properties.Out {
536		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
537	}
538
539	// Add options for the other optional tasks: API-lint and check-released.
540	// We generate separate timestamp files for them.
541
542	doApiLint := false
543	doCheckReleased := false
544
545	// Add API lint options.
546
547	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
548		doApiLint = true
549
550		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
551		if newSince.Valid() {
552			cmd.FlagWithInput("--api-lint ", newSince.Path())
553		} else {
554			cmd.Flag("--api-lint")
555		}
556		d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
557		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
558
559		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
560		if d.Name() != "android.car-system-stubs-docs" &&
561			d.Name() != "android.car-stubs-docs" {
562			cmd.Flag("--lints-as-errors")
563			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
564		}
565
566		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
567		updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
568		d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
569
570		// Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
571		//
572		// TODO: metalava also has a slightly different message hardcoded. Should we unify this
573		// message and metalava's one?
574		msg := `$'` + // Enclose with $' ... '
575			`************************************************************\n` +
576			`Your API changes are triggering API Lint warnings or errors.\n` +
577			`To make these errors go away, fix the code according to the\n` +
578			`error and/or warning messages above.\n` +
579			`\n` +
580			`If it is not possible to do so, there are workarounds:\n` +
581			`\n` +
582			`1. You can suppress the errors with @SuppressLint("<id>")\n` +
583			`   where the <id> is given in brackets in the error message above.\n`
584
585		if baselineFile.Valid() {
586			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
587			cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
588
589			msg += fmt.Sprintf(``+
590				`2. You can update the baseline by executing the following\n`+
591				`   command:\n`+
592				`       (cd $ANDROID_BUILD_TOP && cp \\\n`+
593				`       "%s" \\\n`+
594				`       "%s")\n`+
595				`   To submit the revised baseline.txt to the main Android\n`+
596				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
597		} else {
598			msg += fmt.Sprintf(``+
599				`2. You can add a baseline file of existing lint failures\n`+
600				`   to the build rule of %s.\n`, d.Name())
601		}
602		// Note the message ends with a ' (single quote), to close the $' ... ' .
603		msg += `************************************************************\n'`
604
605		cmd.FlagWithArg("--error-message:api-lint ", msg)
606	}
607
608	// Add "check released" options. (Detect incompatible API changes from the last public release)
609
610	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
611		doCheckReleased = true
612
613		if len(d.Javadoc.properties.Out) > 0 {
614			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
615		}
616
617		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
618		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
619		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
620		updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
621
622		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
623
624		cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
625		cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
626
627		if baselineFile.Valid() {
628			cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
629			cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
630		}
631
632		// Note this string includes quote ($' ... '), which decodes the "\n"s.
633		msg := `$'\n******************************\n` +
634			`You have tried to change the API from what has been previously released in\n` +
635			`an SDK.  Please fix the errors listed above.\n` +
636			`******************************\n'`
637
638		cmd.FlagWithArg("--error-message:compatibility:released ", msg)
639	}
640
641	if generateStubs {
642		rule.Command().
643			BuiltTool("soong_zip").
644			Flag("-write_if_changed").
645			Flag("-jar").
646			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
647			FlagWithArg("-C ", stubsDir.String()).
648			FlagWithArg("-D ", stubsDir.String())
649	}
650
651	if Bool(d.properties.Write_sdk_values) {
652		d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
653		rule.Command().
654			BuiltTool("soong_zip").
655			Flag("-write_if_changed").
656			Flag("-d").
657			FlagWithOutput("-o ", d.metadataZip).
658			FlagWithArg("-C ", d.metadataDir.String()).
659			FlagWithArg("-D ", d.metadataDir.String())
660	}
661
662	// TODO: We don't really need two separate API files, but this is a reminiscence of how
663	// we used to run metalava separately for API lint and the "last_released" check. Unify them.
664	if doApiLint {
665		rule.Command().Text("touch").Output(d.apiLintTimestamp)
666	}
667	if doCheckReleased {
668		rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
669	}
670
671	// TODO(b/183630617): rewrapper doesn't support restat rules
672	if !metalavaUseRbe(ctx) {
673		rule.Restat()
674	}
675
676	zipSyncCleanupCmd(rule, srcJarDir)
677
678	rule.Build("metalava", "metalava merged")
679
680	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
681
682		if len(d.Javadoc.properties.Out) > 0 {
683			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
684		}
685
686		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
687		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
688		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
689
690		if baselineFile.Valid() {
691			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
692		}
693
694		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
695
696		rule := android.NewRuleBuilder(pctx, ctx)
697
698		// Diff command line.
699		// -F matches the closest "opening" line, such as "package android {"
700		// and "  public class Intent {".
701		diff := `diff -u -F '{ *$'`
702
703		rule.Command().Text("( true")
704		rule.Command().
705			Text(diff).
706			Input(apiFile).Input(d.apiFile)
707
708		rule.Command().
709			Text(diff).
710			Input(removedApiFile).Input(d.removedApiFile)
711
712		msg := fmt.Sprintf(`\n******************************\n`+
713			`You have tried to change the API from what has been previously approved.\n\n`+
714			`To make these errors go away, you have two choices:\n`+
715			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
716			`      to the new methods, etc. shown in the above diff.\n\n`+
717			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
718			`         m %s-update-current-api\n\n`+
719			`      To submit the revised current.txt to the main Android repository,\n`+
720			`      you will need approval.\n`+
721			`******************************\n`, ctx.ModuleName())
722
723		rule.Command().
724			Text("touch").Output(d.checkCurrentApiTimestamp).
725			Text(") || (").
726			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
727			Text("; exit 38").
728			Text(")")
729
730		rule.Build("metalavaCurrentApiCheck", "check current API")
731
732		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
733
734		// update API rule
735		rule = android.NewRuleBuilder(pctx, ctx)
736
737		rule.Command().Text("( true")
738
739		rule.Command().
740			Text("cp").Flag("-f").
741			Input(d.apiFile).Flag(apiFile.String())
742
743		rule.Command().
744			Text("cp").Flag("-f").
745			Input(d.removedApiFile).Flag(removedApiFile.String())
746
747		msg = "failed to update public API"
748
749		rule.Command().
750			Text("touch").Output(d.updateCurrentApiTimestamp).
751			Text(") || (").
752			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
753			Text("; exit 38").
754			Text(")")
755
756		rule.Build("metalavaCurrentApiUpdate", "update current API")
757	}
758
759	if String(d.properties.Check_nullability_warnings) != "" {
760		if d.nullabilityWarningsFile == nil {
761			ctx.PropertyErrorf("check_nullability_warnings",
762				"Cannot specify check_nullability_warnings unless validating nullability")
763		}
764
765		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
766
767		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
768
769		msg := fmt.Sprintf(`\n******************************\n`+
770			`The warnings encountered during nullability annotation validation did\n`+
771			`not match the checked in file of expected warnings. The diffs are shown\n`+
772			`above. You have two options:\n`+
773			`   1. Resolve the differences by editing the nullability annotations.\n`+
774			`   2. Update the file of expected warnings by running:\n`+
775			`         cp %s %s\n`+
776			`       and submitting the updated file as part of your change.`,
777			d.nullabilityWarningsFile, checkNullabilityWarnings)
778
779		rule := android.NewRuleBuilder(pctx, ctx)
780
781		rule.Command().
782			Text("(").
783			Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
784			Text("&&").
785			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
786			Text(") || (").
787			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
788			Text("; exit 38").
789			Text(")")
790
791		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
792	}
793}
794
795func StubsDefaultsFactory() android.Module {
796	module := &DocDefaults{}
797
798	module.AddProperties(
799		&JavadocProperties{},
800		&DroidstubsProperties{},
801	)
802
803	android.InitDefaultsModule(module)
804
805	return module
806}
807
808var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
809
810type PrebuiltStubsSourcesProperties struct {
811	Srcs []string `android:"path"`
812}
813
814type PrebuiltStubsSources struct {
815	android.ModuleBase
816	android.DefaultableModuleBase
817	prebuilt android.Prebuilt
818	android.SdkBase
819
820	properties PrebuiltStubsSourcesProperties
821
822	stubsSrcJar android.Path
823}
824
825func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
826	switch tag {
827	case "":
828		return android.Paths{p.stubsSrcJar}, nil
829	default:
830		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
831	}
832}
833
834func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
835	return d.stubsSrcJar
836}
837
838func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
839	if len(p.properties.Srcs) != 1 {
840		ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs))
841		return
842	}
843
844	src := p.properties.Srcs[0]
845	if filepath.Ext(src) == ".srcjar" {
846		// This is a srcjar. We can use it directly.
847		p.stubsSrcJar = android.PathForModuleSrc(ctx, src)
848	} else {
849		outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
850
851		// This is a directory. Glob the contents just in case the directory does not exist.
852		srcGlob := src + "/**/*"
853		srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
854
855		// Although PathForModuleSrc can return nil if either the path doesn't exist or
856		// the path components are invalid it won't in this case because no components
857		// are specified and the module directory must exist in order to get this far.
858		srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src)
859
860		rule := android.NewRuleBuilder(pctx, ctx)
861		rule.Command().
862			BuiltTool("soong_zip").
863			Flag("-write_if_changed").
864			Flag("-jar").
865			FlagWithOutput("-o ", outPath).
866			FlagWithArg("-C ", srcDir.String()).
867			FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths)
868		rule.Restat()
869		rule.Build("zip src", "Create srcjar from prebuilt source")
870		p.stubsSrcJar = outPath
871	}
872}
873
874func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
875	return &p.prebuilt
876}
877
878func (p *PrebuiltStubsSources) Name() string {
879	return p.prebuilt.Name(p.ModuleBase.Name())
880}
881
882// prebuilt_stubs_sources imports a set of java source files as if they were
883// generated by droidstubs.
884//
885// By default, a prebuilt_stubs_sources has a single variant that expects a
886// set of `.java` files generated by droidstubs.
887//
888// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
889// for host modules.
890//
891// Intended only for use by sdk snapshots.
892func PrebuiltStubsSourcesFactory() android.Module {
893	module := &PrebuiltStubsSources{}
894
895	module.AddProperties(&module.properties)
896
897	android.InitPrebuiltModule(module, &module.properties.Srcs)
898	android.InitSdkAwareModule(module)
899	InitDroiddocModule(module, android.HostAndDeviceSupported)
900	return module
901}
902