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