8272984: javadoc support for reproducible builds
Reviewed-by: hannesw
This commit is contained in:
parent
ee3be0bb56
commit
96d0df72db
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets
formats/html
HtmlConfiguration.javaHtmlDocletWriter.javaIndexRedirectWriter.javaSourceToHTMLConverter.java
markup
resources
toolkit
test/langtools/jdk/javadoc
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.javadoc.internal.doclets.formats.html;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
@ -166,6 +167,11 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
*/
|
||||
public final Set<ConditionalPage> conditionalPages;
|
||||
|
||||
/**
|
||||
* The build date, to be recorded in generated files.
|
||||
*/
|
||||
private ZonedDateTime buildDate;
|
||||
|
||||
/**
|
||||
* Constructs the full configuration needed by the doclet, including
|
||||
* the format-specific part, defined in this class, and the format-independent
|
||||
@ -215,6 +221,7 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
|
||||
conditionalPages = EnumSet.noneOf(ConditionalPage.class);
|
||||
}
|
||||
|
||||
protected void initConfiguration(DocletEnvironment docEnv,
|
||||
Function<String, String> resourceKeyMapper) {
|
||||
super.initConfiguration(docEnv, resourceKeyMapper);
|
||||
@ -223,7 +230,6 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
}
|
||||
|
||||
private final Runtime.Version docletVersion;
|
||||
public final Date startTime = new Date();
|
||||
|
||||
@Override
|
||||
public Runtime.Version getDocletVersion() {
|
||||
@ -259,6 +265,10 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
if (!options.validateOptions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ZonedDateTime zdt = options.date();
|
||||
buildDate = zdt != null ? zdt : ZonedDateTime.now();
|
||||
|
||||
if (!getSpecifiedTypeElements().isEmpty()) {
|
||||
Map<String, PackageElement> map = new HashMap<>();
|
||||
PackageElement pkg;
|
||||
@ -279,6 +289,13 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the date to be recorded in generated files}
|
||||
*/
|
||||
public ZonedDateTime getBuildDate() {
|
||||
return buildDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide the page which will appear first in the right-hand frame. It will
|
||||
* be "overview-summary.html" if "-overview" option is used or no
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -453,7 +453,7 @@ public class HtmlDocletWriter {
|
||||
throws DocFileIOException {
|
||||
List<DocPath> additionalStylesheets = configuration.getAdditionalStylesheets();
|
||||
additionalStylesheets.addAll(localStylesheets);
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.startTime)
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.getBuildDate())
|
||||
.setTimestamp(!options.noTimestamp())
|
||||
.setDescription(description)
|
||||
.setGenerator(getGenerator(getClass()))
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -74,7 +74,7 @@ public class IndexRedirectWriter extends HtmlDocletWriter {
|
||||
* @throws DocFileIOException if there is a problem generating the file
|
||||
*/
|
||||
private void generateIndexFile() throws DocFileIOException {
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.startTime)
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.getBuildDate())
|
||||
.setTimestamp(!options.noTimestamp())
|
||||
.setDescription("index redirect")
|
||||
.setGenerator(getGenerator(getClass()))
|
||||
|
4
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java
4
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -232,7 +232,7 @@ public class SourceToHTMLConverter {
|
||||
* @param path the path for the file.
|
||||
*/
|
||||
private void writeToFile(Content body, DocPath path, TypeElement te) throws DocFileIOException {
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.startTime)
|
||||
Head head = new Head(path, configuration.getDocletVersion(), configuration.getBuildDate())
|
||||
// .setTimestamp(!options.notimestamp) // temporary: compatibility!
|
||||
.setTitle(resources.getText("doclet.Window_Source_title"))
|
||||
// .setCharset(options.charset) // temporary: compatibility!
|
||||
|
@ -27,12 +27,14 @@ package jdk.javadoc.internal.doclets.formats.html.markup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
|
||||
@ -50,7 +52,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
|
||||
*/
|
||||
public class Head extends Content {
|
||||
private final Runtime.Version docletVersion;
|
||||
private final Date generatedDate;
|
||||
private final ZonedDateTime generatedDate;
|
||||
private final DocPath pathToRoot;
|
||||
private String title;
|
||||
private String charset;
|
||||
@ -78,7 +80,7 @@ public class Head extends Content {
|
||||
* @param path the path for the file that will include this HEAD element
|
||||
* @param docletVersion the doclet version
|
||||
*/
|
||||
public Head(DocPath path, Runtime.Version docletVersion, Date generatedDate) {
|
||||
public Head(DocPath path, Runtime.Version docletVersion, ZonedDateTime generatedDate) {
|
||||
this.docletVersion = docletVersion;
|
||||
this.generatedDate = generatedDate;
|
||||
pathToRoot = path.parent().invert();
|
||||
@ -279,8 +281,8 @@ public class Head extends Content {
|
||||
}
|
||||
|
||||
if (showTimestamp) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
tree.add(HtmlTree.META("dc.created", dateFormat.format(generatedDate)));
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
tree.add(HtmlTree.META("dc.created", generatedDate.format(dateFormat)));
|
||||
}
|
||||
|
||||
if (description != null) {
|
||||
@ -309,11 +311,14 @@ public class Head extends Content {
|
||||
return tree;
|
||||
}
|
||||
|
||||
private Comment getGeneratedBy(boolean timestamp, Date now) {
|
||||
|
||||
private Comment getGeneratedBy(boolean timestamp, ZonedDateTime buildDate) {
|
||||
String text = "Generated by javadoc"; // marker string, deliberately not localized
|
||||
text += " (" + docletVersion.feature() + ")";
|
||||
if (timestamp) {
|
||||
text += " on " + now;
|
||||
DateTimeFormatter fmt =
|
||||
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy").withLocale(Locale.US);
|
||||
text += " on " + buildDate.format(fmt);
|
||||
}
|
||||
return new Comment(text);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -169,6 +169,12 @@ doclet.systemPropertiesSummary=System Properties Summary
|
||||
doclet.Window_Source_title=Source code
|
||||
doclet.Window_Help_title=API Help
|
||||
|
||||
# 0: a date
|
||||
doclet.Option_date_out_of_range=value for ''--date'' out of range: {0}
|
||||
|
||||
# 0: a date
|
||||
doclet.Option_date_not_valid=value for ''--date'' not valid: {0}
|
||||
|
||||
doclet.help.main_heading=\
|
||||
JavaDoc Help
|
||||
doclet.help.navigation.head=\
|
||||
@ -420,6 +426,12 @@ doclet.usage.windowtitle.parameters=\
|
||||
doclet.usage.windowtitle.description=\
|
||||
Browser window title for the documentation
|
||||
|
||||
doclet.usage.date.parameters=\
|
||||
<date-and-time>
|
||||
doclet.usage.date.description=\
|
||||
Specifies the value to be used to timestamp the generated\n\
|
||||
pages, in ISO 8601 format
|
||||
|
||||
doclet.usage.doctitle.parameters=\
|
||||
<html-code>
|
||||
doclet.usage.doctitle.description=\
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,8 +30,16 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -82,6 +90,12 @@ public abstract class BaseOptions {
|
||||
*/
|
||||
private final LinkedHashSet<List<String>> customTagStrs = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --date}.
|
||||
* {@code null} if option not given.
|
||||
*/
|
||||
private ZonedDateTime date;
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -d}.
|
||||
* Destination directory name, in which doclet will generate the entire
|
||||
@ -338,6 +352,33 @@ public abstract class BaseOptions {
|
||||
}
|
||||
},
|
||||
|
||||
new XOption(resources, "--date", 1) {
|
||||
// Valid --date range: within ten years of now
|
||||
private static final ZonedDateTime now = ZonedDateTime.now();
|
||||
static final ZonedDateTime DATE_MIN = now.minusYears(10);
|
||||
static final ZonedDateTime DATE_MAX = now.plusYears(10);
|
||||
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
if (noTimestamp) {
|
||||
messages.error("doclet.Option_conflict", "--date", "-notimestamp");
|
||||
return false;
|
||||
}
|
||||
String arg = args.get(0);
|
||||
try {
|
||||
date = ZonedDateTime.parse(arg, DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
||||
if (date.isBefore(DATE_MIN) || date.isAfter(DATE_MAX)) {
|
||||
messages.error("doclet.Option_date_out_of_range", arg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (DateTimeParseException x) {
|
||||
messages.error("doclet.Option_date_not_valid", arg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new Option(resources, "-docencoding", 1) {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
@ -471,6 +512,10 @@ public abstract class BaseOptions {
|
||||
@Override
|
||||
public boolean process(String opt, List<String> args) {
|
||||
noTimestamp = true;
|
||||
if (date != null) {
|
||||
messages.error("doclet.Option_conflict", "--date", "-notimestamp");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
@ -739,6 +784,13 @@ public abstract class BaseOptions {
|
||||
return customTagStrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code --date}.
|
||||
*/
|
||||
public ZonedDateTime date() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for command-line option {@code -d}.
|
||||
* Destination directory name, in which doclet will generate the entire
|
||||
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8272984
|
||||
* @summary javadoc support for SOURCE_DATE_EPOCH
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build toolbox.ToolBox javadoc.tester.*
|
||||
* @run main TestDateOption
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class TestDateOption extends JavadocTester {
|
||||
|
||||
/**
|
||||
* The entry point of the test.
|
||||
*
|
||||
* @param args the array of command line arguments
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
public static void main(String... args) throws Exception {
|
||||
TestDateOption tester = new TestDateOption();
|
||||
tester.runTests(m -> new Object[] { Path.of(m.getName()) });
|
||||
}
|
||||
|
||||
ToolBox tb = new ToolBox();
|
||||
|
||||
@Test
|
||||
public void testDateOption(Path base) throws Exception {
|
||||
|
||||
ZonedDateTime zdt = ZonedDateTime.now(); // uses current date, time, timezone etc
|
||||
// adjust the calendar to some date before the default used by javadoc (i.e. today/now)
|
||||
// set a specific time, such as 10 to 3. (Rupert Brooke, Grantchester)
|
||||
ZonedDateTime testDate = zdt.minusDays(100)
|
||||
.withHour(14)
|
||||
.withMinute(50)
|
||||
.withSecond(0);
|
||||
|
||||
out.println("Test Date: '" + testDate + "'");
|
||||
|
||||
Path srcDir = base.resolve("src");
|
||||
tb.writeJavaFiles(srcDir, """
|
||||
package p;
|
||||
/** Comment. */
|
||||
public interface I { }
|
||||
""");
|
||||
Path outDir = base.resolve("out");
|
||||
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"--date", testDate.toString(),
|
||||
"p");
|
||||
checkExit(Exit.OK);
|
||||
|
||||
int featureVersion = Runtime.version().feature();
|
||||
|
||||
// The following format is as used by javadoc; it is the historical format used by Date.toString()
|
||||
DateTimeFormatter fmt =
|
||||
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy").withLocale(Locale.US);
|
||||
String generatedByStamp = testDate.format(fmt);
|
||||
String generatedBy = String.format("<!-- Generated by javadoc (%d) on %s -->",
|
||||
featureVersion, generatedByStamp);
|
||||
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
String dcCreatedStamp = testDate.format(dateFormat);
|
||||
String dcCreated = String.format("""
|
||||
<meta name="dc.created" content="%s">""",
|
||||
dcCreatedStamp);
|
||||
|
||||
// check the timestamps in all generated HTML files
|
||||
for (Path file : tb.findFiles(".html", outputDir)) {
|
||||
checkOutput(outputDir.relativize(file).toString(), true,
|
||||
generatedBy,
|
||||
dcCreated);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadDateOption(Path base) throws Exception {
|
||||
Path srcDir = base.resolve("src");
|
||||
tb.writeJavaFiles(srcDir, """
|
||||
package p;
|
||||
/** Comment. */
|
||||
public interface I { }
|
||||
""");
|
||||
Path outDir = base.resolve("out");
|
||||
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"--date", "NOT A DATE",
|
||||
"p");
|
||||
checkExit(Exit.CMDERR);
|
||||
|
||||
checkOutput(Output.OUT, true,
|
||||
"error: value for '--date' not valid: NOT A DATE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidDateOption(Path base) throws Exception {
|
||||
Path srcDir = base.resolve("src");
|
||||
tb.writeJavaFiles(srcDir, """
|
||||
package p;
|
||||
/** Comment. */
|
||||
public interface I { }
|
||||
""");
|
||||
Path outDir = base.resolve("out");
|
||||
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"--date", new Date(0).toInstant().toString(),
|
||||
"p");
|
||||
checkExit(Exit.CMDERR);
|
||||
|
||||
checkOutput(Output.OUT, true,
|
||||
"error: value for '--date' out of range: 1970-01-01T00:00:00Z");
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -98,6 +98,7 @@ public class TestXOption extends JavadocTester {
|
||||
"-Xmaxwarns ",
|
||||
"-Xdocrootparent ",
|
||||
"-Xdoclint ",
|
||||
"-Xdoclint:");
|
||||
"-Xdoclint:",
|
||||
"--date ");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -66,7 +66,7 @@ public class CheckManPageOptions {
|
||||
|
||||
static final PrintStream out = System.err;
|
||||
|
||||
List<String> MISSING_IN_MAN_PAGE = List.of();
|
||||
List<String> MISSING_IN_MAN_PAGE = List.of("--date");
|
||||
|
||||
void run(String... args) throws Exception {
|
||||
var file = args.length == 0 ? findDefaultFile() : Path.of(args[0]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user