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