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