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