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