| import org.gradle.internal.jvm.Jvm |
| |
| import javax.annotation.Nullable |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // generate javadocs by calling javadoc tool |
| // see https://docs.oracle.com/en/java/javase/11/tools/javadoc.html |
| |
| def resources = scriptResources(buildscript) |
| |
| allprojects { |
| plugins.withType(JavaPlugin) { |
| configurations { |
| missingdoclet |
| } |
| |
| dependencies { |
| missingdoclet "org.apache.lucene.tools:missing-doclet" |
| } |
| |
| ext { |
| relativeDocPath = project.path.replaceFirst(/:\w+:/, "").replace(':', '/') |
| } |
| |
| // We disable the default javadoc task and have our own |
| // javadoc rendering task below. The default javadoc task |
| // will just invoke 'renderJavadoc' (to allow people to call |
| // conventional task name). |
| tasks.matching { it.name == "javadoc" }.all { |
| enabled = false |
| dependsOn "renderJavadoc" |
| } |
| |
| task renderJavadoc(type: RenderJavadocTask) { |
| description "Generates Javadoc API documentation for each module. This directly invokes javadoc tool." |
| group "documentation" |
| |
| taskResources = resources |
| dependsOn sourceSets.main.compileClasspath |
| classpath = sourceSets.main.compileClasspath |
| srcDirSet = sourceSets.main.java |
| |
| outputDir = project.javadoc.destinationDir |
| } |
| |
| if (project.path == ':lucene:luke' || !(project in rootProject.ext.mavenProjects)) { |
| // These projects are not part of the public API so we don't render their javadocs |
| // as part of the site's creation. A side-effect of this is that javadocs would not |
| // be linted for these projects. To avoid this, we connect the regular javadoc task |
| // to check so that everything is validated. |
| project.tasks.getByName("check").dependsOn renderJavadoc |
| } else { |
| task renderSiteJavadoc(type: RenderJavadocTask) { |
| description "Generates Javadoc API documentation for the site (relative links)." |
| group "documentation" |
| |
| taskResources = resources |
| dependsOn sourceSets.main.compileClasspath |
| classpath = sourceSets.main.compileClasspath |
| srcDirSet = sourceSets.main.java |
| |
| relativeProjectLinks = true |
| |
| // Place the documentation under the documentation directory. |
| // docroot is defined in 'documentation.gradle' |
| outputDir = project.docroot.toPath().resolve(project.relativeDocPath).toFile() |
| } |
| } |
| } |
| } |
| |
| // Set up titles and link up some offline docs for all documentation |
| // (they may be unused but this doesn't do any harm). |
| |
| def javaJavadocPackages = rootProject.file("${resources}/java11/") |
| def junitJavadocPackages = rootProject.file("${resources}/junit/") |
| allprojects { |
| project.tasks.withType(RenderJavadocTask) { |
| title = "Lucene ${project.version} ${project.name} API" |
| |
| offlineLinks += [ |
| "https://docs.oracle.com/en/java/javase/11/docs/api/": javaJavadocPackages, |
| "https://junit.org/junit4/javadoc/4.12/": junitJavadocPackages |
| ] |
| |
| luceneDocUrl = provider({ rootProject.luceneDocUrl }) |
| |
| // Set up custom doclet. |
| dependsOn configurations.missingdoclet |
| docletpath = configurations.missingdoclet |
| } |
| } |
| |
| // Configure project-specific tweaks and to-dos. |
| configure(project(":lucene:analysis:common")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure([ |
| project(":lucene:analysis:kuromoji"), |
| project(":lucene:analysis:nori"), |
| project(":lucene:analysis:opennlp"), |
| project(":lucene:analysis:smartcn"), |
| project(":lucene:benchmark"), |
| project(":lucene:codecs"), |
| project(":lucene:grouping"), |
| project(":lucene:highlighter"), |
| project(":lucene:luke"), |
| project(":lucene:monitor"), |
| project(":lucene:queries"), |
| project(":lucene:queryparser"), |
| project(":lucene:replicator"), |
| project(":lucene:spatial-extras"), |
| ]) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure([ |
| project(":lucene:analysis:icu"), |
| project(":lucene:analysis:morfologik"), |
| project(":lucene:analysis:phonetic"), |
| project(":lucene:analysis:stempel"), |
| project(":lucene:classification"), |
| project(":lucene:demo"), |
| project(":lucene:expressions"), |
| project(":lucene:facet"), |
| project(":lucene:join"), |
| project(":lucene:spatial3d"), |
| project(":lucene:suggest"), |
| ]) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing @param tags |
| javadocMissingLevel = "method" |
| } |
| } |
| |
| configure(project(":lucene:backward-codecs")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing @param tags |
| javadocMissingLevel = "method" |
| } |
| } |
| |
| configure(project(":lucene:test-framework")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure(project(":lucene:sandbox")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure(project(":lucene:spatial-test-fixtures")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure(project(":lucene:misc")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| } |
| } |
| |
| configure(project(":lucene:core")) { |
| project.tasks.withType(RenderJavadocTask) { |
| // TODO: fix missing javadocs |
| javadocMissingLevel = "class" |
| // some packages are fixed already |
| javadocMissingMethod = [ |
| "org.apache.lucene.util.automaton", |
| "org.apache.lucene.analysis.standard", |
| "org.apache.lucene.analysis.tokenattributes", |
| "org.apache.lucene.document", |
| "org.apache.lucene.search.similarities", |
| "org.apache.lucene.index", |
| "org.apache.lucene.codecs", |
| "org.apache.lucene.codecs.lucene50", |
| "org.apache.lucene.codecs.lucene60", |
| "org.apache.lucene.codecs.lucene80", |
| "org.apache.lucene.codecs.lucene84", |
| "org.apache.lucene.codecs.lucene86", |
| "org.apache.lucene.codecs.lucene87", |
| "org.apache.lucene.codecs.perfield" |
| ] |
| } |
| } |
| |
| configure(project(':lucene:demo')) { |
| project.tasks.withType(RenderJavadocTask) { |
| // For the demo, we link the example source in the javadocs, as it's ref'ed elsewhere |
| linksource = true |
| } |
| } |
| |
| // Add cross-project documentation task dependencies: |
| // - each RenderJavaDocs task gets a dependency to all tasks with the same name in its dependencies |
| // - the dependency is using dependsOn with a closure to enable lazy evaluation |
| configure(subprojects) { |
| project.tasks.withType(RenderJavadocTask) { task -> |
| task.dependsOn { |
| task.project.configurations.implementation.allDependencies.withType(ProjectDependency).collect { dep -> |
| def otherProject = dep.dependencyProject |
| return otherProject.tasks.findByName(task.name) |
| } |
| } |
| } |
| } |
| |
| class RenderJavadocTask extends DefaultTask { |
| @InputFiles |
| @SkipWhenEmpty |
| SourceDirectorySet srcDirSet; |
| |
| @OutputDirectory |
| File outputDir |
| |
| @CompileClasspath |
| FileCollection classpath |
| |
| @CompileClasspath |
| FileCollection docletpath |
| |
| @Input |
| String title |
| |
| @Input |
| boolean linksource = false |
| |
| @Input |
| boolean relativeProjectLinks = false |
| |
| @Input |
| def offlineLinks = [:] |
| |
| @Input |
| @Optional |
| final Property<String> luceneDocUrl = project.objects.property(String) |
| |
| // default is to require full javadocs |
| @Input |
| String javadocMissingLevel = "parameter" |
| |
| // anything in these packages is checked with level=method. This allows iteratively fixing one package at a time. |
| @Input |
| List<String> javadocMissingMethod = [] |
| |
| // default is not to ignore any elements, should only be used to workaround split packages |
| @Input |
| List<String> javadocMissingIgnore = [] |
| |
| @Input |
| @Optional |
| ListProperty<String> extraOpts = project.objects.listProperty(String) |
| |
| @Optional |
| @Input |
| final Property<String> executable = project.objects.property(String).convention( |
| project.provider { Jvm.current().javadocExecutable.toString() }) |
| |
| @Input |
| def taskResources |
| |
| /** Utility method to recursively collect all tasks with same name like this one that we depend on */ |
| private Set findRenderTasksInDependencies() { |
| Set found = [] |
| def collectDeps |
| collectDeps = { task -> task.taskDependencies.getDependencies(task).findAll{ it.name == this.name && it.enabled && !found.contains(it) }.each{ |
| found << it |
| collectDeps(it) |
| }} |
| collectDeps(this) |
| return found |
| } |
| |
| @TaskAction |
| public void render() { |
| def srcDirs = srcDirSet.sourceDirectories.filter { dir -> dir.exists() } |
| |
| def optionsFile = project.file("${getTemporaryDir()}/javadoc-options.txt") |
| |
| // create the directory, so relative link calculation knows that it's a directory: |
| outputDir.mkdirs(); |
| |
| def opts = [] |
| |
| def overviewSourceSetDir = srcDirs.filter { dir -> project.file("${dir}/overview.html").exists() }.singleFile |
| opts << [ '-overview', project.file("${overviewSourceSetDir}/overview.html") ] |
| |
| opts << [ '-d', outputDir ] |
| opts << '-protected' |
| opts << [ '-encoding', 'UTF-8' ] |
| opts << [ '-charset', 'UTF-8' ] |
| opts << [ '-docencoding', 'UTF-8' ] |
| opts << '-noindex' |
| opts << '-author' |
| opts << '-version' |
| if (linksource) { |
| opts << '-linksource' |
| } |
| opts << '-use' |
| opts << [ '-locale', 'en_US' ] |
| opts << [ '-windowtitle', title ] |
| opts << [ '-doctitle', title ] |
| if (!classpath.isEmpty()) { |
| opts << [ '-classpath', classpath.asPath ] |
| } |
| opts << [ '-bottom', "<i>Copyright © 2000-${project.buildYear} Apache Software Foundation. All Rights Reserved.</i>" ] |
| |
| opts << [ '-tag', 'lucene.experimental:a:WARNING: This API is experimental and might change in incompatible ways in the next release.' ] |
| opts << [ '-tag', 'lucene.internal:a:NOTE: This API is for internal purposes only and might change in incompatible ways in the next release.' ] |
| opts << [ '-tag', "lucene.spi:t:SPI Name (case-insensitive: if the name is 'htmlStrip', 'htmlstrip' can be used when looking up the service)." ] |
| |
| opts << [ '-doclet', "org.apache.lucene.missingdoclet.MissingDoclet" ] |
| opts << [ '-docletpath', docletpath.asPath ] |
| opts << [ '--missing-level', javadocMissingLevel ] |
| if (javadocMissingIgnore) { |
| opts << [ '--missing-ignore', String.join(',', javadocMissingIgnore) ] |
| } |
| if (javadocMissingMethod) { |
| opts << [ '--missing-method', String.join(',', javadocMissingMethod) ] |
| } |
| |
| opts << [ '-quiet' ] |
| |
| // Add all extra options, if any. |
| opts.addAll(extraOpts.orElse([]).get()) |
| |
| def allOfflineLinks = [:] |
| allOfflineLinks.putAll(offlineLinks) |
| |
| // Resolve inter-project links: |
| // - find all (enabled) tasks this tasks depends on (with same name), calling findRenderTasksInDependencies() |
| // - sort the tasks preferring those whose project name equals 'core', then lexigraphical by path |
| // - for each task get output dir to create relative or absolute link |
| findRenderTasksInDependencies() |
| .sort(false, Comparator.comparing { (it.project.name != 'core') as Boolean }.thenComparing(Comparator.comparing { it.path })) |
| .each { otherTask -> |
| def otherProject = otherTask.project |
| // For relative links we compute the actual relative link between projects. |
| if (relativeProjectLinks) { |
| def pathTo = otherTask.outputDir.toPath().toAbsolutePath() |
| def pathFrom = outputDir.toPath().toAbsolutePath() |
| def relative = pathFrom.relativize(pathTo).toString().replace(File.separator, '/') |
| opts << ['-link', relative] |
| } else { |
| // For absolute links, we determine the target URL by assembling the full URL (if base is available). |
| def value = luceneDocUrl.getOrElse(null) |
| if (value) { |
| allOfflineLinks.put("${value}/${otherProject.relativeDocPath}/".toString(), otherTask.outputDir) |
| } |
| } |
| } |
| |
| // Add offline links. |
| allOfflineLinks.each { url, dir -> |
| // Some sanity check/ validation here to ensure dir/package-list or dir/element-list is present. |
| if (!project.file("$dir/package-list").exists() && |
| !project.file("$dir/element-list").exists()) { |
| throw new GradleException("Expected pre-rendered package-list or element-list at ${dir}.") |
| } |
| logger.info("Linking ${url} to ${dir}") |
| opts << [ '-linkoffline', url, dir ] |
| } |
| |
| opts << [ '--release', 11 ] |
| opts << '-Xdoclint:all,-missing' |
| |
| // Increase Javadoc's heap. |
| opts += [ "-J-Xmx512m" ] |
| // Force locale to be "en_US" (fix for: https://bugs.openjdk.java.net/browse/JDK-8222793) |
| opts += [ "-J-Duser.language=en", "-J-Duser.country=US" ] |
| |
| // -J options have to be passed on command line, they are not interpreted if passed via args file. |
| def jOpts = opts.findAll { opt -> opt instanceof String && opt.startsWith("-J") } |
| opts.removeAll(jOpts) |
| |
| // Collect all source files, for now excluding module descriptors. |
| opts.addAll( |
| srcDirs.collectMany { dir -> |
| project.fileTree(dir: dir, include: "**/*.java", exclude: "**/module-info.java").files |
| }.collect { it.toString() } |
| ) |
| |
| // handle doc-files manually since in explicit source file mode javadoc does not copy them. |
| srcDirs.each { File dir -> |
| project.copy { |
| into outputDir |
| |
| from(dir, { |
| include "**/doc-files/**" |
| }) |
| } |
| } |
| |
| // Temporary file that holds all javadoc options for the current task (except jOpts) |
| optionsFile.withWriter("UTF-8", { writer -> |
| // escapes an option with single quotes or whitespace to be passed in the options.txt file for |
| def escapeJavadocOption = { String s -> (s =~ /[ '"]/) ? ("'" + s.replaceAll(/[\\'"]/, /\\$0/) + "'") : s } |
| |
| opts.each { entry -> |
| if (entry instanceof List) { |
| writer.write(entry.collect { escapeJavadocOption(it as String) }.join(" ")) |
| } else { |
| writer.write(escapeJavadocOption(entry as String)) |
| } |
| writer.write('\n') |
| } |
| }) |
| |
| def javadocCmd = project.file(executable.get()) |
| logger.info("Javadoc executable used: ${javadocCmd}") |
| |
| project.quietExec { |
| executable javadocCmd |
| |
| args += [ "@${optionsFile}" ] |
| args += jOpts |
| } |
| |
| // append some special table css, prettify css |
| ant.concat(destfile: "${outputDir}/stylesheet.css", append: "true", fixlastline: "true", encoding: "UTF-8") { |
| filelist(dir: taskResources, files: |
| ["table_padding.css", |
| "custom_styles.css", |
| "prettify/prettify.css"].join(" ") |
| ) |
| } |
| |
| // append prettify to scripts |
| ant.concat(destfile: "${outputDir}/script.js", append: "true", fixlastline: "true", encoding: "UTF-8") { |
| filelist(dir: project.file("${taskResources}/prettify"), files: "prettify.js inject-javadocs.js") |
| } |
| |
| ant.fixcrlf(srcdir: outputDir, includes: "stylesheet.css script.js", eol: "lf", fixlast: "true", encoding: "UTF-8") |
| } |
| } |