xref: /aosp_15_r20/build/soong/java/droiddoc.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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/proptools"
23
24	"android/soong/android"
25	"android/soong/java/config"
26)
27
28func init() {
29	RegisterDocsBuildComponents(android.InitRegistrationContext)
30}
31
32func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
33	ctx.RegisterModuleType("doc_defaults", DocDefaultsFactory)
34
35	ctx.RegisterModuleType("droiddoc", DroiddocFactory)
36	ctx.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
37	ctx.RegisterModuleType("droiddoc_exported_dir", ExportedDroiddocDirFactory)
38	ctx.RegisterModuleType("javadoc", JavadocFactory)
39	ctx.RegisterModuleType("javadoc_host", JavadocHostFactory)
40}
41
42type JavadocProperties struct {
43	// list of source files used to compile the Java module.  May be .java, .logtags, .proto,
44	// or .aidl files.
45	Srcs []string `android:"path,arch_variant"`
46
47	// list of source files that should not be used to build the Java module.
48	// This is most useful in the arch/multilib variants to remove non-common files
49	// filegroup or genrule can be included within this property.
50	Exclude_srcs []string `android:"path,arch_variant"`
51
52	// list of package names that should actually be used. If this property is left unspecified,
53	// all the sources from the srcs property is used.
54	Filter_packages []string
55
56	// list of java libraries that will be in the classpath.
57	Libs proptools.Configurable[[]string] `android:"arch_variant"`
58
59	// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
60	Installable *bool
61
62	// if not blank, set to the version of the sdk to compile against.
63	// Defaults to compiling against the current platform.
64	Sdk_version *string `android:"arch_variant"`
65
66	// When targeting 1.9 and above, override the modules to use with --system,
67	// otherwise provides defaults libraries to add to the bootclasspath.
68	// Defaults to "none"
69	System_modules *string
70
71	Aidl struct {
72		// Top level directories to pass to aidl tool
73		Include_dirs []string
74
75		// Directories rooted at the Android.bp file to pass to aidl tool
76		Local_include_dirs []string
77	}
78
79	// If not blank, set the java version passed to javadoc as -source
80	Java_version *string
81
82	// local files that are used within user customized droiddoc options.
83	Arg_files []string `android:"path"`
84
85	// user customized droiddoc args. Deprecated, use flags instead.
86	// Available variables for substitution:
87	//
88	//  $(location <label>): the path to the arg_files with name <label>
89	//  $$: a literal $
90	Args *string
91
92	// user customized droiddoc args. Not compatible with property args.
93	// Available variables for substitution:
94	//
95	//  $(location <label>): the path to the arg_files with name <label>
96	//  $$: a literal $
97	Flags []string
98
99	// names of the output files used in args that will be generated
100	Out []string
101}
102
103type ApiToCheck struct {
104	// path to the API txt file that the new API extracted from source code is checked
105	// against. The path can be local to the module or from other module (via :module syntax).
106	Api_file *string `android:"path"`
107
108	// path to the API txt file that the new @removed API extractd from source code is
109	// checked against. The path can be local to the module or from other module (via
110	// :module syntax).
111	Removed_api_file *string `android:"path"`
112
113	// If not blank, path to the baseline txt file for approved API check violations.
114	Baseline_file *string `android:"path"`
115
116	// Arguments to the apicheck tool.
117	Args *string
118}
119
120type DroiddocProperties struct {
121	// directory relative to top of the source tree that contains doc templates files.
122	Custom_template *string
123
124	// directories under current module source which contains html/jd files.
125	Html_dirs []string
126
127	// set a value in the Clearsilver hdf namespace.
128	Hdf []string
129
130	// proofread file contains all of the text content of the javadocs concatenated into one file,
131	// suitable for spell-checking and other goodness.
132	Proofread_file *string
133
134	// a todo file lists the program elements that are missing documentation.
135	// At some point, this might be improved to show more warnings.
136	Todo_file *string `android:"path"`
137
138	// A file containing a baseline for allowed lint errors.
139	Lint_baseline *string `android:"path"`
140
141	// directory under current module source that provide additional resources (images).
142	Resourcesdir *string
143
144	// resources output directory under out/soong/.intermediates.
145	Resourcesoutdir *string
146
147	// index.html under current module will be copied to docs out dir, if not null.
148	Static_doc_index_redirect *string `android:"path"`
149
150	// source.properties under current module will be copied to docs out dir, if not null.
151	Static_doc_properties *string `android:"path"`
152
153	// a list of files under current module source dir which contains known tags in Java sources.
154	// filegroup or genrule can be included within this property.
155	Knowntags []string `android:"path"`
156
157	// if set to true, generate docs through Dokka instead of Doclava.
158	Dokka_enabled *bool
159
160	// Compat config XML. Generates compat change documentation if set.
161	Compat_config *string `android:"path"`
162}
163
164// Common flags passed down to build rule
165type droiddocBuilderFlags struct {
166	bootClasspathArgs  string
167	classpathArgs      string
168	sourcepathArgs     string
169	dokkaClasspathArgs string
170	aidlFlags          string
171	aidlDeps           android.Paths
172
173	doclavaStubsFlags string
174	doclavaDocsFlags  string
175	postDoclavaCmds   string
176}
177
178func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
179	android.InitAndroidArchModule(module, hod, android.MultilibCommon)
180	android.InitDefaultableModule(module)
181}
182
183func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool {
184	if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") {
185		if ctx.Config().BuildFromTextStub() {
186			ctx.ModuleErrorf("Generating stubs from api signature files is not available " +
187				"with WITHOUT_CHECK_API=true, as sync between the source Java files and the " +
188				"api signature files is not guaranteed.\n" +
189				"In order to utilize WITHOUT_CHECK_API, generate stubs from the source Java " +
190				"files with BUILD_FROM_SOURCE_STUB=true.\n" +
191				"However, the usage of WITHOUT_CHECK_API is not preferred as the incremental " +
192				"build is slower when generating stubs from the source Java files.\n" +
193				"Consider updating the api signature files and generating the stubs from " +
194				"them instead.")
195		}
196		return false
197	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
198		return true
199	} else if String(apiToCheck.Api_file) != "" {
200		panic("for " + apiVersionTag + " removed_api_file has to be non-empty!")
201	} else if String(apiToCheck.Removed_api_file) != "" {
202		panic("for " + apiVersionTag + " api_file has to be non-empty!")
203	}
204
205	return false
206}
207
208// Javadoc
209type Javadoc struct {
210	android.ModuleBase
211	android.DefaultableModuleBase
212
213	properties JavadocProperties
214
215	srcJars     android.Paths
216	srcFiles    android.Paths
217	sourcepaths android.Paths
218	implicits   android.Paths
219
220	docZip      android.WritablePath
221	stubsSrcJar android.WritablePath
222
223	exportableStubsSrcJar android.WritablePath
224}
225
226// javadoc converts .java source files to documentation using javadoc.
227func JavadocFactory() android.Module {
228	module := &Javadoc{}
229
230	module.AddProperties(&module.properties)
231
232	InitDroiddocModule(module, android.HostAndDeviceSupported)
233	return module
234}
235
236// javadoc_host converts .java source files to documentation using javadoc.
237func JavadocHostFactory() android.Module {
238	module := &Javadoc{}
239
240	module.AddProperties(&module.properties)
241
242	InitDroiddocModule(module, android.HostSupported)
243	return module
244}
245
246func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
247	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
248}
249
250func (j *Javadoc) SystemModules() string {
251	return proptools.String(j.properties.System_modules)
252}
253
254func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
255	return j.SdkVersion(ctx).ApiLevel
256}
257
258func (j *Javadoc) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
259	return j.SdkVersion(ctx).ApiLevel
260}
261
262func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
263	return j.SdkVersion(ctx).ApiLevel
264}
265
266func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
267	if ctx.Device() {
268		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
269		if sdkDep.useModule {
270			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
271			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
272			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
273			ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
274		}
275	}
276
277	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs.GetOrDefault(ctx, nil)...)
278}
279
280func (j *Javadoc) collectAidlFlags(ctx android.ModuleContext, deps deps) droiddocBuilderFlags {
281	var flags droiddocBuilderFlags
282
283	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
284
285	return flags
286}
287
288func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
289	aidlIncludeDirs android.Paths) (string, android.Paths) {
290
291	aidlIncludes := android.PathsForModuleSrc(ctx, j.properties.Aidl.Local_include_dirs)
292	aidlIncludes = append(aidlIncludes, android.PathsForSource(ctx, j.properties.Aidl.Include_dirs)...)
293
294	var flags []string
295	var deps android.Paths
296
297	if aidlPreprocess.Valid() {
298		flags = append(flags, "-p"+aidlPreprocess.String())
299		deps = append(deps, aidlPreprocess.Path())
300	} else {
301		flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
302	}
303
304	flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
305	flags = append(flags, "-I"+ctx.ModuleDir())
306	if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
307		flags = append(flags, "-I"+src.String())
308	}
309
310	minSdkVersion := j.MinSdkVersion(ctx).FinalOrFutureInt()
311	flags = append(flags, fmt.Sprintf("--min_sdk_version=%v", minSdkVersion))
312
313	return strings.Join(flags, " "), deps
314}
315
316// TODO: remove the duplication between this and the one in gen.go
317func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
318	flags droiddocBuilderFlags) android.Paths {
319
320	outSrcFiles := make(android.Paths, 0, len(srcFiles))
321	var aidlSrcs android.Paths
322
323	aidlIncludeFlags := genAidlIncludeFlags(ctx, srcFiles, android.Paths{})
324
325	for _, srcFile := range srcFiles {
326		switch srcFile.Ext() {
327		case ".aidl":
328			aidlSrcs = append(aidlSrcs, srcFile)
329		case ".logtags":
330			javaFile := genLogtags(ctx, srcFile)
331			outSrcFiles = append(outSrcFiles, javaFile)
332		default:
333			outSrcFiles = append(outSrcFiles, srcFile)
334		}
335	}
336
337	// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
338	if len(aidlSrcs) > 0 {
339		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, nil, flags.aidlDeps)
340		outSrcFiles = append(outSrcFiles, srcJarFiles...)
341	}
342
343	return outSrcFiles
344}
345
346func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
347	var deps deps
348
349	sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
350	if sdkDep.invalidVersion {
351		ctx.AddMissingDependencies(sdkDep.bootclasspath)
352		ctx.AddMissingDependencies(sdkDep.java9Classpath)
353	} else if sdkDep.useFiles {
354		deps.bootClasspath = append(deps.bootClasspath, sdkDep.jars...)
355		deps.aidlPreprocess = sdkDep.aidl
356	} else {
357		deps.aidlPreprocess = sdkDep.aidl
358	}
359
360	ctx.VisitDirectDeps(func(module android.Module) {
361		otherName := ctx.OtherModuleName(module)
362		tag := ctx.OtherModuleDependencyTag(module)
363
364		switch tag {
365		case bootClasspathTag:
366			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
367				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...)
368			} else if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
369				// A system modules dependency has been added to the bootclasspath
370				// so add its libs to the bootclasspath.
371				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars...)
372			} else {
373				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
374			}
375		case libTag, sdkLibTag:
376			if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
377				generatingLibsString := android.PrettyConcat(
378					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
379				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
380			} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
381				deps.classpath = append(deps.classpath, dep.HeaderJars...)
382				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
383				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
384			} else if dep, ok := module.(android.SourceFileProducer); ok {
385				checkProducesJars(ctx, dep)
386				deps.classpath = append(deps.classpath, dep.Srcs()...)
387			} else {
388				ctx.ModuleErrorf("depends on non-java module %q", otherName)
389			}
390
391		case java9LibTag:
392			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
393				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
394			} else {
395				ctx.ModuleErrorf("depends on non-java module %q", otherName)
396			}
397		case systemModulesTag:
398			if deps.systemModules != nil {
399				panic("Found two system module dependencies")
400			}
401			if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
402				deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps}
403			} else {
404				ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
405					ctx.OtherModuleName(module))
406			}
407		case aconfigDeclarationTag:
408			if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok {
409				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath)
410			} else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok {
411				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
412			} else {
413				ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+
414					"module type is allowed for flags_packages property, but %s is neither "+
415					"of these supported module types",
416					module.Name(),
417				)
418			}
419		}
420	})
421	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
422	// may contain filegroup or genrule.
423	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
424	j.implicits = append(j.implicits, srcFiles...)
425
426	// Module can depend on a java_aconfig_library module using the ":module_name{.tag}" syntax.
427	// Find the corresponding aconfig_declarations module name for such case.
428	for _, src := range j.properties.Srcs {
429		if moduleName, tag := android.SrcIsModuleWithTag(src); moduleName != "" {
430			otherModule := android.GetModuleProxyFromPathDep(ctx, moduleName, tag)
431			if otherModule != nil {
432				if dep, ok := android.OtherModuleProvider(ctx, *otherModule, android.CodegenInfoProvider); ok {
433					deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
434				}
435			}
436		}
437	}
438
439	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
440		if filterPackages == nil {
441			return srcs
442		}
443		filtered := []android.Path{}
444		for _, src := range srcs {
445			if src.Ext() != ".java" {
446				// Don't filter-out non-Java (=generated sources) by package names. This is not ideal,
447				// but otherwise metalava emits stub sources having references to the generated AIDL classes
448				// in filtered-out pacages (e.g. com.android.internal.*).
449				// TODO(b/141149570) We need to fix this by introducing default private constructors or
450				// fixing metalava to not emit constructors having references to unknown classes.
451				filtered = append(filtered, src)
452				continue
453			}
454			packageName := strings.ReplaceAll(filepath.Dir(src.Rel()), "/", ".")
455			if android.HasAnyPrefix(packageName, filterPackages) {
456				filtered = append(filtered, src)
457			}
458		}
459		return filtered
460	}
461	srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
462
463	aidlFlags := j.collectAidlFlags(ctx, deps)
464	srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
465
466	// srcs may depend on some genrule output.
467	j.srcJars = srcFiles.FilterByExt(".srcjar")
468	j.srcJars = append(j.srcJars, deps.srcJars...)
469
470	j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
471	j.srcFiles = append(j.srcFiles, deps.srcs...)
472
473	if len(j.srcFiles) > 0 {
474		j.sourcepaths = android.PathsForModuleSrc(ctx, []string{"."})
475	}
476
477	return deps
478}
479
480func (j *Javadoc) expandArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
481	var argFiles android.Paths
482	argFilesMap := map[string]string{}
483	argFileLabels := []string{}
484
485	for _, label := range j.properties.Arg_files {
486		var paths = android.PathsForModuleSrc(ctx, []string{label})
487		if _, exists := argFilesMap[label]; !exists {
488			argFilesMap[label] = strings.Join(cmd.PathsForInputs(paths), " ")
489			argFileLabels = append(argFileLabels, label)
490			argFiles = append(argFiles, paths...)
491		} else {
492			ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
493				label, argFilesMap[label], paths)
494		}
495	}
496
497	var argsPropertyName string
498	flags := make([]string, 0)
499	if j.properties.Args != nil && j.properties.Flags != nil {
500		ctx.PropertyErrorf("args", "flags is set. Cannot set args")
501	} else if args := proptools.String(j.properties.Args); args != "" {
502		flags = append(flags, args)
503		argsPropertyName = "args"
504	} else {
505		flags = append(flags, j.properties.Flags...)
506		argsPropertyName = "flags"
507	}
508
509	for _, flag := range flags {
510		expanded, err := android.Expand(flag, func(name string) (string, error) {
511			if strings.HasPrefix(name, "location ") {
512				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
513				if paths, ok := argFilesMap[label]; ok {
514					return paths, nil
515				} else {
516					return "", fmt.Errorf("unknown location label %q, expecting one of %q",
517						label, strings.Join(argFileLabels, ", "))
518				}
519			} else if name == "genDir" {
520				return android.PathForModuleGen(ctx).String(), nil
521			}
522			return "", fmt.Errorf("unknown variable '$(%s)'", name)
523		})
524
525		if err != nil {
526			ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
527		}
528		cmd.Flag(expanded)
529	}
530
531	cmd.Implicits(argFiles)
532}
533
534func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
535	j.addDeps(ctx)
536}
537
538func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
539	deps := j.collectDeps(ctx)
540
541	j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
542
543	outDir := android.PathForModuleOut(ctx, "out")
544	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
545
546	j.stubsSrcJar = nil
547
548	rule := android.NewRuleBuilder(pctx, ctx)
549
550	rule.Command().Text("rm -rf").Text(outDir.String())
551	rule.Command().Text("mkdir -p").Text(outDir.String())
552
553	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, j.srcJars)
554
555	javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
556
557	cmd := javadocSystemModulesCmd(ctx, rule, j.srcFiles, outDir, srcJarDir, srcJarList,
558		deps.systemModules, deps.classpath, j.sourcepaths)
559
560	cmd.FlagWithArg("-source ", javaVersion.String()).
561		Flag("-J-Xmx1024m").
562		Flag("-XDignore.symbol.file").
563		Flag("-Xdoclint:none")
564
565	j.expandArgs(ctx, cmd)
566
567	rule.Command().
568		BuiltTool("soong_zip").
569		Flag("-write_if_changed").
570		Flag("-d").
571		FlagWithOutput("-o ", j.docZip).
572		FlagWithArg("-C ", outDir.String()).
573		FlagWithArg("-D ", outDir.String())
574
575	rule.Restat()
576
577	zipSyncCleanupCmd(rule, srcJarDir)
578
579	rule.Build("javadoc", "javadoc")
580
581	ctx.SetOutputFiles(android.Paths{j.docZip}, ".docs.zip")
582}
583
584// Droiddoc
585type Droiddoc struct {
586	Javadoc
587
588	properties DroiddocProperties
589}
590
591// droiddoc converts .java source files to documentation using doclava or dokka.
592func DroiddocFactory() android.Module {
593	module := &Droiddoc{}
594
595	module.AddProperties(&module.properties,
596		&module.Javadoc.properties)
597
598	InitDroiddocModule(module, android.HostAndDeviceSupported)
599	return module
600}
601
602// droiddoc_host converts .java source files to documentation using doclava or dokka.
603func DroiddocHostFactory() android.Module {
604	module := &Droiddoc{}
605
606	module.AddProperties(&module.properties,
607		&module.Javadoc.properties)
608
609	InitDroiddocModule(module, android.HostSupported)
610	return module
611}
612
613func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) {
614	d.Javadoc.addDeps(ctx)
615
616	if String(d.properties.Custom_template) != "" {
617		ctx.AddDependency(ctx.Module(), droiddocTemplateTag, String(d.properties.Custom_template))
618	}
619}
620
621func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, docletPath classpath) {
622	buildNumberFile := ctx.Config().BuildNumberFile(ctx)
623	// Droiddoc always gets "-source 1.8" because it doesn't support 1.9 sources.  For modules with 1.9
624	// sources, droiddoc will get sources produced by metalava which will have already stripped out the
625	// 1.9 language features.
626	cmd.FlagWithArg("-source ", getStubsJavaVersion().String()).
627		Flag("-J-Xmx1600m").
628		Flag("-J-XX:-OmitStackTraceInFastThrow").
629		Flag("-XDignore.symbol.file").
630		Flag("--ignore-source-errors").
631		FlagWithArg("-doclet ", "com.google.doclava.Doclava").
632		FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
633		FlagWithArg("-Xmaxerrs ", "10").
634		FlagWithArg("-Xmaxwarns ", "10").
635		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
636		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED").
637		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED").
638		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED").
639		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED").
640		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED").
641		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
642		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
643		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
644
645	if String(d.properties.Custom_template) == "" {
646		// TODO: This is almost always droiddoc-templates-sdk
647		ctx.PropertyErrorf("custom_template", "must specify a template")
648	}
649
650	ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
651		if t, ok := m.(*ExportedDroiddocDir); ok {
652			cmd.FlagWithArg("-templatedir ", t.dir.String()).Implicits(t.deps)
653		} else {
654			ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_exported_dir", ctx.OtherModuleName(m))
655		}
656	})
657
658	if len(d.properties.Html_dirs) > 0 {
659		htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
660		cmd.FlagWithArg("-htmldir ", htmlDir.String()).
661			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")}))
662	}
663
664	if len(d.properties.Html_dirs) > 1 {
665		htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
666		cmd.FlagWithArg("-htmldir2 ", htmlDir2.String()).
667			Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[1], "**/*")}))
668	}
669
670	if len(d.properties.Html_dirs) > 2 {
671		ctx.PropertyErrorf("html_dirs", "Droiddoc only supports up to 2 html dirs")
672	}
673
674	knownTags := android.PathsForModuleSrc(ctx, d.properties.Knowntags)
675	cmd.FlagForEachInput("-knowntags ", knownTags)
676
677	cmd.FlagForEachArg("-hdf ", d.properties.Hdf)
678
679	if String(d.properties.Proofread_file) != "" {
680		proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
681		cmd.FlagWithOutput("-proofread ", proofreadFile)
682	}
683
684	if String(d.properties.Todo_file) != "" {
685		// tricky part:
686		// we should not compute full path for todo_file through PathForModuleOut().
687		// the non-standard doclet will get the full path relative to "-o".
688		cmd.FlagWithArg("-todo ", String(d.properties.Todo_file)).
689			ImplicitOutput(android.PathForModuleOut(ctx, String(d.properties.Todo_file)))
690	}
691
692	if String(d.properties.Lint_baseline) != "" {
693		cmd.FlagWithInput("-lintbaseline ", android.PathForModuleSrc(ctx, String(d.properties.Lint_baseline)))
694	}
695
696	if String(d.properties.Resourcesdir) != "" {
697		// TODO: should we add files under resourcesDir to the implicits? It seems that
698		// resourcesDir is one sub dir of htmlDir
699		resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
700		cmd.FlagWithArg("-resourcesdir ", resourcesDir.String())
701	}
702
703	if String(d.properties.Resourcesoutdir) != "" {
704		// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
705		cmd.FlagWithArg("-resourcesoutdir ", String(d.properties.Resourcesoutdir))
706	}
707}
708
709func (d *Droiddoc) postDoclavaCmds(ctx android.ModuleContext, rule *android.RuleBuilder) {
710	if String(d.properties.Static_doc_index_redirect) != "" {
711		staticDocIndexRedirect := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_index_redirect))
712		rule.Command().Text("cp").
713			Input(staticDocIndexRedirect).
714			Output(android.PathForModuleOut(ctx, "out", "index.html"))
715	}
716
717	if String(d.properties.Static_doc_properties) != "" {
718		staticDocProperties := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_properties))
719		rule.Command().Text("cp").
720			Input(staticDocProperties).
721			Output(android.PathForModuleOut(ctx, "out", "source.properties"))
722	}
723}
724
725func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
726	outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
727
728	cmd := rule.Command().
729		BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
730		Flag(config.JavacVmFlags).
731		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
732		FlagWithInput("@", srcJarList)
733
734	// TODO(ccross): Remove this if- statement once we finish migration for all Doclava
735	// based stubs generation.
736	// In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
737	// dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
738	// the correct package name base path.
739	if len(sourcepaths) > 0 {
740		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
741	} else {
742		cmd.FlagWithArg("-sourcepath ", srcJarDir.String())
743	}
744
745	cmd.FlagWithArg("-d ", outDir.String()).
746		Flag("-quiet")
747
748	return cmd
749}
750
751func javadocSystemModulesCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
752	outDir, srcJarDir, srcJarList android.Path, systemModules *systemModules,
753	classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
754
755	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
756
757	flag, deps := systemModules.FormJavaSystemModulesPath(ctx.Device())
758	cmd.Flag(flag).Implicits(deps)
759
760	cmd.FlagWithArg("--patch-module ", "java.base=.")
761
762	if len(classpath) > 0 {
763		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
764	}
765
766	return cmd
767}
768
769func javadocBootclasspathCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
770	outDir, srcJarDir, srcJarList android.Path, bootclasspath, classpath classpath,
771	sourcepaths android.Paths) *android.RuleBuilderCommand {
772
773	cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
774
775	if len(bootclasspath) == 0 && ctx.Device() {
776		// explicitly specify -bootclasspath "" if the bootclasspath is empty to
777		// ensure java does not fall back to the default bootclasspath.
778		cmd.FlagWithArg("-bootclasspath ", `""`)
779	} else if len(bootclasspath) > 0 {
780		cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
781	}
782
783	if len(classpath) > 0 {
784		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
785	}
786
787	return cmd
788}
789
790func dokkaCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
791	outDir, srcJarDir android.Path, bootclasspath, classpath classpath) *android.RuleBuilderCommand {
792
793	// Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
794	dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
795
796	return rule.Command().
797		BuiltTool("dokka").
798		Flag(config.JavacVmFlags).
799		Flag("-J--add-opens=java.base/java.lang=ALL-UNNAMED").
800		Flag(srcJarDir.String()).
801		FlagWithInputList("-classpath ", dokkaClasspath, ":").
802		FlagWithArg("-format ", "dac").
803		FlagWithArg("-dacRoot ", "/reference/kotlin").
804		FlagWithArg("-output ", outDir.String())
805}
806
807func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
808	deps := d.Javadoc.collectDeps(ctx)
809
810	d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
811
812	jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar")
813	doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar")
814
815	outDir := android.PathForModuleOut(ctx, "out")
816	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
817
818	rule := android.NewRuleBuilder(pctx, ctx)
819
820	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
821
822	var cmd *android.RuleBuilderCommand
823	if Bool(d.properties.Dokka_enabled) {
824		cmd = dokkaCmd(ctx, rule, outDir, srcJarDir, deps.bootClasspath, deps.classpath)
825	} else {
826		cmd = javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
827			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
828	}
829
830	d.expandArgs(ctx, cmd)
831
832	if d.properties.Compat_config != nil {
833		compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
834		cmd.FlagWithInput("-compatconfig ", compatConfig)
835	}
836
837	var desc string
838	if Bool(d.properties.Dokka_enabled) {
839		desc = "dokka"
840	} else {
841		d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
842
843		for _, o := range d.Javadoc.properties.Out {
844			cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
845		}
846
847		d.postDoclavaCmds(ctx, rule)
848		desc = "doclava"
849	}
850
851	rule.Command().
852		BuiltTool("soong_zip").
853		Flag("-write_if_changed").
854		Flag("-d").
855		FlagWithOutput("-o ", d.docZip).
856		FlagWithArg("-C ", outDir.String()).
857		FlagWithArg("-D ", outDir.String())
858
859	rule.Restat()
860
861	zipSyncCleanupCmd(rule, srcJarDir)
862
863	rule.Build("javadoc", desc)
864
865	ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, "")
866	ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, ".docs.zip")
867}
868
869// Exported Droiddoc Directory
870var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
871
872type ExportedDroiddocDirProperties struct {
873	// path to the directory containing Droiddoc related files.
874	Path *string
875}
876
877type ExportedDroiddocDir struct {
878	android.ModuleBase
879
880	properties ExportedDroiddocDirProperties
881
882	deps android.Paths
883	dir  android.Path
884}
885
886// droiddoc_exported_dir exports a directory of html templates or nullability annotations for use by doclava.
887func ExportedDroiddocDirFactory() android.Module {
888	module := &ExportedDroiddocDir{}
889	module.AddProperties(&module.properties)
890	android.InitAndroidModule(module)
891	return module
892}
893
894func (d *ExportedDroiddocDir) DepsMutator(android.BottomUpMutatorContext) {}
895
896func (d *ExportedDroiddocDir) GenerateAndroidBuildActions(ctx android.ModuleContext) {
897	path := String(d.properties.Path)
898	d.dir = android.PathForModuleSrc(ctx, path)
899	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
900}
901
902// Defaults
903type DocDefaults struct {
904	android.ModuleBase
905	android.DefaultsModuleBase
906}
907
908func DocDefaultsFactory() android.Module {
909	module := &DocDefaults{}
910
911	module.AddProperties(
912		&JavadocProperties{},
913		&DroiddocProperties{},
914	)
915
916	android.InitDefaultsModule(module)
917
918	return module
919}
920
921func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
922	srcJarDir android.ModuleOutPath, srcJars android.Paths) android.OutputPath {
923
924	cmd := rule.Command()
925	cmd.Text("rm -rf").Text(cmd.PathForOutput(srcJarDir))
926	cmd = rule.Command()
927	cmd.Text("mkdir -p").Text(cmd.PathForOutput(srcJarDir))
928	srcJarList := srcJarDir.Join(ctx, "list")
929
930	rule.Temporary(srcJarList)
931
932	cmd = rule.Command()
933	cmd.BuiltTool("zipsync").
934		FlagWithArg("-d ", cmd.PathForOutput(srcJarDir)).
935		FlagWithOutput("-l ", srcJarList).
936		FlagWithArg("-f ", `"*.java"`).
937		Inputs(srcJars)
938
939	return srcJarList
940}
941
942func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
943	rule.Command().Text("rm -rf").Text(srcJarDir.String())
944}
945