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