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