xref: /aosp_15_r20/build/soong/java/droidstubs.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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	"regexp"
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// The values allowed for Droidstubs' Api_levels_sdk_type
31var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
32
33type StubsType int
34
35const (
36	Everything StubsType = iota
37	Runtime
38	Exportable
39	Unavailable
40)
41
42func (s StubsType) String() string {
43	switch s {
44	case Everything:
45		return "everything"
46	case Runtime:
47		return "runtime"
48	case Exportable:
49		return "exportable"
50	default:
51		return ""
52	}
53}
54
55func StringToStubsType(s string) StubsType {
56	switch strings.ToLower(s) {
57	case Everything.String():
58		return Everything
59	case Runtime.String():
60		return Runtime
61	case Exportable.String():
62		return Exportable
63	default:
64		return Unavailable
65	}
66}
67
68func init() {
69	RegisterStubsBuildComponents(android.InitRegistrationContext)
70}
71
72func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
73	ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
74
75	ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
76	ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
77
78	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
79}
80
81type stubsArtifacts struct {
82	nullabilityWarningsFile android.WritablePath
83	annotationsZip          android.WritablePath
84	apiVersionsXml          android.WritablePath
85	metadataZip             android.WritablePath
86	metadataDir             android.WritablePath
87}
88
89// Droidstubs
90type Droidstubs struct {
91	Javadoc
92	embeddableInModuleAndImport
93
94	properties     DroidstubsProperties
95	apiFile        android.Path
96	removedApiFile android.Path
97
98	checkCurrentApiTimestamp      android.WritablePath
99	updateCurrentApiTimestamp     android.WritablePath
100	checkLastReleasedApiTimestamp android.WritablePath
101	apiLintTimestamp              android.WritablePath
102	apiLintReport                 android.WritablePath
103
104	checkNullabilityWarningsTimestamp android.WritablePath
105
106	everythingArtifacts stubsArtifacts
107	exportableArtifacts stubsArtifacts
108
109	exportableApiFile        android.WritablePath
110	exportableRemovedApiFile android.WritablePath
111}
112
113type DroidstubsProperties struct {
114	// The generated public API filename by Metalava, defaults to <module>_api.txt
115	Api_filename *string
116
117	// the generated removed API filename by Metalava, defaults to <module>_removed.txt
118	Removed_api_filename *string
119
120	Check_api struct {
121		Last_released ApiToCheck
122
123		Current ApiToCheck
124
125		Api_lint struct {
126			Enabled *bool
127
128			// If set, performs api_lint on any new APIs not found in the given signature file
129			New_since *string `android:"path"`
130
131			// If not blank, path to the baseline txt file for approved API lint violations.
132			Baseline_file *string `android:"path"`
133		}
134	}
135
136	// user can specify the version of previous released API file in order to do compatibility check.
137	Previous_api *string `android:"path"`
138
139	// is set to true, Metalava will allow framework SDK to contain annotations.
140	Annotations_enabled *bool
141
142	// a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
143	Merge_annotations_dirs []string
144
145	// a list of top-level directories containing Java stub files to merge show/hide annotations from.
146	Merge_inclusion_annotations_dirs []string
147
148	// a file containing a list of classes to do nullability validation for.
149	Validate_nullability_from_list *string
150
151	// a file containing expected warnings produced by validation of nullability annotations.
152	Check_nullability_warnings *string
153
154	// if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
155	Create_doc_stubs *bool
156
157	// if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
158	// Has no effect if create_doc_stubs: true.
159	Output_javadoc_comments *bool
160
161	// if set to false then do not write out stubs. Defaults to true.
162	//
163	// TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
164	Generate_stubs *bool
165
166	// if set to true, provides a hint to the build system that this rule uses a lot of memory,
167	// which can be used for scheduling purposes
168	High_mem *bool
169
170	// if set to true, Metalava will allow framework SDK to contain API levels annotations.
171	Api_levels_annotations_enabled *bool
172
173	// Apply the api levels database created by this module rather than generating one in this droidstubs.
174	Api_levels_module *string
175
176	// the dirs which Metalava extracts API levels annotations from.
177	Api_levels_annotations_dirs []string
178
179	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system', 'module-lib' and 'system-server'; defaults to public.
180	Api_levels_sdk_type *string
181
182	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
183	Api_levels_jar_filename *string
184
185	// if set to true, collect the values used by the Dev tools and
186	// write them in files packaged with the SDK. Defaults to false.
187	Write_sdk_values *bool
188
189	// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
190	// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
191	Extensions_info_file *string `android:"path"`
192
193	// API surface of this module. If set, the module contributes to an API surface.
194	// For the full list of available API surfaces, refer to soong/android/sdk_version.go
195	Api_surface *string
196
197	// a list of aconfig_declarations module names that the stubs generated in this module
198	// depend on.
199	Aconfig_declarations []string
200
201	// List of hard coded filegroups containing Metalava config files that are passed to every
202	// Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd.
203	ConfigFiles []string `android:"path" blueprint:"mutated"`
204}
205
206// Used by xsd_config
207type ApiFilePath interface {
208	ApiFilePath(StubsType) (android.Path, error)
209}
210
211type ApiStubsSrcProvider interface {
212	StubsSrcJar(StubsType) (android.Path, error)
213}
214
215// Provider of information about API stubs, used by java_sdk_library.
216type ApiStubsProvider interface {
217	AnnotationsZip(StubsType) (android.Path, error)
218	ApiFilePath
219	RemovedApiFilePath(StubsType) (android.Path, error)
220
221	ApiStubsSrcProvider
222}
223
224type currentApiTimestampProvider interface {
225	CurrentApiTimestamp() android.Path
226}
227
228type annotationFlagsParams struct {
229	migratingNullability    bool
230	validatingNullability   bool
231	nullabilityWarningsFile android.WritablePath
232	annotationsZip          android.WritablePath
233}
234type stubsCommandParams struct {
235	srcJarDir               android.ModuleOutPath
236	stubsDir                android.OptionalPath
237	stubsSrcJar             android.WritablePath
238	metadataZip             android.WritablePath
239	metadataDir             android.WritablePath
240	apiVersionsXml          android.WritablePath
241	nullabilityWarningsFile android.WritablePath
242	annotationsZip          android.WritablePath
243	stubConfig              stubsCommandConfigParams
244}
245type stubsCommandConfigParams struct {
246	stubsType             StubsType
247	javaVersion           javaVersion
248	deps                  deps
249	checkApi              bool
250	generateStubs         bool
251	doApiLint             bool
252	doCheckReleased       bool
253	writeSdkValues        bool
254	migratingNullability  bool
255	validatingNullability bool
256}
257
258// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
259// documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
260// a droiddoc module to generate documentation.
261func DroidstubsFactory() android.Module {
262	module := &Droidstubs{}
263
264	module.AddProperties(&module.properties,
265		&module.Javadoc.properties)
266	module.properties.ConfigFiles = getMetalavaConfigFilegroupReference()
267	module.initModuleAndImport(module)
268
269	InitDroiddocModule(module, android.HostAndDeviceSupported)
270
271	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
272		module.createApiContribution(ctx)
273	})
274	return module
275}
276
277// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
278// to be documented, filtering out hidden classes and methods.  The resulting .java files are intended to be
279// passed to a droiddoc_host module to generate documentation.  Use a droidstubs_host instead of a droidstubs
280// module when symbols needed by the source files are provided by java_library_host modules.
281func DroidstubsHostFactory() android.Module {
282	module := &Droidstubs{}
283
284	module.AddProperties(&module.properties,
285		&module.Javadoc.properties)
286
287	module.properties.ConfigFiles = getMetalavaConfigFilegroupReference()
288	InitDroiddocModule(module, android.HostSupported)
289	return module
290}
291
292func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) {
293	switch stubsType {
294	case Everything:
295		ret, err = d.everythingArtifacts.annotationsZip, nil
296	case Exportable:
297		ret, err = d.exportableArtifacts.annotationsZip, nil
298	default:
299		ret, err = nil, fmt.Errorf("annotations zip not supported for the stub type %s", stubsType.String())
300	}
301	return ret, err
302}
303
304func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err error) {
305	switch stubsType {
306	case Everything:
307		ret, err = d.apiFile, nil
308	case Exportable:
309		ret, err = d.exportableApiFile, nil
310	default:
311		ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String())
312	}
313	if ret == nil && err == nil {
314		err = fmt.Errorf("api file is null for the stub type %s", stubsType.String())
315	}
316	return ret, err
317}
318
319func (d *Droidstubs) ApiVersionsXmlFilePath(stubsType StubsType) (ret android.Path, err error) {
320	switch stubsType {
321	case Everything:
322		ret, err = d.everythingArtifacts.apiVersionsXml, nil
323	case Exportable:
324		ret, err = d.exportableArtifacts.apiVersionsXml, nil
325	default:
326		ret, err = nil, fmt.Errorf("api versions xml file path not supported for the stub type %s", stubsType.String())
327	}
328	if ret == nil && err == nil {
329		err = fmt.Errorf("api versions xml file is null for the stub type %s", stubsType.String())
330	}
331	return ret, err
332}
333
334func (d *Droidstubs) DocZip(stubsType StubsType) (ret android.Path, err error) {
335	switch stubsType {
336	case Everything:
337		ret, err = d.docZip, nil
338	default:
339		ret, err = nil, fmt.Errorf("docs zip not supported for the stub type %s", stubsType.String())
340	}
341	if ret == nil && err == nil {
342		err = fmt.Errorf("docs zip is null for the stub type %s", stubsType.String())
343	}
344	return ret, err
345}
346
347func (d *Droidstubs) RemovedApiFilePath(stubsType StubsType) (ret android.Path, err error) {
348	switch stubsType {
349	case Everything:
350		ret, err = d.removedApiFile, nil
351	case Exportable:
352		ret, err = d.exportableRemovedApiFile, nil
353	default:
354		ret, err = nil, fmt.Errorf("removed api file path not supported for the stub type %s", stubsType.String())
355	}
356	if ret == nil && err == nil {
357		err = fmt.Errorf("removed api file is null for the stub type %s", stubsType.String())
358	}
359	return ret, err
360}
361
362func (d *Droidstubs) StubsSrcJar(stubsType StubsType) (ret android.Path, err error) {
363	switch stubsType {
364	case Everything:
365		ret, err = d.stubsSrcJar, nil
366	case Exportable:
367		ret, err = d.exportableStubsSrcJar, nil
368	default:
369		ret, err = nil, fmt.Errorf("stubs srcjar not supported for the stub type %s", stubsType.String())
370	}
371	if ret == nil && err == nil {
372		err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String())
373	}
374	return ret, err
375}
376
377func (d *Droidstubs) CurrentApiTimestamp() android.Path {
378	return d.checkCurrentApiTimestamp
379}
380
381var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
382var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
383var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
384var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"}
385var metalavaCurrentApiTimestampTag = dependencyTag{name: "metalava-current-api-timestamp-tag"}
386
387func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
388	d.Javadoc.addDeps(ctx)
389
390	if len(d.properties.Merge_annotations_dirs) != 0 {
391		for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
392			ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
393		}
394	}
395
396	if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
397		for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
398			ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
399		}
400	}
401
402	if len(d.properties.Api_levels_annotations_dirs) != 0 {
403		for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
404			ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
405		}
406	}
407
408	if len(d.properties.Aconfig_declarations) != 0 {
409		for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations {
410			ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName)
411		}
412	}
413
414	if d.properties.Api_levels_module != nil {
415		ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module))
416	}
417}
418
419func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, metadataDir android.WritablePath) {
420	cmd.FlagWithArg("--sdk-values ", metadataDir.String())
421}
422
423func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) {
424
425	apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
426	uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName)
427	cmd.FlagWithOutput("--api ", uncheckedApiFile)
428	if checkApi || String(d.properties.Api_filename) != "" {
429		if stubsType == Everything {
430			d.apiFile = uncheckedApiFile
431		} else if stubsType == Exportable {
432			d.exportableApiFile = uncheckedApiFile
433		}
434	} else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
435		if stubsType == Everything {
436			// If check api is disabled then make the source file available for export.
437			d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
438		} else if stubsType == Exportable {
439			d.exportableApiFile = uncheckedApiFile
440		}
441	}
442
443	removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
444	uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName)
445	cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
446	if checkApi || String(d.properties.Removed_api_filename) != "" {
447		if stubsType == Everything {
448			d.removedApiFile = uncheckedRemovedFile
449		} else if stubsType == Exportable {
450			d.exportableRemovedApiFile = uncheckedRemovedFile
451		}
452	} else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
453		if stubsType == Everything {
454			// If check api is disabled then make the source removed api file available for export.
455			d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
456		} else if stubsType == Exportable {
457			d.exportableRemovedApiFile = uncheckedRemovedFile
458		}
459	}
460
461	if stubsDir.Valid() {
462		if Bool(d.properties.Create_doc_stubs) {
463			cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
464		} else {
465			cmd.FlagWithArg("--stubs ", stubsDir.String())
466			if !Bool(d.properties.Output_javadoc_comments) {
467				cmd.Flag("--exclude-documentation-from-stubs")
468			}
469		}
470	}
471}
472
473func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) {
474	if Bool(d.properties.Annotations_enabled) {
475		cmd.Flag(config.MetalavaAnnotationsFlags)
476
477		if params.migratingNullability {
478			previousApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Previous_api)})
479			cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles)
480		}
481
482		if s := String(d.properties.Validate_nullability_from_list); s != "" {
483			cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
484		}
485
486		if params.validatingNullability {
487			cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile)
488		}
489
490		cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip)
491
492		if len(d.properties.Merge_annotations_dirs) != 0 {
493			d.mergeAnnoDirFlags(ctx, cmd)
494		}
495
496		cmd.Flag(config.MetalavaAnnotationsWarningsFlags)
497	}
498}
499
500func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
501	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
502		if t, ok := m.(*ExportedDroiddocDir); ok {
503			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
504		} else {
505			ctx.PropertyErrorf("merge_annotations_dirs",
506				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
507		}
508	})
509}
510
511func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
512	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
513		if t, ok := m.(*ExportedDroiddocDir); ok {
514			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
515		} else {
516			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
517				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
518		}
519	})
520}
521
522func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
523	var apiVersions android.Path
524	if proptools.Bool(d.properties.Api_levels_annotations_enabled) {
525		d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
526		apiVersions = apiVersionsXml
527	} else {
528		ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) {
529			if s, ok := m.(*Droidstubs); ok {
530				if stubsType == Everything {
531					apiVersions = s.everythingArtifacts.apiVersionsXml
532				} else if stubsType == Exportable {
533					apiVersions = s.exportableArtifacts.apiVersionsXml
534				} else {
535					ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String())
536				}
537			} else {
538				ctx.PropertyErrorf("api_levels_module",
539					"module %q is not a droidstubs module", ctx.OtherModuleName(m))
540			}
541		})
542	}
543	if apiVersions != nil {
544		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
545		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
546		cmd.FlagWithInput("--apply-api-levels ", apiVersions)
547	}
548}
549
550// AndroidPlusUpdatableJar is the name of some extra jars added into `module-lib` and
551// `system-server` directories that contain all the APIs provided by the platform and updatable
552// modules because the `android.jar` files do not. See b/337836752.
553const AndroidPlusUpdatableJar = "android-plus-updatable.jar"
554
555func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
556	if len(d.properties.Api_levels_annotations_dirs) == 0 {
557		ctx.PropertyErrorf("api_levels_annotations_dirs",
558			"has to be non-empty if api levels annotations was enabled!")
559	}
560
561	cmd.FlagWithOutput("--generate-api-levels ", apiVersionsXml)
562
563	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
564
565	// TODO: Avoid the duplication of API surfaces, reuse apiScope.
566	// Add all relevant --android-jar-pattern patterns for Metalava.
567	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
568	// an actual file present on disk (in the order the patterns were passed). For system APIs for
569	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
570	// for older releases. Similarly, module-lib falls back to system API.
571	var sdkDirs []string
572	apiLevelsSdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public")
573	switch apiLevelsSdkType {
574	case "system-server":
575		sdkDirs = []string{"system-server", "module-lib", "system", "public"}
576	case "module-lib":
577		sdkDirs = []string{"module-lib", "system", "public"}
578	case "system":
579		sdkDirs = []string{"system", "public"}
580	case "public":
581		sdkDirs = []string{"public"}
582	default:
583		ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
584		return
585	}
586
587	// Construct a pattern to match the appropriate extensions that should be included in the
588	// generated api-versions.xml file.
589	//
590	// Use the first item in the sdkDirs array as that is the sdk type for the target API levels
591	// being generated but has the advantage over `Api_levels_sdk_type` as it has been validated.
592	// The exception is for system-server which needs to include module-lib and system-server. That
593	// is because while system-server extends module-lib the system-server extension directory only
594	// contains service-* modules which provide system-server APIs it does not list the modules which
595	// only provide a module-lib, so they have to be included separately.
596	extensionSurfacesPattern := sdkDirs[0]
597	if apiLevelsSdkType == "system-server" {
598		// Take the first two items in sdkDirs, which are system-server and module-lib, and construct
599		// a pattern that will match either.
600		extensionSurfacesPattern = strings.Join(sdkDirs[0:2], "|")
601	}
602	extensionsPattern := fmt.Sprintf(`/extensions/[0-9]+/(%s)/.*\.jar`, extensionSurfacesPattern)
603
604	var dirs []string
605	var extensions_dir string
606	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
607		if t, ok := m.(*ExportedDroiddocDir); ok {
608			extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern)
609
610			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
611			// ideally this should be read from prebuiltApis.properties.Extensions_*
612			for _, dep := range t.deps {
613				// Check to see if it matches an extension first.
614				depBase := dep.Base()
615				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
616					if extensions_dir == "" {
617						extensions_dir = t.dir.String() + "/extensions"
618					}
619					cmd.Implicit(dep)
620				} else if depBase == filename {
621					// Check to see if it matches a dessert release for an SDK, e.g. Android, Car, Wear, etc..
622					cmd.Implicit(dep)
623				} else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil {
624					// The output api-versions.xml has been requested to include information on SDK
625					// extensions. That means it also needs to include
626					// so
627					// The module-lib and system-server directories should use `android-plus-updatable.jar`
628					// instead of `android.jar`. See AndroidPlusUpdatableJar for more information.
629					cmd.Implicit(dep)
630				} else if filename != "android.jar" && depBase == "android.jar" {
631					// Metalava implicitly searches these patterns:
632					//  prebuilts/tools/common/api-versions/android-%/android.jar
633					//  prebuilts/sdk/%/public/android.jar
634					// Add android.jar files from the api_levels_annotations_dirs directories to try
635					// to satisfy these patterns.  If Metalava can't find a match for an API level
636					// between 1 and 28 in at least one pattern it will fail.
637					cmd.Implicit(dep)
638				}
639			}
640
641			dirs = append(dirs, t.dir.String())
642		} else {
643			ctx.PropertyErrorf("api_levels_annotations_dirs",
644				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
645		}
646	})
647
648	// Generate the list of --android-jar-pattern options. The order matters so the first one which
649	// matches will be the one that is used for a specific api level..
650	for _, sdkDir := range sdkDirs {
651		for _, dir := range dirs {
652			addPattern := func(jarFilename string) {
653				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename))
654			}
655
656			if sdkDir == "module-lib" || sdkDir == "system-server" {
657				// The module-lib and system-server android.jars do not include the updatable modules (as
658				// doing so in the source would introduce dependency cycles and the prebuilts have to
659				// match the sources). So, instead an additional `android-plus-updatable.jar` will be used
660				// that does include the updatable modules and this pattern will match that. This pattern
661				// is added in addition to the following pattern to decouple this change from the change
662				// to add the `android-plus-updatable.jar`.
663				addPattern(AndroidPlusUpdatableJar)
664			}
665
666			addPattern(filename)
667		}
668	}
669
670	if d.properties.Extensions_info_file != nil {
671		if extensions_dir == "" {
672			ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found")
673		}
674		info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
675		cmd.Implicit(info_file)
676		cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
677		cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
678	}
679}
680
681func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType) {
682	if len(d.Javadoc.properties.Out) > 0 {
683		ctx.PropertyErrorf("out", "out property may not be combined with check_api")
684	}
685
686	apiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Api_file)})
687	removedApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Removed_api_file)})
688
689	cmd.FlagForEachInput("--check-compatibility:api:released ", apiFiles)
690	cmd.FlagForEachInput("--check-compatibility:removed:released ", removedApiFiles)
691
692	baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
693	if baselineFile.Valid() {
694		cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
695	}
696}
697
698func metalavaUseRbe(ctx android.ModuleContext) bool {
699	return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA")
700}
701
702func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
703	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams, configFiles android.Paths) *android.RuleBuilderCommand {
704	rule.Command().Text("rm -rf").Flag(homeDir.String())
705	rule.Command().Text("mkdir -p").Flag(homeDir.String())
706
707	cmd := rule.Command()
708	cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
709
710	if metalavaUseRbe(ctx) {
711		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
712		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
713		compare := ctx.Config().IsEnvTrue("RBE_METALAVA_COMPARE")
714		remoteUpdateCache := !ctx.Config().IsEnvFalse("RBE_METALAVA_REMOTE_UPDATE_CACHE")
715		labels := map[string]string{"type": "tool", "name": "metalava"}
716		// TODO: metalava pool rejects these jobs
717		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
718		rule.Rewrapper(&remoteexec.REParams{
719			Labels:              labels,
720			ExecStrategy:        execStrategy,
721			ToolchainInputs:     []string{config.JavaCmd(ctx).String()},
722			Platform:            map[string]string{remoteexec.PoolKey: pool},
723			Compare:             compare,
724			NumLocalRuns:        1,
725			NumRemoteRuns:       1,
726			NoRemoteUpdateCache: !remoteUpdateCache,
727		})
728	}
729
730	cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
731		Flag(config.JavacVmFlags).
732		Flag(config.MetalavaAddOpens).
733		FlagWithArg("--java-source ", params.javaVersion.String()).
734		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, fmt.Sprintf("%s.metalava.rsp", params.stubsType.String())), srcs).
735		FlagWithInput("@", srcJarList)
736
737	// Metalava does not differentiate between bootclasspath and classpath and has not done so for
738	// years, so it is unlikely to change any time soon.
739	combinedPaths := append(([]android.Path)(nil), params.deps.bootClasspath.Paths()...)
740	combinedPaths = append(combinedPaths, params.deps.classpath.Paths()...)
741	if len(combinedPaths) > 0 {
742		cmd.FlagWithInputList("--classpath ", combinedPaths, ":")
743	}
744
745	cmd.Flag(config.MetalavaFlags)
746
747	addMetalavaConfigFilesToCmd(cmd, configFiles)
748
749	return cmd
750}
751
752// MetalavaConfigFilegroup is the name of the filegroup in build/soong/java/metalava that lists
753// the configuration files to pass to Metalava.
754const MetalavaConfigFilegroup = "metalava-config-files"
755
756// Get a reference to the MetalavaConfigFilegroup suitable for use in a property.
757func getMetalavaConfigFilegroupReference() []string {
758	return []string{":" + MetalavaConfigFilegroup}
759}
760
761// addMetalavaConfigFilesToCmd adds --config-file options to use the config files list in the
762// MetalavaConfigFilegroup filegroup.
763func addMetalavaConfigFilesToCmd(cmd *android.RuleBuilderCommand, configFiles android.Paths) {
764	cmd.FlagForEachInput("--config-file ", configFiles)
765}
766
767// Pass flagged apis related flags to metalava. When aconfig_declarations property is not
768// defined for a module, simply revert all flagged apis annotations. If aconfig_declarations
769// property is defined, apply transformations and only revert the flagged apis that are not
770// enabled via release configurations and are not specified in aconfig_declarations
771func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) {
772	var filterArgs string
773	switch stubsType {
774	// No flagged apis specific flags need to be passed to metalava when generating
775	// everything stubs
776	case Everything:
777		return
778
779	case Runtime:
780		filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
781
782	case Exportable:
783		// When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with
784		// the flagged apis that have read_write permissions are exposed on top of the enabled
785		// and read_only apis. This is to support local override of flag values at runtime.
786		if ctx.Config().ReleaseExportRuntimeApis() {
787			filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
788		} else {
789			filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'"
790		}
791	}
792
793	if len(aconfigFlagsPaths) == 0 {
794		// This argument should not be added for "everything" stubs
795		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
796		return
797	}
798
799	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
800	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
801
802	ctx.Build(pctx, android.BuildParams{
803		Rule:        gatherReleasedFlaggedApisRule,
804		Inputs:      aconfigFlagsPaths,
805		Output:      releasedFlaggedApisFile,
806		Description: fmt.Sprintf("%s gather aconfig flags", stubsType),
807		Args: map[string]string{
808			"flags_path":  android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "),
809			"filter_args": filterArgs,
810		},
811	})
812
813	ctx.Build(pctx, android.BuildParams{
814		Rule:        generateMetalavaRevertAnnotationsRule,
815		Input:       releasedFlaggedApisFile,
816		Output:      revertAnnotationsFile,
817		Description: fmt.Sprintf("%s revert annotations", stubsType),
818	})
819
820	cmd.FlagWithInput("@", revertAnnotationsFile)
821}
822
823func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
824	params stubsCommandParams) *android.RuleBuilderCommand {
825	if BoolDefault(d.properties.High_mem, false) {
826		// This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
827		rule.HighMem()
828	}
829
830	if params.stubConfig.generateStubs {
831		rule.Command().Text("rm -rf").Text(params.stubsDir.String())
832		rule.Command().Text("mkdir -p").Text(params.stubsDir.String())
833	}
834
835	srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars)
836
837	homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home")
838
839	configFiles := android.PathsForModuleSrc(ctx, d.properties.ConfigFiles)
840
841	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig, configFiles)
842	cmd.Implicits(d.Javadoc.implicits)
843
844	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
845
846	if params.stubConfig.writeSdkValues {
847		d.sdkValuesFlags(ctx, cmd, params.metadataDir)
848	}
849
850	annotationParams := annotationFlagsParams{
851		migratingNullability:    params.stubConfig.migratingNullability,
852		validatingNullability:   params.stubConfig.validatingNullability,
853		nullabilityWarningsFile: params.nullabilityWarningsFile,
854		annotationsZip:          params.annotationsZip,
855	}
856
857	d.annotationsFlags(ctx, cmd, annotationParams)
858	d.inclusionAnnotationsFlags(ctx, cmd)
859	d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml)
860
861	if params.stubConfig.doCheckReleased {
862		d.apiCompatibilityFlags(ctx, cmd, params.stubConfig.stubsType)
863	}
864
865	d.expandArgs(ctx, cmd)
866
867	for _, o := range d.Javadoc.properties.Out {
868		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
869	}
870
871	return cmd
872}
873
874// Sandbox rule for generating the everything stubs and other artifacts
875func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
876	srcJarDir := android.PathForModuleOut(ctx, Everything.String(), "srcjars")
877	rule := android.NewRuleBuilder(pctx, ctx)
878	rule.Sbox(android.PathForModuleOut(ctx, Everything.String()),
879		android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
880		SandboxInputs()
881
882	var stubsDir android.OptionalPath
883	if params.generateStubs {
884		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, Everything.String(), "stubsDir"))
885		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-"+"stubs.srcjar")
886	}
887
888	if params.writeSdkValues {
889		d.everythingArtifacts.metadataDir = android.PathForModuleOut(ctx, Everything.String(), "metadata")
890		d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip")
891	}
892
893	if Bool(d.properties.Annotations_enabled) {
894		if params.validatingNullability {
895			d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt")
896		}
897		d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip")
898	}
899	if Bool(d.properties.Api_levels_annotations_enabled) {
900		d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml")
901	}
902
903	commonCmdParams := stubsCommandParams{
904		srcJarDir:               srcJarDir,
905		stubsDir:                stubsDir,
906		stubsSrcJar:             d.Javadoc.stubsSrcJar,
907		metadataDir:             d.everythingArtifacts.metadataDir,
908		apiVersionsXml:          d.everythingArtifacts.apiVersionsXml,
909		nullabilityWarningsFile: d.everythingArtifacts.nullabilityWarningsFile,
910		annotationsZip:          d.everythingArtifacts.annotationsZip,
911		stubConfig:              params,
912	}
913
914	cmd := d.commonMetalavaStubCmd(ctx, rule, commonCmdParams)
915
916	d.everythingOptionalCmd(ctx, cmd, params.doApiLint, params.doCheckReleased)
917
918	if params.generateStubs {
919		rule.Command().
920			BuiltTool("soong_zip").
921			Flag("-write_if_changed").
922			Flag("-jar").
923			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
924			FlagWithArg("-C ", stubsDir.String()).
925			FlagWithArg("-D ", stubsDir.String())
926	}
927
928	if params.writeSdkValues {
929		rule.Command().
930			BuiltTool("soong_zip").
931			Flag("-write_if_changed").
932			Flag("-d").
933			FlagWithOutput("-o ", d.everythingArtifacts.metadataZip).
934			FlagWithArg("-C ", d.everythingArtifacts.metadataDir.String()).
935			FlagWithArg("-D ", d.everythingArtifacts.metadataDir.String())
936	}
937
938	// TODO: We don't really need two separate API files, but this is a reminiscence of how
939	// we used to run metalava separately for API lint and the "last_released" check. Unify them.
940	if params.doApiLint {
941		rule.Command().Text("touch").Output(d.apiLintTimestamp)
942	}
943	if params.doCheckReleased {
944		rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
945	}
946
947	// TODO(b/183630617): rewrapper doesn't support restat rules
948	if !metalavaUseRbe(ctx) {
949		rule.Restat()
950	}
951
952	zipSyncCleanupCmd(rule, srcJarDir)
953
954	rule.Build("metalava", "metalava merged")
955}
956
957// Sandbox rule for generating the everything artifacts that are not run by
958// default but only run based on the module configurations
959func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) {
960
961	// Add API lint options.
962	treatDocumentationIssuesAsErrors := false
963	if doApiLint {
964		var newSince android.Paths
965		if d.properties.Check_api.Api_lint.New_since != nil {
966			newSince = android.PathsForModuleSrc(ctx, []string{proptools.String(d.properties.Check_api.Api_lint.New_since)})
967		}
968		cmd.Flag("--api-lint")
969		cmd.FlagForEachInput("--api-lint-previous-api ", newSince)
970		d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt")
971		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
972
973		// If UnflaggedApi issues have not already been configured then make sure that existing
974		// UnflaggedApi issues are reported as warnings but issues in new/changed code are treated as
975		// errors by the Build Warnings Aye Aye Analyzer in Gerrit.
976		// Once existing issues have been fixed this will be changed to error.
977		// TODO(b/362771529): Switch to --error
978		if !strings.Contains(cmd.String(), " UnflaggedApi ") {
979			cmd.Flag("--error-when-new UnflaggedApi")
980		}
981
982		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
983		if d.Name() != "android.car-system-stubs-docs" &&
984			d.Name() != "android.car-stubs-docs" {
985			treatDocumentationIssuesAsErrors = true
986			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
987		}
988
989		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
990		updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "api_lint_baseline.txt")
991		d.apiLintTimestamp = android.PathForModuleOut(ctx, Everything.String(), "api_lint.timestamp")
992
993		// Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
994		//
995		// TODO: metalava also has a slightly different message hardcoded. Should we unify this
996		// message and metalava's one?
997		msg := `$'` + // Enclose with $' ... '
998			`************************************************************\n` +
999			`Your API changes are triggering API Lint warnings or errors.\n` +
1000			`\n` +
1001			`To make the failures go away:\n` +
1002			`\n` +
1003			`1. REQUIRED: Read the messages carefully and address them by` +
1004			`   fixing the API if appropriate.\n` +
1005			`2. If the failure is a false positive, you can suppress it with:\n` +
1006			`        @SuppressLint("<id>")\n` +
1007			`   where the <id> is given in brackets in the error message above.\n`
1008
1009		if baselineFile.Valid() {
1010			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
1011			cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
1012
1013			msg += fmt.Sprintf(``+
1014				`3. FOR LSC ONLY: You can update the baseline by executing\n`+
1015				`   the following command:\n`+
1016				`       (cd $ANDROID_BUILD_TOP && cp \\\n`+
1017				`       "%s" \\\n`+
1018				`       "%s")\n`+
1019				`   To submit the revised baseline.txt to the main Android\n`+
1020				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
1021		} else {
1022			msg += fmt.Sprintf(``+
1023				`3. FOR LSC ONLY: You can add a baseline file of existing lint failures\n`+
1024				`   to the build rule of %s.\n`, d.Name())
1025		}
1026		// Note the message ends with a ' (single quote), to close the $' ... ' .
1027		msg += `************************************************************\n'`
1028
1029		cmd.FlagWithArg("--error-message:api-lint ", msg)
1030	}
1031
1032	if !treatDocumentationIssuesAsErrors {
1033		treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
1034	}
1035
1036	// Add "check released" options. (Detect incompatible API changes from the last public release)
1037	if doCheckReleased {
1038		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
1039		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp")
1040		if baselineFile.Valid() {
1041			updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt")
1042			cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
1043		}
1044		// Note this string includes quote ($' ... '), which decodes the "\n"s.
1045		msg := `$'\n******************************\n` +
1046			`You have tried to change the API from what has been previously released in\n` +
1047			`an SDK.  Please fix the errors listed above.\n` +
1048			`******************************\n'`
1049
1050		cmd.FlagWithArg("--error-message:compatibility:released ", msg)
1051	}
1052
1053	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
1054		// Pass the current API file into metalava so it can use it as the basis for determining how to
1055		// generate the output signature files (both api and removed).
1056		currentApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
1057		cmd.FlagWithInput("--use-same-format-as ", currentApiFile)
1058	}
1059}
1060
1061// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be
1062// hidden as they are very noisy and provide little value.
1063var HIDDEN_DOCUMENTATION_ISSUES = []string{
1064	"Deprecated",
1065	"IntDef",
1066	"Nullable",
1067}
1068
1069func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) {
1070	// Treat documentation issues as warnings, but error when new.
1071	cmd.Flag("--error-when-new-category").Flag("Documentation")
1072
1073	// Hide some documentation issues that generated a lot of noise for little benefit.
1074	cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES)
1075}
1076
1077// Sandbox rule for generating exportable stubs and other artifacts
1078func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
1079	optionalCmdParams := stubsCommandParams{
1080		stubConfig: params,
1081	}
1082
1083	if params.generateStubs {
1084		d.Javadoc.exportableStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar")
1085		optionalCmdParams.stubsSrcJar = d.Javadoc.exportableStubsSrcJar
1086	}
1087
1088	if params.writeSdkValues {
1089		d.exportableArtifacts.metadataZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-metadata.zip")
1090		d.exportableArtifacts.metadataDir = android.PathForModuleOut(ctx, params.stubsType.String(), "metadata")
1091		optionalCmdParams.metadataZip = d.exportableArtifacts.metadataZip
1092		optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir
1093	}
1094
1095	if Bool(d.properties.Annotations_enabled) {
1096		if params.validatingNullability {
1097			d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt")
1098			optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile
1099		}
1100		d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip")
1101		optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip
1102	}
1103	if Bool(d.properties.Api_levels_annotations_enabled) {
1104		d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml")
1105		optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml
1106	}
1107
1108	if params.checkApi || String(d.properties.Api_filename) != "" {
1109		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
1110		d.exportableApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename)
1111	}
1112
1113	if params.checkApi || String(d.properties.Removed_api_filename) != "" {
1114		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_api.txt")
1115		d.exportableRemovedApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename)
1116	}
1117
1118	d.optionalStubCmd(ctx, optionalCmdParams)
1119}
1120
1121func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) {
1122
1123	params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars")
1124	rule := android.NewRuleBuilder(pctx, ctx)
1125	rule.Sbox(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String()),
1126		android.PathForModuleOut(ctx, fmt.Sprintf("metalava_%s.sbox.textproto", params.stubConfig.stubsType.String()))).
1127		SandboxInputs()
1128
1129	if params.stubConfig.generateStubs {
1130		params.stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "stubsDir"))
1131	}
1132
1133	cmd := d.commonMetalavaStubCmd(ctx, rule, params)
1134
1135	generateRevertAnnotationArgs(ctx, cmd, params.stubConfig.stubsType, params.stubConfig.deps.aconfigProtoFiles)
1136
1137	if params.stubConfig.doApiLint {
1138		// Pass the lint baseline file as an input to resolve the lint errors.
1139		// The exportable stubs generation does not update the lint baseline file.
1140		// Lint baseline file update is handled by the everything stubs
1141		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
1142		if baselineFile.Valid() {
1143			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
1144		}
1145	}
1146
1147	// Treat documentation issues as warnings, but error when new.
1148	treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
1149
1150	if params.stubConfig.generateStubs {
1151		rule.Command().
1152			BuiltTool("soong_zip").
1153			Flag("-write_if_changed").
1154			Flag("-jar").
1155			FlagWithOutput("-o ", params.stubsSrcJar).
1156			FlagWithArg("-C ", params.stubsDir.String()).
1157			FlagWithArg("-D ", params.stubsDir.String())
1158	}
1159
1160	if params.stubConfig.writeSdkValues {
1161		rule.Command().
1162			BuiltTool("soong_zip").
1163			Flag("-write_if_changed").
1164			Flag("-d").
1165			FlagWithOutput("-o ", params.metadataZip).
1166			FlagWithArg("-C ", params.metadataDir.String()).
1167			FlagWithArg("-D ", params.metadataDir.String())
1168	}
1169
1170	// TODO(b/183630617): rewrapper doesn't support restat rules
1171	if !metalavaUseRbe(ctx) {
1172		rule.Restat()
1173	}
1174
1175	zipSyncCleanupCmd(rule, params.srcJarDir)
1176
1177	rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged")
1178}
1179
1180func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1181	deps := d.Javadoc.collectDeps(ctx)
1182
1183	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
1184	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
1185
1186	// Add options for the other optional tasks: API-lint and check-released.
1187	// We generate separate timestamp files for them.
1188	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false)
1189	doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
1190
1191	writeSdkValues := Bool(d.properties.Write_sdk_values)
1192
1193	annotationsEnabled := Bool(d.properties.Annotations_enabled)
1194
1195	migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != ""
1196	validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
1197		String(d.properties.Validate_nullability_from_list) != "")
1198
1199	checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
1200		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
1201
1202	stubCmdParams := stubsCommandConfigParams{
1203		javaVersion:           javaVersion,
1204		deps:                  deps,
1205		checkApi:              checkApi,
1206		generateStubs:         generateStubs,
1207		doApiLint:             doApiLint,
1208		doCheckReleased:       doCheckReleased,
1209		writeSdkValues:        writeSdkValues,
1210		migratingNullability:  migratingNullability,
1211		validatingNullability: validatingNullability,
1212	}
1213	stubCmdParams.stubsType = Everything
1214	// Create default (i.e. "everything" stubs) rule for metalava
1215	d.everythingStubCmd(ctx, stubCmdParams)
1216
1217	// The module generates "exportable" (and "runtime" eventually) stubs regardless of whether
1218	// aconfig_declarations property is defined or not. If the property is not defined, the module simply
1219	// strips all flagged apis to generate the "exportable" stubs
1220	stubCmdParams.stubsType = Exportable
1221	d.exportableStubCmd(ctx, stubCmdParams)
1222
1223	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
1224
1225		if len(d.Javadoc.properties.Out) > 0 {
1226			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
1227		}
1228
1229		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
1230		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
1231		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
1232
1233		if baselineFile.Valid() {
1234			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
1235		}
1236
1237		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_current_api.timestamp")
1238
1239		rule := android.NewRuleBuilder(pctx, ctx)
1240
1241		// Diff command line.
1242		// -F matches the closest "opening" line, such as "package android {"
1243		// and "  public class Intent {".
1244		diff := `diff -u -F '{ *$'`
1245
1246		rule.Command().Text("( true")
1247		rule.Command().
1248			Text(diff).
1249			Input(apiFile).Input(d.apiFile)
1250
1251		rule.Command().
1252			Text(diff).
1253			Input(removedApiFile).Input(d.removedApiFile)
1254
1255		msg := fmt.Sprintf(`\n******************************\n`+
1256			`You have tried to change the API from what has been previously approved.\n\n`+
1257			`To make these errors go away, you have two choices:\n`+
1258			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
1259			`      to the new methods, etc. shown in the above diff.\n\n`+
1260			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
1261			`         m %s-update-current-api\n\n`+
1262			`      To submit the revised current.txt to the main Android repository,\n`+
1263			`      you will need approval.\n`+
1264			`If your build failed due to stub validation, you can resolve the errors with\n`+
1265			`either of the two choices above and try re-building the target.\n`+
1266			`If the mismatch between the stubs and the current.txt is intended,\n`+
1267			`you can try re-building the target by executing the following command:\n`+
1268			`m DISABLE_STUB_VALIDATION=true <your build target>.\n`+
1269			`Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+
1270			`******************************\n`, ctx.ModuleName())
1271
1272		rule.Command().
1273			Text("touch").Output(d.checkCurrentApiTimestamp).
1274			Text(") || (").
1275			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1276			Text("; exit 38").
1277			Text(")")
1278
1279		rule.Build("metalavaCurrentApiCheck", "check current API")
1280
1281		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp")
1282
1283		// update API rule
1284		rule = android.NewRuleBuilder(pctx, ctx)
1285
1286		rule.Command().Text("( true")
1287
1288		rule.Command().
1289			Text("cp").Flag("-f").
1290			Input(d.apiFile).Flag(apiFile.String())
1291
1292		rule.Command().
1293			Text("cp").Flag("-f").
1294			Input(d.removedApiFile).Flag(removedApiFile.String())
1295
1296		msg = "failed to update public API"
1297
1298		rule.Command().
1299			Text("touch").Output(d.updateCurrentApiTimestamp).
1300			Text(") || (").
1301			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1302			Text("; exit 38").
1303			Text(")")
1304
1305		rule.Build("metalavaCurrentApiUpdate", "update current API")
1306	}
1307
1308	if String(d.properties.Check_nullability_warnings) != "" {
1309		if d.everythingArtifacts.nullabilityWarningsFile == nil {
1310			ctx.PropertyErrorf("check_nullability_warnings",
1311				"Cannot specify check_nullability_warnings unless validating nullability")
1312		}
1313
1314		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
1315
1316		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
1317
1318		msg := fmt.Sprintf(`\n******************************\n`+
1319			`The warnings encountered during nullability annotation validation did\n`+
1320			`not match the checked in file of expected warnings. The diffs are shown\n`+
1321			`above. You have two options:\n`+
1322			`   1. Resolve the differences by editing the nullability annotations.\n`+
1323			`   2. Update the file of expected warnings by running:\n`+
1324			`         cp %s %s\n`+
1325			`       and submitting the updated file as part of your change.`,
1326			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings)
1327
1328		rule := android.NewRuleBuilder(pctx, ctx)
1329
1330		rule.Command().
1331			Text("(").
1332			Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile).
1333			Text("&&").
1334			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
1335			Text(") || (").
1336			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1337			Text("; exit 38").
1338			Text(")")
1339
1340		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
1341	}
1342
1343	d.setOutputFiles(ctx)
1344}
1345
1346// This method sets the outputFiles property, which is used to set the
1347// OutputFilesProvider later.
1348// Droidstubs' tag supports specifying with the stubs type.
1349// While supporting the pre-existing tags, it also supports tags with
1350// the stubs type prefix. Some examples are shown below:
1351// {.annotations.zip} - pre-existing behavior. Returns the path to the
1352// annotation zip.
1353// {.exportable} - Returns the path to the exportable stubs src jar.
1354// {.exportable.annotations.zip} - Returns the path to the exportable
1355// annotations zip file.
1356// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions
1357// xml file. For unsupported combinations, the default everything output file
1358// is returned.
1359func (d *Droidstubs) setOutputFiles(ctx android.ModuleContext) {
1360	tagToOutputFileFunc := map[string]func(StubsType) (android.Path, error){
1361		"":                     d.StubsSrcJar,
1362		".docs.zip":            d.DocZip,
1363		".api.txt":             d.ApiFilePath,
1364		android.DefaultDistTag: d.ApiFilePath,
1365		".removed-api.txt":     d.RemovedApiFilePath,
1366		".annotations.zip":     d.AnnotationsZip,
1367		".api_versions.xml":    d.ApiVersionsXmlFilePath,
1368	}
1369	stubsTypeToPrefix := map[StubsType]string{
1370		Everything: "",
1371		Exportable: ".exportable",
1372	}
1373	for _, tag := range android.SortedKeys(tagToOutputFileFunc) {
1374		for _, stubType := range android.SortedKeys(stubsTypeToPrefix) {
1375			tagWithPrefix := stubsTypeToPrefix[stubType] + tag
1376			outputFile, err := tagToOutputFileFunc[tag](stubType)
1377			if err == nil && outputFile != nil {
1378				ctx.SetOutputFiles(android.Paths{outputFile}, tagWithPrefix)
1379			}
1380		}
1381	}
1382}
1383
1384func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
1385	api_file := d.properties.Check_api.Current.Api_file
1386	api_surface := d.properties.Api_surface
1387
1388	props := struct {
1389		Name        *string
1390		Api_surface *string
1391		Api_file    *string
1392		Visibility  []string
1393	}{}
1394
1395	props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
1396	props.Api_surface = api_surface
1397	props.Api_file = api_file
1398	props.Visibility = []string{"//visibility:override", "//visibility:public"}
1399
1400	ctx.CreateModule(ApiContributionFactory, &props)
1401}
1402
1403// TODO (b/262014796): Export the API contributions of CorePlatformApi
1404// A map to populate the api surface of a droidstub from a substring appearing in its name
1405// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
1406// use a strict naming convention
1407var (
1408	droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{
1409		// public is commented out since the core libraries use public in their java_sdk_library names
1410		"intracore":     android.SdkIntraCore,
1411		"intra.core":    android.SdkIntraCore,
1412		"system_server": android.SdkSystemServer,
1413		"system-server": android.SdkSystemServer,
1414		"system":        android.SdkSystem,
1415		"module_lib":    android.SdkModule,
1416		"module-lib":    android.SdkModule,
1417		"platform.api":  android.SdkCorePlatform,
1418		"test":          android.SdkTest,
1419		"toolchain":     android.SdkToolchain,
1420	}
1421)
1422
1423func StubsDefaultsFactory() android.Module {
1424	module := &DocDefaults{}
1425
1426	module.AddProperties(
1427		&JavadocProperties{},
1428		&DroidstubsProperties{},
1429	)
1430
1431	android.InitDefaultsModule(module)
1432
1433	return module
1434}
1435
1436var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
1437
1438type PrebuiltStubsSourcesProperties struct {
1439	Srcs []string `android:"path"`
1440
1441	// Name of the source soong module that gets shadowed by this prebuilt
1442	// If unspecified, follows the naming convention that the source module of
1443	// the prebuilt is Name() without "prebuilt_" prefix
1444	Source_module_name *string
1445
1446	// Non-nil if this prebuilt stub srcs  module was dynamically created by a java_sdk_library_import
1447	// The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
1448	// (without any prebuilt_ prefix)
1449	Created_by_java_sdk_library_name *string `blueprint:"mutated"`
1450}
1451
1452func (j *PrebuiltStubsSources) BaseModuleName() string {
1453	return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name())
1454}
1455
1456func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string {
1457	return j.properties.Created_by_java_sdk_library_name
1458}
1459
1460type PrebuiltStubsSources struct {
1461	android.ModuleBase
1462	android.DefaultableModuleBase
1463	embeddableInModuleAndImport
1464
1465	prebuilt android.Prebuilt
1466
1467	properties PrebuiltStubsSourcesProperties
1468
1469	stubsSrcJar android.Path
1470}
1471
1472func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) {
1473	return d.stubsSrcJar, nil
1474}
1475
1476func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1477	if len(p.properties.Srcs) != 1 {
1478		ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs))
1479		return
1480	}
1481
1482	src := p.properties.Srcs[0]
1483	if filepath.Ext(src) == ".srcjar" {
1484		// This is a srcjar. We can use it directly.
1485		p.stubsSrcJar = android.PathForModuleSrc(ctx, src)
1486	} else {
1487		outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
1488
1489		// This is a directory. Glob the contents just in case the directory does not exist.
1490		srcGlob := src + "/**/*"
1491		srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
1492
1493		// Although PathForModuleSrc can return nil if either the path doesn't exist or
1494		// the path components are invalid it won't in this case because no components
1495		// are specified and the module directory must exist in order to get this far.
1496		srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src)
1497
1498		rule := android.NewRuleBuilder(pctx, ctx)
1499		rule.Command().
1500			BuiltTool("soong_zip").
1501			Flag("-write_if_changed").
1502			Flag("-jar").
1503			FlagWithOutput("-o ", outPath).
1504			FlagWithArg("-C ", srcDir.String()).
1505			FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths)
1506		rule.Restat()
1507		rule.Build("zip src", "Create srcjar from prebuilt source")
1508		p.stubsSrcJar = outPath
1509	}
1510
1511	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "")
1512	// prebuilt droidstubs does not output "exportable" stubs.
1513	// Output the "everything" stubs srcjar file if the tag is ".exportable".
1514	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, ".exportable")
1515}
1516
1517func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
1518	return &p.prebuilt
1519}
1520
1521func (p *PrebuiltStubsSources) Name() string {
1522	return p.prebuilt.Name(p.ModuleBase.Name())
1523}
1524
1525// prebuilt_stubs_sources imports a set of java source files as if they were
1526// generated by droidstubs.
1527//
1528// By default, a prebuilt_stubs_sources has a single variant that expects a
1529// set of `.java` files generated by droidstubs.
1530//
1531// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
1532// for host modules.
1533//
1534// Intended only for use by sdk snapshots.
1535func PrebuiltStubsSourcesFactory() android.Module {
1536	module := &PrebuiltStubsSources{}
1537
1538	module.AddProperties(&module.properties)
1539	module.initModuleAndImport(module)
1540
1541	android.InitPrebuiltModule(module, &module.properties.Srcs)
1542	InitDroiddocModule(module, android.HostAndDeviceSupported)
1543	return module
1544}
1545