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