1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package java
16
17import (
18	"fmt"
19	"path/filepath"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24
25	"android/soong/android"
26	"android/soong/java/config"
27	"android/soong/remoteexec"
28)
29
30func init() {
31	RegisterDocsBuildComponents(android.InitRegistrationContext)
32	RegisterStubsBuildComponents(android.InitRegistrationContext)
33
34	// Register sdk member type.
35	android.RegisterSdkMemberType(&droidStubsSdkMemberType{
36		SdkMemberTypeBase: android.SdkMemberTypeBase{
37			PropertyName: "stubs_sources",
38			// stubs_sources can be used with sdk to provide the source stubs for APIs provided by
39			// the APEX.
40			SupportsSdk: true,
41		},
42	})
43}
44
45func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
46	ctx.RegisterModuleType("doc_defaults", DocDefaultsFactory)
47
48	ctx.RegisterModuleType("droiddoc", DroiddocFactory)
49	ctx.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
50	ctx.RegisterModuleType("droiddoc_exported_dir", ExportedDroiddocDirFactory)
51	ctx.RegisterModuleType("javadoc", JavadocFactory)
52	ctx.RegisterModuleType("javadoc_host", JavadocHostFactory)
53}
54
55func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
56	ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
57
58	ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
59	ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
60
61	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
62}
63
64var (
65	srcsLibTag = dependencyTag{name: "sources from javalib"}
66)
67
68type JavadocProperties struct {
69	// list of source files used to compile the Java module.  May be .java, .logtags, .proto,
70	// or .aidl files.
71	Srcs []string `android:"path,arch_variant"`
72
73	// list of directories rooted at the Android.bp file that will
74	// be added to the search paths for finding source files when passing package names.
75	Local_sourcepaths []string
76
77	// list of source files that should not be used to build the Java module.
78	// This is most useful in the arch/multilib variants to remove non-common files
79	// filegroup or genrule can be included within this property.
80	Exclude_srcs []string `android:"path,arch_variant"`
81
82	// list of package names that should actually be used. If this property is left unspecified,
83	// all the sources from the srcs property is used.
84	Filter_packages []string
85
86	// list of java libraries that will be in the classpath.
87	Libs []string `android:"arch_variant"`
88
89	// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
90	Installable *bool
91
92	// if not blank, set to the version of the sdk to compile against.
93	// Defaults to compiling against the current platform.
94	Sdk_version *string `android:"arch_variant"`
95
96	// When targeting 1.9 and above, override the modules to use with --system,
97	// otherwise provides defaults libraries to add to the bootclasspath.
98	// Defaults to "none"
99	System_modules *string
100
101	Aidl struct {
102		// Top level directories to pass to aidl tool
103		Include_dirs []string
104
105		// Directories rooted at the Android.bp file to pass to aidl tool
106		Local_include_dirs []string
107	}
108
109	// If not blank, set the java version passed to javadoc as -source
110	Java_version *string
111
112	// local files that are used within user customized droiddoc options.
113	Arg_files []string `android:"path"`
114
115	// user customized droiddoc args. Deprecated, use flags instead.
116	// Available variables for substitution:
117	//
118	//  $(location <label>): the path to the arg_files with name <label>
119	//  $$: a literal $
120	Args *string
121
122	// user customized droiddoc args. Not compatible with property args.
123	// Available variables for substitution:
124	//
125	//  $(location <label>): the path to the arg_files with name <label>
126	//  $$: a literal $
127	Flags []string
128
129	// names of the output files used in args that will be generated
130	Out []string
131
132	// If set, metalava is sandboxed to only read files explicitly specified on the command
133	// line. Defaults to false.
134	Sandbox *bool
135}
136
137type ApiToCheck struct {
138	// path to the API txt file that the new API extracted from source code is checked
139	// against. The path can be local to the module or from other module (via :module syntax).
140	Api_file *string `android:"path"`
141
142	// path to the API txt file that the new @removed API extractd from source code is
143	// checked against. The path can be local to the module or from other module (via
144	// :module syntax).
145	Removed_api_file *string `android:"path"`
146
147	// If not blank, path to the baseline txt file for approved API check violations.
148	Baseline_file *string `android:"path"`
149
150	// Arguments to the apicheck tool.
151	Args *string
152}
153
154type DroiddocProperties struct {
155	// directory relative to top of the source tree that contains doc templates files.
156	Custom_template *string
157
158	// directories under current module source which contains html/jd files.
159	Html_dirs []string
160
161	// set a value in the Clearsilver hdf namespace.
162	Hdf []string
163
164	// proofread file contains all of the text content of the javadocs concatenated into one file,
165	// suitable for spell-checking and other goodness.
166	Proofread_file *string
167
168	// a todo file lists the program elements that are missing documentation.
169	// At some point, this might be improved to show more warnings.
170	Todo_file *string `android:"path"`
171
172	// directory under current module source that provide additional resources (images).
173	Resourcesdir *string
174
175	// resources output directory under out/soong/.intermediates.
176	Resourcesoutdir *string
177
178	// if set to true, collect the values used by the Dev tools and
179	// write them in files packaged with the SDK. Defaults to false.
180	Write_sdk_values *bool
181
182	// index.html under current module will be copied to docs out dir, if not null.
183	Static_doc_index_redirect *string `android:"path"`
184
185	// source.properties under current module will be copied to docs out dir, if not null.
186	Static_doc_properties *string `android:"path"`
187
188	// a list of files under current module source dir which contains known tags in Java sources.
189	// filegroup or genrule can be included within this property.
190	Knowntags []string `android:"path"`
191
192	// the generated public API filename by Doclava.
193	Api_filename *string
194
195	// the generated removed API filename by Doclava.
196	Removed_api_filename *string
197
198	// the generated removed Dex API filename by Doclava.
199	Removed_dex_api_filename *string
200
201	// if set to false, don't allow droiddoc to generate stubs source files. Defaults to false.
202	Create_stubs *bool
203
204	Check_api struct {
205		Last_released ApiToCheck
206
207		Current ApiToCheck
208
209		// do not perform API check against Last_released, in the case that both two specified API
210		// files by Last_released are modules which don't exist.
211		Ignore_missing_latest_api *bool `blueprint:"mutated"`
212	}
213
214	// if set to true, generate docs through Dokka instead of Doclava.
215	Dokka_enabled *bool
216
217	// Compat config XML. Generates compat change documentation if set.
218	Compat_config *string `android:"path"`
219}
220
221type DroidstubsProperties struct {
222	// the generated public API filename by Metalava.
223	Api_filename *string
224
225	// the generated removed API filename by Metalava.
226	Removed_api_filename *string
227
228	// the generated removed Dex API filename by Metalava.
229	Removed_dex_api_filename *string
230
231	Check_api struct {
232		Last_released ApiToCheck
233
234		Current ApiToCheck
235
236		// The java_sdk_library module generates references to modules (i.e. filegroups)
237		// from which information about the latest API version can be obtained. As those
238		// modules may not exist (e.g. because a previous version has not been released) it
239		// sets ignore_missing_latest_api=true on the droidstubs modules it creates so
240		// that droidstubs can ignore those references if the modules do not yet exist.
241		//
242		// If true then this will ignore module references for modules that do not exist
243		// in properties that supply the previous version of the API.
244		//
245		// There are two sets of those:
246		// * Api_file, Removed_api_file in check_api.last_released
247		// * New_since in check_api.api_lint.new_since
248		//
249		// The first two must be set as a pair, so either they should both exist or neither
250		// should exist - in which case when this property is true they are ignored. If one
251		// exists and the other does not then it is an error.
252		Ignore_missing_latest_api *bool `blueprint:"mutated"`
253
254		Api_lint struct {
255			Enabled *bool
256
257			// If set, performs api_lint on any new APIs not found in the given signature file
258			New_since *string `android:"path"`
259
260			// If not blank, path to the baseline txt file for approved API lint violations.
261			Baseline_file *string `android:"path"`
262		}
263	}
264
265	// user can specify the version of previous released API file in order to do compatibility check.
266	Previous_api *string `android:"path"`
267
268	// is set to true, Metalava will allow framework SDK to contain annotations.
269	Annotations_enabled *bool
270
271	// a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
272	Merge_annotations_dirs []string
273
274	// a list of top-level directories containing Java stub files to merge show/hide annotations from.
275	Merge_inclusion_annotations_dirs []string
276
277	// a file containing a list of classes to do nullability validation for.
278	Validate_nullability_from_list *string
279
280	// a file containing expected warnings produced by validation of nullability annotations.
281	Check_nullability_warnings *string
282
283	// if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
284	Create_doc_stubs *bool
285
286	// if set to false then do not write out stubs. Defaults to true.
287	//
288	// TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
289	Generate_stubs *bool
290
291	// is set to true, Metalava will allow framework SDK to contain API levels annotations.
292	Api_levels_annotations_enabled *bool
293
294	// the dirs which Metalava extracts API levels annotations from.
295	Api_levels_annotations_dirs []string
296
297	// if set to true, collect the values used by the Dev tools and
298	// write them in files packaged with the SDK. Defaults to false.
299	Write_sdk_values *bool
300
301	// If set to true, .xml based public API file will be also generated, and
302	// JDiff tool will be invoked to genreate javadoc files. Defaults to false.
303	Jdiff_enabled *bool
304}
305
306//
307// Common flags passed down to build rule
308//
309type droiddocBuilderFlags struct {
310	bootClasspathArgs  string
311	classpathArgs      string
312	sourcepathArgs     string
313	dokkaClasspathArgs string
314	aidlFlags          string
315	aidlDeps           android.Paths
316
317	doclavaStubsFlags string
318	doclavaDocsFlags  string
319	postDoclavaCmds   string
320}
321
322func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
323	android.InitAndroidArchModule(module, hod, android.MultilibCommon)
324	android.InitDefaultableModule(module)
325}
326
327func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool {
328	if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") {
329		return false
330	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
331		return true
332	} else if String(apiToCheck.Api_file) != "" {
333		panic("for " + apiVersionTag + " removed_api_file has to be non-empty!")
334	} else if String(apiToCheck.Removed_api_file) != "" {
335		panic("for " + apiVersionTag + " api_file has to be non-empty!")
336	}
337
338	return false
339}
340
341func ignoreMissingModules(ctx android.BottomUpMutatorContext, apiToCheck *ApiToCheck) {
342	api_file := String(apiToCheck.Api_file)
343	removed_api_file := String(apiToCheck.Removed_api_file)
344
345	api_module := android.SrcIsModule(api_file)
346	removed_api_module := android.SrcIsModule(removed_api_file)
347
348	if api_module == "" || removed_api_module == "" {
349		return
350	}
351
352	if ctx.OtherModuleExists(api_module) || ctx.OtherModuleExists(removed_api_module) {
353		return
354	}
355
356	apiToCheck.Api_file = nil
357	apiToCheck.Removed_api_file = nil
358}
359
360// Used by xsd_config
361type ApiFilePath interface {
362	ApiFilePath() android.Path
363}
364
365type ApiStubsSrcProvider interface {
366	StubsSrcJar() android.Path
367}
368
369// Provider of information about API stubs, used by java_sdk_library.
370type ApiStubsProvider interface {
371	ApiFilePath
372	RemovedApiFilePath() android.Path
373
374	ApiStubsSrcProvider
375}
376
377//
378// Javadoc
379//
380type Javadoc struct {
381	android.ModuleBase
382	android.DefaultableModuleBase
383
384	properties JavadocProperties
385
386	srcJars     android.Paths
387	srcFiles    android.Paths
388	sourcepaths android.Paths
389	argFiles    android.Paths
390	implicits   android.Paths
391
392	args []string
393
394	docZip      android.WritablePath
395	stubsSrcJar android.WritablePath
396}
397
398func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
399	switch tag {
400	case "":
401		return android.Paths{j.stubsSrcJar}, nil
402	case ".docs.zip":
403		return android.Paths{j.docZip}, nil
404	default:
405		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
406	}
407}
408
409// javadoc converts .java source files to documentation using javadoc.
410func JavadocFactory() android.Module {
411	module := &Javadoc{}
412
413	module.AddProperties(&module.properties)
414
415	InitDroiddocModule(module, android.HostAndDeviceSupported)
416	return module
417}
418
419// javadoc_host converts .java source files to documentation using javadoc.
420func JavadocHostFactory() android.Module {
421	module := &Javadoc{}
422
423	module.AddProperties(&module.properties)
424
425	InitDroiddocModule(module, android.HostSupported)
426	return module
427}
428
429var _ android.OutputFileProducer = (*Javadoc)(nil)
430
431func (j *Javadoc) sdkVersion() sdkSpec {
432	return sdkSpecFrom(String(j.properties.Sdk_version))
433}
434
435func (j *Javadoc) systemModules() string {
436	return proptools.String(j.properties.System_modules)
437}
438
439func (j *Javadoc) minSdkVersion() sdkSpec {
440	return j.sdkVersion()
441}
442
443func (j *Javadoc) targetSdkVersion() sdkSpec {
444	return j.sdkVersion()
445}
446
447func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
448	if ctx.Device() {
449		sdkDep := decodeSdkDep(ctx, sdkContext(j))
450		if sdkDep.useModule {
451			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
452			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
453			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
454			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
455		}
456	}
457
458	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
459}
460
461func (j *Javadoc) collectAidlFlags(ctx android.ModuleContext, deps deps) droiddocBuilderFlags {
462	var flags droiddocBuilderFlags
463
464	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
465
466	return flags
467}
468
469func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
470	aidlIncludeDirs android.Paths) (string, android.Paths) {
471
472	aidlIncludes := android.PathsForModuleSrc(ctx, j.properties.Aidl.Local_include_dirs)
473	aidlIncludes = append(aidlIncludes, android.PathsForSource(ctx, j.properties.Aidl.Include_dirs)...)
474
475	var flags []string
476	var deps android.Paths
477
478	if aidlPreprocess.Valid() {
479		flags = append(flags, "-p"+aidlPreprocess.String())
480		deps = append(deps, aidlPreprocess.Path())
481	} else {
482		flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
483	}
484
485	flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
486	flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
487	if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
488		flags = append(flags, "-I"+src.String())
489	}
490
491	return strings.Join(flags, " "), deps
492}
493
494// TODO: remove the duplication between this and the one in gen.go
495func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
496	flags droiddocBuilderFlags) android.Paths {
497
498	outSrcFiles := make(android.Paths, 0, len(srcFiles))
499	var aidlSrcs android.Paths
500
501	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
502
503	for _, srcFile := range srcFiles {
504		switch srcFile.Ext() {
505		case ".aidl":
506			aidlSrcs = append(aidlSrcs, srcFile)
507		case ".logtags":
508			javaFile := genLogtags(ctx, srcFile)
509			outSrcFiles = append(outSrcFiles, javaFile)
510		default:
511			outSrcFiles = append(outSrcFiles, srcFile)
512		}
513	}
514
515	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
516	if len(aidlSrcs) > 0 {
517		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
518		outSrcFiles = append(outSrcFiles, srcJarFiles...)
519	}
520
521	return outSrcFiles
522}
523
524func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
525	var deps deps
526
527	sdkDep := decodeSdkDep(ctx, sdkContext(j))
528	if sdkDep.invalidVersion {
529		ctx.AddMissingDependencies(sdkDep.bootclasspath)
530		ctx.AddMissingDependencies(sdkDep.java9Classpath)
531	} else if sdkDep.useFiles {
532		deps.bootClasspath = append(deps.bootClasspath, sdkDep.jars...)
533		deps.aidlPreprocess = sdkDep.aidl
534	} else {
535		deps.aidlPreprocess = sdkDep.aidl
536	}
537
538	ctx.VisitDirectDeps(func(module android.Module) {
539		otherName := ctx.OtherModuleName(module)
540		tag := ctx.OtherModuleDependencyTag(module)
541
542		switch tag {
543		case bootClasspathTag:
544			if dep, ok := module.(Dependency); ok {
545				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
546			} else if sm, ok := module.(SystemModulesProvider); ok {
547				// A system modules dependency has been added to the bootclasspath
548				// so add its libs to the bootclasspath.
549				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
550			} else {
551				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
552			}
553		case libTag:
554			switch dep := module.(type) {
555			case SdkLibraryDependency:
556				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
557			case Dependency:
558				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
559				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
560			case android.SourceFileProducer:
561				checkProducesJars(ctx, dep)
562				deps.classpath = append(deps.classpath, dep.Srcs()...)
563			default:
564				ctx.ModuleErrorf("depends on non-java module %q", otherName)
565			}
566		case java9LibTag:
567			switch dep := module.(type) {
568			case Dependency:
569				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
570			default:
571				ctx.ModuleErrorf("depends on non-java module %q", otherName)
572			}
573		case systemModulesTag:
574			if deps.systemModules != nil {
575				panic("Found two system module dependencies")
576			}
577			sm := module.(SystemModulesProvider)
578			outputDir, outputDeps := sm.OutputDirAndDeps()
579			deps.systemModules = &systemModules{outputDir, outputDeps}
580		}
581	})
582	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
583	// may contain filegroup or genrule.
584	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
585	j.implicits = append(j.implicits, srcFiles...)
586
587	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
588		if filterPackages == nil {
589			return srcs
590		}
591		filtered := []android.Path{}
592		for _, src := range srcs {
593			if src.Ext() != ".java" {
594				// Don't filter-out non-Java (=generated sources) by package names. This is not ideal,
595				// but otherwise metalava emits stub sources having references to the generated AIDL classes
596				// in filtered-out pacages (e.g. com.android.internal.*).
597				// TODO(b/141149570) We need to fix this by introducing default private constructors or
598				// fixing metalava to not emit constructors having references to unknown classes.
599				filtered = append(filtered, src)
600				continue
601			}
602			packageName := strings.ReplaceAll(filepath.Dir(src.Rel()), "/", ".")
603			if android.HasAnyPrefix(packageName, filterPackages) {
604				filtered = append(filtered, src)
605			}
606		}
607		return filtered
608	}
609	srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
610
611	// While metalava needs package html files, it does not need them to be explicit on the command
612	// line. More importantly, the metalava rsp file is also used by the subsequent jdiff action if
613	// jdiff_enabled=true. javadoc complains if it receives html files on the command line. The filter
614	// below excludes html files from the rsp file for both metalava and jdiff. Note that the html
615	// files are still included as implicit inputs for successful remote execution and correct
616	// incremental builds.
617	filterHtml := func(srcs []android.Path) []android.Path {
618		filtered := []android.Path{}
619		for _, src := range srcs {
620			if src.Ext() == ".html" {
621				continue
622			}
623			filtered = append(filtered, src)
624		}
625		return filtered
626	}
627	srcFiles = filterHtml(srcFiles)
628
629	aidlFlags := j.collectAidlFlags(ctx, deps)
630	srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
631
632	// srcs may depend on some genrule output.
633	j.srcJars = srcFiles.FilterByExt(".srcjar")
634	j.srcJars = append(j.srcJars, deps.srcJars...)
635
636	j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
637	j.srcFiles = append(j.srcFiles, deps.srcs...)
638
639	if j.properties.Local_sourcepaths == nil && len(j.srcFiles) > 0 {
640		j.properties.Local_sourcepaths = append(j.properties.Local_sourcepaths, ".")
641	}
642	j.sourcepaths = android.PathsForModuleSrc(ctx, j.properties.Local_sourcepaths)
643
644	j.argFiles = android.PathsForModuleSrc(ctx, j.properties.Arg_files)
645	argFilesMap := map[string]string{}
646	argFileLabels := []string{}
647
648	for _, label := range j.properties.Arg_files {
649		var paths = android.PathsForModuleSrc(ctx, []string{label})
650		if _, exists := argFilesMap[label]; !exists {
651			argFilesMap[label] = strings.Join(paths.Strings(), " ")
652			argFileLabels = append(argFileLabels, label)
653		} else {
654			ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
655				label, argFilesMap[label], paths)
656		}
657	}
658
659	var argsPropertyName string
660	flags := make([]string, 0)
661	if j.properties.Args != nil && j.properties.Flags != nil {
662		ctx.PropertyErrorf("args", "flags is set. Cannot set args")
663	} else if args := proptools.String(j.properties.Args); args != "" {
664		flags = append(flags, args)
665		argsPropertyName = "args"
666	} else {
667		flags = append(flags, j.properties.Flags...)
668		argsPropertyName = "flags"
669	}
670
671	for _, flag := range flags {
672		args, err := android.Expand(flag, func(name string) (string, error) {
673			if strings.HasPrefix(name, "location ") {
674				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
675				if paths, ok := argFilesMap[label]; ok {
676					return paths, nil
677				} else {
678					return "", fmt.Errorf("unknown location label %q, expecting one of %q",
679						label, strings.Join(argFileLabels, ", "))
680				}
681			} else if name == "genDir" {
682				return android.PathForModuleGen(ctx).String(), nil
683			}
684			return "", fmt.Errorf("unknown variable '$(%s)'", name)
685		})
686
687		if err != nil {
688			ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
689		}
690		j.args = append(j.args, args)
691	}
692
693	return deps
694}
695
696func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
697	j.addDeps(ctx)
698}
699
700func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
701	deps := j.collectDeps(ctx)
702
703	j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
704
705	outDir := android.PathForModuleOut(ctx, "out")
706	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
707
708	j.stubsSrcJar = nil
709
710	rule := android.NewRuleBuilder()
711
712	rule.Command().Text("rm -rf").Text(outDir.String())
713	rule.Command().Text("mkdir -p").Text(outDir.String())
714
715	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, j.srcJars)
716
717	javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
718
719	cmd := javadocSystemModulesCmd(ctx, rule, j.srcFiles, outDir, srcJarDir, srcJarList,
720		deps.systemModules, deps.classpath, j.sourcepaths)
721
722	cmd.FlagWithArg("-source ", javaVersion.String()).
723		Flag("-J-Xmx1024m").
724		Flag("-XDignore.symbol.file").
725		Flag("-Xdoclint:none")
726
727	rule.Command().
728		BuiltTool(ctx, "soong_zip").
729		Flag("-write_if_changed").
730		Flag("-d").
731		FlagWithOutput("-o ", j.docZip).
732		FlagWithArg("-C ", outDir.String()).
733		FlagWithArg("-D ", outDir.String())
734
735	rule.Restat()
736
737	zipSyncCleanupCmd(rule, srcJarDir)
738
739	rule.Build(pctx, ctx, "javadoc", "javadoc")
740}
741
742//
743// Droiddoc
744//
745type Droiddoc struct {
746	Javadoc
747
748	properties        DroiddocProperties
749	apiFile           android.WritablePath
750	privateApiFile    android.WritablePath
751	removedApiFile    android.WritablePath
752	removedDexApiFile android.WritablePath
753
754	checkCurrentApiTimestamp      android.WritablePath
755	updateCurrentApiTimestamp     android.WritablePath
756	checkLastReleasedApiTimestamp android.WritablePath
757
758	apiFilePath android.Path
759}
760
761// droiddoc converts .java source files to documentation using doclava or dokka.
762func DroiddocFactory() android.Module {
763	module := &Droiddoc{}
764
765	module.AddProperties(&module.properties,
766		&module.Javadoc.properties)
767
768	InitDroiddocModule(module, android.HostAndDeviceSupported)
769	return module
770}
771
772// droiddoc_host converts .java source files to documentation using doclava or dokka.
773func DroiddocHostFactory() android.Module {
774	module := &Droiddoc{}
775
776	module.AddProperties(&module.properties,
777		&module.Javadoc.properties)
778
779	InitDroiddocModule(module, android.HostSupported)
780	return module
781}
782
783func (d *Droiddoc) ApiFilePath() android.Path {
784	return d.apiFilePath
785}
786
787func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) {
788	d.Javadoc.addDeps(ctx)
789
790	if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
791		ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
792	}
793
794	if String(d.properties.Custom_template) != "" {
795		ctx.AddDependency(ctx.Module(), droiddocTemplateTag, String(d.properties.Custom_template))
796	}
797}
798
799func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, docletPath classpath) {
800	buildNumberFile := ctx.Config().BuildNumberFile(ctx)
801	// Droiddoc always gets "-source 1.8" because it doesn't support 1.9 sources.  For modules with 1.9
802	// sources, droiddoc will get sources produced by metalava which will have already stripped out the
803	// 1.9 language features.
804	cmd.FlagWithArg("-source ", "1.8").
805		Flag("-J-Xmx1600m").
806		Flag("-J-XX:-OmitStackTraceInFastThrow").
807		Flag("-XDignore.symbol.file").
808		FlagWithArg("-doclet ", "com.google.doclava.Doclava").
809		FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
810		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
811		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
812
813	if String(d.properties.Custom_template) == "" {
814		// TODO: This is almost always droiddoc-templates-sdk
815		ctx.PropertyErrorf("custom_template", "must specify a template")
816	}
817
818	ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
819		if t, ok := m.(*ExportedDroiddocDir); ok {
820			cmd.FlagWithArg("-templatedir ", t.dir.String()).Implicits(t.deps)
821		} else {
822			ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_exported_dir", ctx.OtherModuleName(m))
823		}
824	})
825
826	if len(d.properties.Html_dirs) > 0 {
827		htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
828		cmd.FlagWithArg("-htmldir ", htmlDir.String()).
829			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")}))
830	}
831
832	if len(d.properties.Html_dirs) > 1 {
833		htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
834		cmd.FlagWithArg("-htmldir2 ", htmlDir2.String()).
835			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[1], "**/*")}))
836	}
837
838	if len(d.properties.Html_dirs) > 2 {
839		ctx.PropertyErrorf("html_dirs", "Droiddoc only supports up to 2 html dirs")
840	}
841
842	knownTags := android.PathsForModuleSrc(ctx, d.properties.Knowntags)
843	cmd.FlagForEachInput("-knowntags ", knownTags)
844
845	cmd.FlagForEachArg("-hdf ", d.properties.Hdf)
846
847	if String(d.properties.Proofread_file) != "" {
848		proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
849		cmd.FlagWithOutput("-proofread ", proofreadFile)
850	}
851
852	if String(d.properties.Todo_file) != "" {
853		// tricky part:
854		// we should not compute full path for todo_file through PathForModuleOut().
855		// the non-standard doclet will get the full path relative to "-o".
856		cmd.FlagWithArg("-todo ", String(d.properties.Todo_file)).
857			ImplicitOutput(android.PathForModuleOut(ctx, String(d.properties.Todo_file)))
858	}
859
860	if String(d.properties.Resourcesdir) != "" {
861		// TODO: should we add files under resourcesDir to the implicits? It seems that
862		// resourcesDir is one sub dir of htmlDir
863		resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
864		cmd.FlagWithArg("-resourcesdir ", resourcesDir.String())
865	}
866
867	if String(d.properties.Resourcesoutdir) != "" {
868		// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
869		cmd.FlagWithArg("-resourcesoutdir ", String(d.properties.Resourcesoutdir))
870	}
871}
872
873func (d *Droiddoc) createStubs() bool {
874	return BoolDefault(d.properties.Create_stubs, false)
875}
876
877func (d *Droiddoc) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
878	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
879		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
880		String(d.properties.Api_filename) != "" {
881
882		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
883		cmd.FlagWithOutput("-api ", d.apiFile)
884		d.apiFilePath = d.apiFile
885	}
886
887	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
888		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
889		String(d.properties.Removed_api_filename) != "" {
890		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
891		cmd.FlagWithOutput("-removedApi ", d.removedApiFile)
892	}
893
894	if String(d.properties.Removed_dex_api_filename) != "" {
895		d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
896		cmd.FlagWithOutput("-removedDexApi ", d.removedDexApiFile)
897	}
898
899	if d.createStubs() {
900		cmd.FlagWithArg("-stubs ", stubsDir.String())
901	}
902
903	if Bool(d.properties.Write_sdk_values) {
904		cmd.FlagWithArg("-sdkvalues ", android.PathForModuleOut(ctx, "out").String())
905	}
906}
907
908func (d *Droiddoc) postDoclavaCmds(ctx android.ModuleContext, rule *android.RuleBuilder) {
909	if String(d.properties.Static_doc_index_redirect) != "" {
910		staticDocIndexRedirect := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_index_redirect))
911		rule.Command().Text("cp").
912			Input(staticDocIndexRedirect).
913			Output(android.PathForModuleOut(ctx, "out", "index.html"))
914	}
915
916	if String(d.properties.Static_doc_properties) != "" {
917		staticDocProperties := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_properties))
918		rule.Command().Text("cp").
919			Input(staticDocProperties).
920			Output(android.PathForModuleOut(ctx, "out", "source.properties"))
921	}
922}
923
924func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
925	outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
926
927	cmd := rule.Command().
928		BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
929		Flag(config.JavacVmFlags).
930		FlagWithArg("-encoding ", "UTF-8").
931		FlagWithRspFileInputList("@", srcs).
932		FlagWithInput("@", srcJarList)
933
934	// TODO(ccross): Remove this if- statement once we finish migration for all Doclava
935	// based stubs generation.
936	// In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
937	// dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
938	// the correct package name base path.
939	if len(sourcepaths) > 0 {
940		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
941	} else {
942		cmd.FlagWithArg("-sourcepath ", srcJarDir.String())
943	}
944
945	cmd.FlagWithArg("-d ", outDir.String()).
946		Flag("-quiet")
947
948	return cmd
949}
950
951func javadocSystemModulesCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
952	outDir, srcJarDir, srcJarList android.Path, systemModules *systemModules,
953	classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
954
955	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
956
957	flag, deps := systemModules.FormJavaSystemModulesPath(ctx.Device())
958	cmd.Flag(flag).Implicits(deps)
959
960	cmd.FlagWithArg("--patch-module ", "java.base=.")
961
962	if len(classpath) > 0 {
963		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
964	}
965
966	return cmd
967}
968
969func javadocBootclasspathCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
970	outDir, srcJarDir, srcJarList android.Path, bootclasspath, classpath classpath,
971	sourcepaths android.Paths) *android.RuleBuilderCommand {
972
973	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
974
975	if len(bootclasspath) == 0 && ctx.Device() {
976		// explicitly specify -bootclasspath "" if the bootclasspath is empty to
977		// ensure java does not fall back to the default bootclasspath.
978		cmd.FlagWithArg("-bootclasspath ", `""`)
979	} else if len(bootclasspath) > 0 {
980		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
981	}
982
983	if len(classpath) > 0 {
984		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
985	}
986
987	return cmd
988}
989
990func dokkaCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
991	outDir, srcJarDir android.Path, bootclasspath, classpath classpath) *android.RuleBuilderCommand {
992
993	// Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
994	dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
995
996	return rule.Command().
997		BuiltTool(ctx, "dokka").
998		Flag(config.JavacVmFlags).
999		Flag(srcJarDir.String()).
1000		FlagWithInputList("-classpath ", dokkaClasspath, ":").
1001		FlagWithArg("-format ", "dac").
1002		FlagWithArg("-dacRoot ", "/reference/kotlin").
1003		FlagWithArg("-output ", outDir.String())
1004}
1005
1006func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1007	deps := d.Javadoc.collectDeps(ctx)
1008
1009	d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
1010	d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
1011
1012	jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
1013	doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
1014	java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
1015	checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")}
1016
1017	outDir := android.PathForModuleOut(ctx, "out")
1018	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
1019	stubsDir := android.PathForModuleOut(ctx, "stubsDir")
1020
1021	rule := android.NewRuleBuilder()
1022
1023	rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
1024	rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
1025
1026	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
1027
1028	var cmd *android.RuleBuilderCommand
1029	if Bool(d.properties.Dokka_enabled) {
1030		cmd = dokkaCmd(ctx, rule, outDir, srcJarDir, deps.bootClasspath, deps.classpath)
1031	} else {
1032		cmd = javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
1033			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
1034	}
1035
1036	d.stubsFlags(ctx, cmd, stubsDir)
1037
1038	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
1039
1040	if d.properties.Compat_config != nil {
1041		compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
1042		cmd.FlagWithInput("-compatconfig ", compatConfig)
1043	}
1044
1045	var desc string
1046	if Bool(d.properties.Dokka_enabled) {
1047		desc = "dokka"
1048	} else {
1049		d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
1050
1051		for _, o := range d.Javadoc.properties.Out {
1052			cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
1053		}
1054
1055		d.postDoclavaCmds(ctx, rule)
1056		desc = "doclava"
1057	}
1058
1059	rule.Command().
1060		BuiltTool(ctx, "soong_zip").
1061		Flag("-write_if_changed").
1062		Flag("-d").
1063		FlagWithOutput("-o ", d.docZip).
1064		FlagWithArg("-C ", outDir.String()).
1065		FlagWithArg("-D ", outDir.String())
1066
1067	rule.Command().
1068		BuiltTool(ctx, "soong_zip").
1069		Flag("-write_if_changed").
1070		Flag("-jar").
1071		FlagWithOutput("-o ", d.stubsSrcJar).
1072		FlagWithArg("-C ", stubsDir.String()).
1073		FlagWithArg("-D ", stubsDir.String())
1074
1075	rule.Restat()
1076
1077	zipSyncCleanupCmd(rule, srcJarDir)
1078
1079	rule.Build(pctx, ctx, "javadoc", desc)
1080
1081	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
1082		!ctx.Config().IsPdkBuild() {
1083
1084		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
1085		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
1086
1087		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
1088
1089		rule := android.NewRuleBuilder()
1090
1091		rule.Command().Text("( true")
1092
1093		rule.Command().
1094			BuiltTool(ctx, "apicheck").
1095			Flag("-JXmx1024m").
1096			FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
1097			OptionalFlag(d.properties.Check_api.Current.Args).
1098			Input(apiFile).
1099			Input(d.apiFile).
1100			Input(removedApiFile).
1101			Input(d.removedApiFile)
1102
1103		msg := fmt.Sprintf(`\n******************************\n`+
1104			`You have tried to change the API from what has been previously approved.\n\n`+
1105			`To make these errors go away, you have two choices:\n`+
1106			`   1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
1107			`      errors above.\n\n`+
1108			`   2. You can update current.txt by executing the following command:\n`+
1109			`         make %s-update-current-api\n\n`+
1110			`      To submit the revised current.txt to the main Android repository,\n`+
1111			`      you will need approval.\n`+
1112			`******************************\n`, ctx.ModuleName())
1113
1114		rule.Command().
1115			Text("touch").Output(d.checkCurrentApiTimestamp).
1116			Text(") || (").
1117			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1118			Text("; exit 38").
1119			Text(")")
1120
1121		rule.Build(pctx, ctx, "doclavaCurrentApiCheck", "check current API")
1122
1123		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
1124
1125		// update API rule
1126		rule = android.NewRuleBuilder()
1127
1128		rule.Command().Text("( true")
1129
1130		rule.Command().
1131			Text("cp").Flag("-f").
1132			Input(d.apiFile).Flag(apiFile.String())
1133
1134		rule.Command().
1135			Text("cp").Flag("-f").
1136			Input(d.removedApiFile).Flag(removedApiFile.String())
1137
1138		msg = "failed to update public API"
1139
1140		rule.Command().
1141			Text("touch").Output(d.updateCurrentApiTimestamp).
1142			Text(") || (").
1143			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1144			Text("; exit 38").
1145			Text(")")
1146
1147		rule.Build(pctx, ctx, "doclavaCurrentApiUpdate", "update current API")
1148	}
1149
1150	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
1151		!ctx.Config().IsPdkBuild() {
1152
1153		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
1154		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
1155
1156		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
1157
1158		rule := android.NewRuleBuilder()
1159
1160		rule.Command().
1161			Text("(").
1162			BuiltTool(ctx, "apicheck").
1163			Flag("-JXmx1024m").
1164			FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
1165			OptionalFlag(d.properties.Check_api.Last_released.Args).
1166			Input(apiFile).
1167			Input(d.apiFile).
1168			Input(removedApiFile).
1169			Input(d.removedApiFile)
1170
1171		msg := `\n******************************\n` +
1172			`You have tried to change the API from what has been previously released in\n` +
1173			`an SDK.  Please fix the errors listed above.\n` +
1174			`******************************\n`
1175
1176		rule.Command().
1177			Text("touch").Output(d.checkLastReleasedApiTimestamp).
1178			Text(") || (").
1179			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1180			Text("; exit 38").
1181			Text(")")
1182
1183		rule.Build(pctx, ctx, "doclavaLastApiCheck", "check last API")
1184	}
1185}
1186
1187//
1188// Droidstubs
1189//
1190type Droidstubs struct {
1191	Javadoc
1192	android.SdkBase
1193
1194	properties              DroidstubsProperties
1195	apiFile                 android.WritablePath
1196	apiXmlFile              android.WritablePath
1197	lastReleasedApiXmlFile  android.WritablePath
1198	privateApiFile          android.WritablePath
1199	removedApiFile          android.WritablePath
1200	removedDexApiFile       android.WritablePath
1201	nullabilityWarningsFile android.WritablePath
1202
1203	checkCurrentApiTimestamp      android.WritablePath
1204	updateCurrentApiTimestamp     android.WritablePath
1205	checkLastReleasedApiTimestamp android.WritablePath
1206	apiLintTimestamp              android.WritablePath
1207	apiLintReport                 android.WritablePath
1208
1209	checkNullabilityWarningsTimestamp android.WritablePath
1210
1211	annotationsZip android.WritablePath
1212	apiVersionsXml android.WritablePath
1213
1214	apiFilePath android.Path
1215
1216	jdiffDocZip      android.WritablePath
1217	jdiffStubsSrcJar android.WritablePath
1218
1219	metadataZip android.WritablePath
1220	metadataDir android.WritablePath
1221}
1222
1223// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
1224// documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
1225// a droiddoc module to generate documentation.
1226func DroidstubsFactory() android.Module {
1227	module := &Droidstubs{}
1228
1229	module.AddProperties(&module.properties,
1230		&module.Javadoc.properties)
1231
1232	InitDroiddocModule(module, android.HostAndDeviceSupported)
1233	android.InitSdkAwareModule(module)
1234	return module
1235}
1236
1237// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
1238// to be documented, filtering out hidden classes and methods.  The resulting .java files are intended to be
1239// passed to a droiddoc_host module to generate documentation.  Use a droidstubs_host instead of a droidstubs
1240// module when symbols needed by the source files are provided by java_library_host modules.
1241func DroidstubsHostFactory() android.Module {
1242	module := &Droidstubs{}
1243
1244	module.AddProperties(&module.properties,
1245		&module.Javadoc.properties)
1246
1247	InitDroiddocModule(module, android.HostSupported)
1248	return module
1249}
1250
1251func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
1252	switch tag {
1253	case "":
1254		return android.Paths{d.stubsSrcJar}, nil
1255	case ".docs.zip":
1256		return android.Paths{d.docZip}, nil
1257	case ".annotations.zip":
1258		return android.Paths{d.annotationsZip}, nil
1259	case ".api_versions.xml":
1260		return android.Paths{d.apiVersionsXml}, nil
1261	default:
1262		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
1263	}
1264}
1265
1266func (d *Droidstubs) ApiFilePath() android.Path {
1267	return d.apiFilePath
1268}
1269
1270func (d *Droidstubs) RemovedApiFilePath() android.Path {
1271	return d.removedApiFile
1272}
1273
1274func (d *Droidstubs) StubsSrcJar() android.Path {
1275	return d.stubsSrcJar
1276}
1277
1278func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
1279	d.Javadoc.addDeps(ctx)
1280
1281	// If requested clear any properties that provide information about the latest version
1282	// of an API and which reference non-existent modules.
1283	if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
1284		ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
1285
1286		// If the new_since references a module, e.g. :module-latest-api and the module
1287		// does not exist then clear it.
1288		newSinceSrc := d.properties.Check_api.Api_lint.New_since
1289		newSinceSrcModule := android.SrcIsModule(proptools.String(newSinceSrc))
1290		if newSinceSrcModule != "" && !ctx.OtherModuleExists(newSinceSrcModule) {
1291			d.properties.Check_api.Api_lint.New_since = nil
1292		}
1293	}
1294
1295	if len(d.properties.Merge_annotations_dirs) != 0 {
1296		for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
1297			ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
1298		}
1299	}
1300
1301	if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
1302		for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
1303			ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
1304		}
1305	}
1306
1307	if len(d.properties.Api_levels_annotations_dirs) != 0 {
1308		for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
1309			ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
1310		}
1311	}
1312}
1313
1314func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
1315	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
1316		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
1317		String(d.properties.Api_filename) != "" {
1318		d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
1319		cmd.FlagWithOutput("--api ", d.apiFile)
1320		d.apiFilePath = d.apiFile
1321	}
1322
1323	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
1324		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
1325		String(d.properties.Removed_api_filename) != "" {
1326		d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
1327		cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
1328	}
1329
1330	if String(d.properties.Removed_dex_api_filename) != "" {
1331		d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
1332		cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
1333	}
1334
1335	if Bool(d.properties.Write_sdk_values) {
1336		d.metadataDir = android.PathForModuleOut(ctx, "metadata")
1337		cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
1338	}
1339
1340	if stubsDir.Valid() {
1341		if Bool(d.properties.Create_doc_stubs) {
1342			cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
1343		} else {
1344			cmd.FlagWithArg("--stubs ", stubsDir.String())
1345			cmd.Flag("--exclude-documentation-from-stubs")
1346		}
1347	}
1348}
1349
1350func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
1351	if Bool(d.properties.Annotations_enabled) {
1352		cmd.Flag("--include-annotations")
1353
1354		validatingNullability :=
1355			android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
1356				String(d.properties.Validate_nullability_from_list) != ""
1357
1358		migratingNullability := String(d.properties.Previous_api) != ""
1359		if migratingNullability {
1360			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
1361			cmd.FlagWithInput("--migrate-nullness ", previousApi)
1362		}
1363
1364		if s := String(d.properties.Validate_nullability_from_list); s != "" {
1365			cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
1366		}
1367
1368		if validatingNullability {
1369			d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
1370			cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
1371		}
1372
1373		d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
1374		cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
1375
1376		if len(d.properties.Merge_annotations_dirs) != 0 {
1377			d.mergeAnnoDirFlags(ctx, cmd)
1378		}
1379
1380		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
1381		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
1382			FlagWithArg("--hide ", "SuperfluousPrefix").
1383			FlagWithArg("--hide ", "AnnotationExtraction")
1384	}
1385}
1386
1387func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
1388	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
1389		if t, ok := m.(*ExportedDroiddocDir); ok {
1390			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
1391		} else {
1392			ctx.PropertyErrorf("merge_annotations_dirs",
1393				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
1394		}
1395	})
1396}
1397
1398func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
1399	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
1400		if t, ok := m.(*ExportedDroiddocDir); ok {
1401			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
1402		} else {
1403			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
1404				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
1405		}
1406	})
1407}
1408
1409func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
1410	if Bool(d.properties.Api_levels_annotations_enabled) {
1411		d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
1412
1413		if len(d.properties.Api_levels_annotations_dirs) == 0 {
1414			ctx.PropertyErrorf("api_levels_annotations_dirs",
1415				"has to be non-empty if api levels annotations was enabled!")
1416		}
1417
1418		cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
1419		cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
1420		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
1421		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
1422
1423		ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
1424			if t, ok := m.(*ExportedDroiddocDir); ok {
1425				for _, dep := range t.deps {
1426					if strings.HasSuffix(dep.String(), "android.jar") {
1427						cmd.Implicit(dep)
1428					}
1429				}
1430				cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/android.jar")
1431			} else {
1432				ctx.PropertyErrorf("api_levels_annotations_dirs",
1433					"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
1434			}
1435		})
1436
1437	}
1438}
1439
1440func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
1441	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() && d.apiFile != nil {
1442		if d.apiFile.String() == "" {
1443			ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
1444		}
1445
1446		d.apiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.xml")
1447		cmd.FlagWithOutput("--api-xml ", d.apiXmlFile)
1448
1449		if String(d.properties.Check_api.Last_released.Api_file) == "" {
1450			ctx.PropertyErrorf("check_api.last_released.api_file",
1451				"has to be non-empty if jdiff was enabled!")
1452		}
1453
1454		lastReleasedApi := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
1455		d.lastReleasedApiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_last_released_api.xml")
1456		cmd.FlagWithInput("--convert-to-jdiff ", lastReleasedApi).Output(d.lastReleasedApiXmlFile)
1457	}
1458}
1459
1460func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
1461	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicitsRsp android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
1462	// Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
1463	rule.HighMem()
1464	cmd := rule.Command()
1465	if ctx.Config().IsEnvTrue("RBE_METALAVA") {
1466		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
1467		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
1468		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
1469		labels := map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"}
1470		if !sandbox {
1471			execStrategy = remoteexec.LocalExecStrategy
1472			labels["shallow"] = "true"
1473		}
1474		inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
1475		if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
1476			inputs = append(inputs, strings.Split(v, ",")...)
1477		}
1478		cmd.Text((&remoteexec.REParams{
1479			Labels:          labels,
1480			ExecStrategy:    execStrategy,
1481			Inputs:          inputs,
1482			RSPFile:         implicitsRsp.String(),
1483			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
1484			Platform:        map[string]string{remoteexec.PoolKey: pool},
1485		}).NoVarTemplate(ctx.Config()))
1486	}
1487
1488	cmd.BuiltTool(ctx, "metalava").
1489		Flag(config.JavacVmFlags).
1490		FlagWithArg("-encoding ", "UTF-8").
1491		FlagWithArg("-source ", javaVersion.String()).
1492		FlagWithRspFileInputList("@", srcs).
1493		FlagWithInput("@", srcJarList)
1494
1495	if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
1496		cmd.Implicit(android.PathForSource(ctx, javaHome))
1497	}
1498
1499	if sandbox {
1500		cmd.FlagWithOutput("--strict-input-files ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
1501	} else {
1502		cmd.FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
1503	}
1504
1505	if implicitsRsp != nil {
1506		cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
1507	}
1508
1509	if len(bootclasspath) > 0 {
1510		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
1511	}
1512
1513	if len(classpath) > 0 {
1514		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
1515	}
1516
1517	if len(sourcepaths) > 0 {
1518		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
1519	} else {
1520		cmd.FlagWithArg("-sourcepath ", `""`)
1521	}
1522
1523	cmd.Flag("--no-banner").
1524		Flag("--color").
1525		Flag("--quiet").
1526		Flag("--format=v2").
1527		FlagWithArg("--repeat-errors-max ", "10").
1528		FlagWithArg("--hide ", "UnresolvedImport")
1529
1530	return cmd
1531}
1532
1533func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1534	deps := d.Javadoc.collectDeps(ctx)
1535
1536	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
1537
1538	// Create rule for metalava
1539
1540	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
1541
1542	rule := android.NewRuleBuilder()
1543
1544	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
1545	var stubsDir android.OptionalPath
1546	if generateStubs {
1547		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
1548		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "stubsDir"))
1549		rule.Command().Text("rm -rf").Text(stubsDir.String())
1550		rule.Command().Text("mkdir -p").Text(stubsDir.String())
1551	}
1552
1553	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
1554
1555	implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
1556
1557	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
1558		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp,
1559		Bool(d.Javadoc.properties.Sandbox))
1560	cmd.Implicits(d.Javadoc.implicits)
1561
1562	d.stubsFlags(ctx, cmd, stubsDir)
1563
1564	d.annotationsFlags(ctx, cmd)
1565	d.inclusionAnnotationsFlags(ctx, cmd)
1566	d.apiLevelsAnnotationsFlags(ctx, cmd)
1567	d.apiToXmlFlags(ctx, cmd)
1568
1569	if android.InList("--generate-documentation", d.Javadoc.args) {
1570		// Currently Metalava have the ability to invoke Javadoc in a seperate process.
1571		// Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
1572		// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
1573		d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
1574	}
1575
1576	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
1577	for _, o := range d.Javadoc.properties.Out {
1578		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
1579	}
1580
1581	// Add options for the other optional tasks: API-lint and check-released.
1582	// We generate separate timestamp files for them.
1583
1584	doApiLint := false
1585	doCheckReleased := false
1586
1587	// Add API lint options.
1588
1589	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().IsPdkBuild() {
1590		doApiLint = true
1591
1592		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
1593		if newSince.Valid() {
1594			cmd.FlagWithInput("--api-lint ", newSince.Path())
1595		} else {
1596			cmd.Flag("--api-lint")
1597		}
1598		d.apiLintReport = android.PathForModuleOut(ctx, "api_lint_report.txt")
1599		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
1600
1601		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
1602		updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
1603		d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
1604
1605		// Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
1606		// However, because $' ... ' doesn't expand environmental variables, we can't just embed
1607		// $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again,
1608		// which is why we have '"$PWD"$' in it.
1609		//
1610		// TODO: metalava also has a slightly different message hardcoded. Should we unify this
1611		// message and metalava's one?
1612		msg := `$'` + // Enclose with $' ... '
1613			`************************************************************\n` +
1614			`Your API changes are triggering API Lint warnings or errors.\n` +
1615			`To make these errors go away, fix the code according to the\n` +
1616			`error and/or warning messages above.\n` +
1617			`\n` +
1618			`If it is not possible to do so, there are workarounds:\n` +
1619			`\n` +
1620			`1. You can suppress the errors with @SuppressLint("<id>")\n`
1621
1622		if baselineFile.Valid() {
1623			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
1624			cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
1625
1626			msg += fmt.Sprintf(``+
1627				`2. You can update the baseline by executing the following\n`+
1628				`   command:\n`+
1629				`       cp \\\n`+
1630				`       "'"$PWD"$'/%s" \\\n`+
1631				`       "'"$PWD"$'/%s"\n`+
1632				`   To submit the revised baseline.txt to the main Android\n`+
1633				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
1634		} else {
1635			msg += fmt.Sprintf(``+
1636				`2. You can add a baseline file of existing lint failures\n`+
1637				`   to the build rule of %s.\n`, d.Name())
1638		}
1639		// Note the message ends with a ' (single quote), to close the $' ... ' .
1640		msg += `************************************************************\n'`
1641
1642		cmd.FlagWithArg("--error-message:api-lint ", msg)
1643	}
1644
1645	// Add "check released" options. (Detect incompatible API changes from the last public release)
1646
1647	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") &&
1648		!ctx.Config().IsPdkBuild() {
1649		doCheckReleased = true
1650
1651		if len(d.Javadoc.properties.Out) > 0 {
1652			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
1653		}
1654
1655		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
1656		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
1657		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
1658		updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt")
1659
1660		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
1661
1662		cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
1663		cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
1664
1665		if baselineFile.Valid() {
1666			cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
1667			cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
1668		}
1669
1670		// Note this string includes quote ($' ... '), which decodes the "\n"s.
1671		msg := `$'\n******************************\n` +
1672			`You have tried to change the API from what has been previously released in\n` +
1673			`an SDK.  Please fix the errors listed above.\n` +
1674			`******************************\n'`
1675
1676		cmd.FlagWithArg("--error-message:compatibility:released ", msg)
1677	}
1678
1679	impRule := android.NewRuleBuilder()
1680	impCmd := impRule.Command()
1681	// An action that copies the ninja generated rsp file to a new location. This allows us to
1682	// add a large number of inputs to a file without exceeding bash command length limits (which
1683	// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
1684	// rsp file to be ${output}.rsp.
1685	impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
1686	impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
1687	cmd.Implicit(implicitsRsp)
1688
1689	if generateStubs {
1690		rule.Command().
1691			BuiltTool(ctx, "soong_zip").
1692			Flag("-write_if_changed").
1693			Flag("-jar").
1694			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
1695			FlagWithArg("-C ", stubsDir.String()).
1696			FlagWithArg("-D ", stubsDir.String())
1697	}
1698
1699	if Bool(d.properties.Write_sdk_values) {
1700		d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
1701		rule.Command().
1702			BuiltTool(ctx, "soong_zip").
1703			Flag("-write_if_changed").
1704			Flag("-d").
1705			FlagWithOutput("-o ", d.metadataZip).
1706			FlagWithArg("-C ", d.metadataDir.String()).
1707			FlagWithArg("-D ", d.metadataDir.String())
1708	}
1709
1710	// TODO: We don't really need two separate API files, but this is a reminiscence of how
1711	// we used to run metalava separately for API lint and the "last_released" check. Unify them.
1712	if doApiLint {
1713		rule.Command().Text("touch").Output(d.apiLintTimestamp)
1714	}
1715	if doCheckReleased {
1716		rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
1717	}
1718
1719	rule.Restat()
1720
1721	zipSyncCleanupCmd(rule, srcJarDir)
1722
1723	rule.Build(pctx, ctx, "metalava", "metalava merged")
1724
1725	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") &&
1726		!ctx.Config().IsPdkBuild() {
1727
1728		if len(d.Javadoc.properties.Out) > 0 {
1729			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
1730		}
1731
1732		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
1733		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
1734		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
1735
1736		if baselineFile.Valid() {
1737			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
1738		}
1739
1740		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
1741
1742		rule := android.NewRuleBuilder()
1743
1744		// Diff command line.
1745		// -F matches the closest "opening" line, such as "package android {"
1746		// and "  public class Intent {".
1747		diff := `diff -u -F '{ *$'`
1748
1749		rule.Command().Text("( true")
1750		rule.Command().
1751			Text(diff).
1752			Input(apiFile).Input(d.apiFile)
1753
1754		rule.Command().
1755			Text(diff).
1756			Input(removedApiFile).Input(d.removedApiFile)
1757
1758		msg := fmt.Sprintf(`\n******************************\n`+
1759			`You have tried to change the API from what has been previously approved.\n\n`+
1760			`To make these errors go away, you have two choices:\n`+
1761			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
1762			`      to the new methods, etc. shown in the above diff.\n\n`+
1763			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
1764			`         make %s-update-current-api\n\n`+
1765			`      To submit the revised current.txt to the main Android repository,\n`+
1766			`      you will need approval.\n`+
1767			`******************************\n`, ctx.ModuleName())
1768
1769		rule.Command().
1770			Text("touch").Output(d.checkCurrentApiTimestamp).
1771			Text(") || (").
1772			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1773			Text("; exit 38").
1774			Text(")")
1775
1776		rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "check current API")
1777
1778		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
1779
1780		// update API rule
1781		rule = android.NewRuleBuilder()
1782
1783		rule.Command().Text("( true")
1784
1785		rule.Command().
1786			Text("cp").Flag("-f").
1787			Input(d.apiFile).Flag(apiFile.String())
1788
1789		rule.Command().
1790			Text("cp").Flag("-f").
1791			Input(d.removedApiFile).Flag(removedApiFile.String())
1792
1793		msg = "failed to update public API"
1794
1795		rule.Command().
1796			Text("touch").Output(d.updateCurrentApiTimestamp).
1797			Text(") || (").
1798			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1799			Text("; exit 38").
1800			Text(")")
1801
1802		rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API")
1803	}
1804
1805	if String(d.properties.Check_nullability_warnings) != "" {
1806		if d.nullabilityWarningsFile == nil {
1807			ctx.PropertyErrorf("check_nullability_warnings",
1808				"Cannot specify check_nullability_warnings unless validating nullability")
1809		}
1810
1811		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
1812
1813		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "check_nullability_warnings.timestamp")
1814
1815		msg := fmt.Sprintf(`\n******************************\n`+
1816			`The warnings encountered during nullability annotation validation did\n`+
1817			`not match the checked in file of expected warnings. The diffs are shown\n`+
1818			`above. You have two options:\n`+
1819			`   1. Resolve the differences by editing the nullability annotations.\n`+
1820			`   2. Update the file of expected warnings by running:\n`+
1821			`         cp %s %s\n`+
1822			`       and submitting the updated file as part of your change.`,
1823			d.nullabilityWarningsFile, checkNullabilityWarnings)
1824
1825		rule := android.NewRuleBuilder()
1826
1827		rule.Command().
1828			Text("(").
1829			Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
1830			Text("&&").
1831			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
1832			Text(") || (").
1833			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
1834			Text("; exit 38").
1835			Text(")")
1836
1837		rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
1838	}
1839
1840	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
1841		if len(d.Javadoc.properties.Out) > 0 {
1842			ctx.PropertyErrorf("out", "out property may not be combined with jdiff")
1843		}
1844
1845		outDir := android.PathForModuleOut(ctx, "jdiff-out")
1846		srcJarDir := android.PathForModuleOut(ctx, "jdiff-srcjars")
1847		stubsDir := android.PathForModuleOut(ctx, "jdiff-stubsDir")
1848
1849		rule := android.NewRuleBuilder()
1850
1851		// Please sync with android-api-council@ before making any changes for the name of jdiffDocZip below
1852		// since there's cron job downstream that fetch this .zip file periodically.
1853		// See b/116221385 for reference.
1854		d.jdiffDocZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-docs.zip")
1855		d.jdiffStubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-stubs.srcjar")
1856
1857		jdiff := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jdiff.jar")
1858
1859		rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
1860		rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
1861
1862		srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
1863
1864		cmd := javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
1865			deps.bootClasspath, deps.classpath, d.sourcepaths)
1866
1867		cmd.Flag("-J-Xmx1600m").
1868			Flag("-XDignore.symbol.file").
1869			FlagWithArg("-doclet ", "jdiff.JDiff").
1870			FlagWithInput("-docletpath ", jdiff).
1871			Flag("-quiet")
1872
1873		if d.apiXmlFile != nil {
1874			cmd.FlagWithArg("-newapi ", strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext())).
1875				FlagWithArg("-newapidir ", filepath.Dir(d.apiXmlFile.String())).
1876				Implicit(d.apiXmlFile)
1877		}
1878
1879		if d.lastReleasedApiXmlFile != nil {
1880			cmd.FlagWithArg("-oldapi ", strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext())).
1881				FlagWithArg("-oldapidir ", filepath.Dir(d.lastReleasedApiXmlFile.String())).
1882				Implicit(d.lastReleasedApiXmlFile)
1883		}
1884
1885		rule.Command().
1886			BuiltTool(ctx, "soong_zip").
1887			Flag("-write_if_changed").
1888			Flag("-d").
1889			FlagWithOutput("-o ", d.jdiffDocZip).
1890			FlagWithArg("-C ", outDir.String()).
1891			FlagWithArg("-D ", outDir.String())
1892
1893		rule.Command().
1894			BuiltTool(ctx, "soong_zip").
1895			Flag("-write_if_changed").
1896			Flag("-jar").
1897			FlagWithOutput("-o ", d.jdiffStubsSrcJar).
1898			FlagWithArg("-C ", stubsDir.String()).
1899			FlagWithArg("-D ", stubsDir.String())
1900
1901		rule.Restat()
1902
1903		zipSyncCleanupCmd(rule, srcJarDir)
1904
1905		rule.Build(pctx, ctx, "jdiff", "jdiff")
1906	}
1907}
1908
1909//
1910// Exported Droiddoc Directory
1911//
1912var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
1913var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
1914var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
1915var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
1916
1917type ExportedDroiddocDirProperties struct {
1918	// path to the directory containing Droiddoc related files.
1919	Path *string
1920}
1921
1922type ExportedDroiddocDir struct {
1923	android.ModuleBase
1924
1925	properties ExportedDroiddocDirProperties
1926
1927	deps android.Paths
1928	dir  android.Path
1929}
1930
1931// droiddoc_exported_dir exports a directory of html templates or nullability annotations for use by doclava.
1932func ExportedDroiddocDirFactory() android.Module {
1933	module := &ExportedDroiddocDir{}
1934	module.AddProperties(&module.properties)
1935	android.InitAndroidModule(module)
1936	return module
1937}
1938
1939func (d *ExportedDroiddocDir) DepsMutator(android.BottomUpMutatorContext) {}
1940
1941func (d *ExportedDroiddocDir) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1942	path := String(d.properties.Path)
1943	d.dir = android.PathForModuleSrc(ctx, path)
1944	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
1945}
1946
1947//
1948// Defaults
1949//
1950type DocDefaults struct {
1951	android.ModuleBase
1952	android.DefaultsModuleBase
1953}
1954
1955func DocDefaultsFactory() android.Module {
1956	module := &DocDefaults{}
1957
1958	module.AddProperties(
1959		&JavadocProperties{},
1960		&DroiddocProperties{},
1961	)
1962
1963	android.InitDefaultsModule(module)
1964
1965	return module
1966}
1967
1968func StubsDefaultsFactory() android.Module {
1969	module := &DocDefaults{}
1970
1971	module.AddProperties(
1972		&JavadocProperties{},
1973		&DroidstubsProperties{},
1974	)
1975
1976	android.InitDefaultsModule(module)
1977
1978	return module
1979}
1980
1981func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
1982	srcJarDir android.ModuleOutPath, srcJars android.Paths) android.OutputPath {
1983
1984	rule.Command().Text("rm -rf").Text(srcJarDir.String())
1985	rule.Command().Text("mkdir -p").Text(srcJarDir.String())
1986	srcJarList := srcJarDir.Join(ctx, "list")
1987
1988	rule.Temporary(srcJarList)
1989
1990	rule.Command().BuiltTool(ctx, "zipsync").
1991		FlagWithArg("-d ", srcJarDir.String()).
1992		FlagWithOutput("-l ", srcJarList).
1993		FlagWithArg("-f ", `"*.java"`).
1994		Inputs(srcJars)
1995
1996	return srcJarList
1997}
1998
1999func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
2000	rule.Command().Text("rm -rf").Text(srcJarDir.String())
2001}
2002
2003var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
2004
2005type PrebuiltStubsSourcesProperties struct {
2006	Srcs []string `android:"path"`
2007}
2008
2009type PrebuiltStubsSources struct {
2010	android.ModuleBase
2011	android.DefaultableModuleBase
2012	prebuilt android.Prebuilt
2013	android.SdkBase
2014
2015	properties PrebuiltStubsSourcesProperties
2016
2017	// The source directories containing stubs source files.
2018	srcDirs     android.Paths
2019	stubsSrcJar android.ModuleOutPath
2020}
2021
2022func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
2023	switch tag {
2024	case "":
2025		return android.Paths{p.stubsSrcJar}, nil
2026	default:
2027		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
2028	}
2029}
2030
2031func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
2032	return d.stubsSrcJar
2033}
2034
2035func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
2036	p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
2037
2038	p.srcDirs = android.PathsForModuleSrc(ctx, p.properties.Srcs)
2039
2040	rule := android.NewRuleBuilder()
2041	command := rule.Command().
2042		BuiltTool(ctx, "soong_zip").
2043		Flag("-write_if_changed").
2044		Flag("-jar").
2045		FlagWithOutput("-o ", p.stubsSrcJar)
2046
2047	for _, d := range p.srcDirs {
2048		dir := d.String()
2049		command.
2050			FlagWithArg("-C ", dir).
2051			FlagWithInput("-D ", d)
2052	}
2053
2054	rule.Restat()
2055
2056	rule.Build(pctx, ctx, "zip src", "Create srcjar from prebuilt source")
2057}
2058
2059func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
2060	return &p.prebuilt
2061}
2062
2063func (p *PrebuiltStubsSources) Name() string {
2064	return p.prebuilt.Name(p.ModuleBase.Name())
2065}
2066
2067// prebuilt_stubs_sources imports a set of java source files as if they were
2068// generated by droidstubs.
2069//
2070// By default, a prebuilt_stubs_sources has a single variant that expects a
2071// set of `.java` files generated by droidstubs.
2072//
2073// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
2074// for host modules.
2075//
2076// Intended only for use by sdk snapshots.
2077func PrebuiltStubsSourcesFactory() android.Module {
2078	module := &PrebuiltStubsSources{}
2079
2080	module.AddProperties(&module.properties)
2081
2082	android.InitPrebuiltModule(module, &module.properties.Srcs)
2083	android.InitSdkAwareModule(module)
2084	InitDroiddocModule(module, android.HostAndDeviceSupported)
2085	return module
2086}
2087
2088type droidStubsSdkMemberType struct {
2089	android.SdkMemberTypeBase
2090}
2091
2092func (mt *droidStubsSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
2093	mctx.AddVariationDependencies(nil, dependencyTag, names...)
2094}
2095
2096func (mt *droidStubsSdkMemberType) IsInstance(module android.Module) bool {
2097	_, ok := module.(*Droidstubs)
2098	return ok
2099}
2100
2101func (mt *droidStubsSdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
2102	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_stubs_sources")
2103}
2104
2105func (mt *droidStubsSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
2106	return &droidStubsInfoProperties{}
2107}
2108
2109type droidStubsInfoProperties struct {
2110	android.SdkMemberPropertiesBase
2111
2112	StubsSrcJar android.Path
2113}
2114
2115func (p *droidStubsInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
2116	droidstubs := variant.(*Droidstubs)
2117	p.StubsSrcJar = droidstubs.stubsSrcJar
2118}
2119
2120func (p *droidStubsInfoProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
2121	if p.StubsSrcJar != nil {
2122		builder := ctx.SnapshotBuilder()
2123
2124		snapshotRelativeDir := filepath.Join("java", ctx.Name()+"_stubs_sources")
2125
2126		builder.UnzipToSnapshot(p.StubsSrcJar, snapshotRelativeDir)
2127
2128		propertySet.AddProperty("srcs", []string{snapshotRelativeDir})
2129	}
2130}
2131