1
0
mirror of https://github.com/google/nomulus synced 2026-02-08 05:50:24 +00:00

Migrate the documentation package to Java 11 (#729)

* Migrate the documentation package to Java 11

The old Doclet API is deprected and removed in Java 12. This commit
changes the documentation package to use the new recommended API.
However it is not a drop-in replacement and there are non-idiomatic
usages all over the place. I think it is eaiser to keep the current code
logic and kind of shoehorn in the new API than starting afresh as the
return on investment of a do-over is not great.

Also note that the docs package is disabled as of this commit because we
are still using Java 8 to compile which lacks the new API. Once we
switch our toolchains to Java 11 (but still compiling Java 8 bytecode)
we can re-enable this package.

TESTED=ran `./gradlew :docs:test` locally with the documentation package
enabled.
This commit is contained in:
Lai Jiang
2020-07-30 17:12:33 -04:00
committed by GitHub
parent bf20a8ef96
commit a02b67caf5
11 changed files with 304 additions and 259 deletions

View File

@@ -17,78 +17,61 @@ package google.registry.documentation;
import static google.registry.util.BuildPathUtils.getProjectRoot;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.CharStreams;
import com.sun.javadoc.RootDoc;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javadoc.JavadocTool;
import com.sun.tools.javadoc.Messager;
import com.sun.tools.javadoc.ModifierFilter;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import java.util.EnumMap;
import java.util.Map;
import javax.tools.StandardLocation;
import jdk.javadoc.doclet.DocletEnvironment;
import jdk.javadoc.internal.tool.AccessKind;
import jdk.javadoc.internal.tool.JavadocTool;
import jdk.javadoc.internal.tool.Messager;
import jdk.javadoc.internal.tool.ToolOption;
/**
* Wrapper class to simplify calls to the javadoc system and hide internal details. An instance
* represents a set of parameters for calling out to javadoc; these parameters can be set via
* the appropriate methods, and determine what files and packages javadoc will process. The
* actual running of javadoc occurs when calling getRootDoc() to retrieve a javadoc RootDoc.
* Wrapper class to simplify calls to the javadoc system and hide internal details. An instance
* represents a set of parameters for calling out to javadoc; these parameters can be set via the
* appropriate methods, and determine what files and packages javadoc will process. The actual
* running of javadoc occurs when calling getRootDoc() to retrieve a javadoc RootDoc.
*/
public final class JavadocWrapper {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** Shows any member visible at at least the default (package) level. */
private static final long VISIBILITY_MASK =
Modifier.PUBLIC | Modifier.PROTECTED | ModifierFilter.PACKAGE;
private static final AccessKind ACCESS_KIND = AccessKind.PACKAGE;
/** Root directory for source files. If null, will use the current directory. */
private static final String SOURCE_PATH = getProjectRoot().resolve("core/src/main/java")
.toString();
/** Specific source files to generate documentation for. */
private static final ImmutableSet<String> SOURCE_FILE_NAMES = ImmutableSet.of();
/** Root directory for source files. */
public static final String SOURCE_PATH =
getProjectRoot().resolve("core/src/main/java").toString();
/** Specific packages to generate documentation for. */
private static final ImmutableSet<String> SOURCE_PACKAGE_NAMES =
ImmutableSet.of(FlowDocumentation.FLOW_PACKAGE_NAME);
/** Whether or not the Javadoc tool should eschew excessive log output. */
private static final boolean QUIET = true;
private static final ImmutableList<String> SOURCE_PACKAGE_NAMES =
ImmutableList.of(FlowDocumentation.FLOW_PACKAGE_NAME);
/**
* Obtains a Javadoc {@link RootDoc} object containing raw Javadoc documentation.
* Wraps a call to the static method createRootDoc() and passes in instance-specific settings.
* Obtains a Javadoc {@link DocletEnvironment} object containing raw Javadoc documentation. Wraps
* a call to the static method {@link #createDocletEnv} and passes in instance-specific settings.
*/
public static RootDoc getRootDoc() throws IOException {
public static DocletEnvironment getDocletEnv() throws Exception {
logger.atInfo().log("Starting Javadoc tool");
File sourceFilePath = new File(SOURCE_PATH);
logger.atInfo().log("Using source directory: %s", sourceFilePath.getAbsolutePath());
try {
return createRootDoc(
SOURCE_PATH,
SOURCE_PACKAGE_NAMES,
SOURCE_FILE_NAMES,
VISIBILITY_MASK,
QUIET);
return createDocletEnv(SOURCE_PATH, SOURCE_PACKAGE_NAMES);
} finally {
logger.atInfo().log("Javadoc tool finished");
}
}
/**
* Obtains a Javadoc root document object for the specified source path and package/Java names.
* If the source path is null, then the working directory is assumed as the source path.
* Obtains a Javadoc {@link DocletEnvironment} object for the specified source path and
* package/Java names. If the source path is null, then the working directory is assumed as the
* source path.
*
* <p>If a list of package names is provided, then Javadoc will run on these packages and all
* their subpackages, based out of the specified source path.
@@ -96,62 +79,42 @@ public final class JavadocWrapper {
* <p>If a list of file names is provided, then Javadoc will also run on these Java source files.
* The specified source path is not considered in this case.
*
* @see <a href="http://relation.to/12969.lace">Testing Java doclets</a>
* @see <a href="http://www.docjar.com/docs/api/com/sun/tools/javadoc/JavadocTool.html">JavadocTool</a>
* @param sourcePath the directory where to look for packages.
* @param packageNames name of the package to run javadoc on, including subpackages.
* @see <a
* href="https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html">
* Package jdk.javadoc.doclet</a>
*/
private static RootDoc createRootDoc(
@Nullable String sourcePath,
Collection<String> packageNames,
Collection<String> fileNames,
long visibilityMask,
boolean quiet) throws IOException {
private static DocletEnvironment createDocletEnv(
String sourcePath, Collection<String> packageNames) throws Exception {
// Create a context to hold settings for Javadoc.
Context context = new Context();
// Redirect Javadoc stdout/stderr to null writers, since otherwise the Java compiler
// issues lots of errors for classes that are imported and visible to blaze but not
// visible locally to the compiler.
// TODO(b/19124943): Find a way to ignore those errors so we can show real ones?
Messager.preRegister(
context,
JavadocWrapper.class.getName(),
new PrintWriter(CharStreams.nullWriter()), // For errors.
new PrintWriter(CharStreams.nullWriter()), // For warnings.
new PrintWriter(CharStreams.nullWriter())); // For notices.
// Pre-register a messager for the context.
Messager.preRegister(context, JavadocWrapper.class.getName());
// Set source path option for Javadoc.
try (JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8)) {
List<File> sourcePathFiles = new ArrayList<>();
if (sourcePath != null) {
for (String sourcePathEntry : Splitter.on(':').split(sourcePath)) {
sourcePathFiles.add(new File(sourcePathEntry));
}
}
fileManager.setLocation(StandardLocation.SOURCE_PATH, sourcePathFiles);
fileManager.setLocation(StandardLocation.SOURCE_PATH, ImmutableList.of(new File(sourcePath)));
// Create an instance of Javadoc.
JavadocTool javadocTool = JavadocTool.make0(context);
// Convert the package and file lists to a format Javadoc can understand.
ListBuffer<String> subPackages = new ListBuffer<>();
subPackages.addAll(packageNames);
ListBuffer<String> javaNames = new ListBuffer<>();
javaNames.addAll(fileNames);
// Set up javadoc tool options.
Map<ToolOption, Object> options = new EnumMap<>(ToolOption.class);
options.put(ToolOption.SHOW_PACKAGES, ACCESS_KIND);
options.put(ToolOption.SHOW_TYPES, ACCESS_KIND);
options.put(ToolOption.SHOW_MEMBERS, ACCESS_KIND);
options.put(ToolOption.SHOW_MODULE_CONTENTS, ACCESS_KIND);
options.put(ToolOption.SUBPACKAGES, packageNames);
// Invoke Javadoc and ask it for a RootDoc containing the specified packages.
return javadocTool.getRootDocImpl(
Locale.US.toString(), // Javadoc comment locale
UTF_8.name(), // Source character encoding
new ModifierFilter(visibilityMask), // Element visibility filter
javaNames.toList(), // Included Java file names
com.sun.tools.javac.util.List.nil(), // Doclet options
com.sun.tools.javac.util.List.nil(), // Source files
false, // Don't use BreakIterator
subPackages.toList(), // Included sub-package names
com.sun.tools.javac.util.List.nil(), // Excluded package names
false, // Read source files, not classes
false, // Don't run legacy doclet
quiet); // If asked, run Javadoc quietly
// Invoke Javadoc and ask it for a DocletEnvironment containing the specified packages.
return javadocTool.getEnvironment(
options, // options
ImmutableList.of(), // java names
ImmutableList.of()); // java files
}
}