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