8241616: Timestamps on ct.sym entries lead to non-reproducible builds

Generate ct.sym in a reproducible way

Reviewed-by: ihse
This commit is contained in:
Jan Lahoda 2020-04-29 18:35:14 +02:00
parent 31479a0d48
commit ed9cbe252d
4 changed files with 121 additions and 60 deletions

View File

@ -33,9 +33,11 @@ import build.tools.symbolgenerator.CreateSymbols
.RequiresDescription;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -53,6 +55,7 @@ import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
@ -65,11 +68,15 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
@ -209,7 +216,8 @@ public class CreateSymbols {
* {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
*/
@SuppressWarnings("unchecked")
public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation) throws IOException {
public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation,
long timestamp, String currentVersion, String systemModules) throws IOException {
LoadDescriptions data = load(ctDescriptionFileExtra != null ? Paths.get(ctDescriptionFileExtra)
: null,
Paths.get(ctDescriptionFile), null);
@ -217,12 +225,13 @@ public class CreateSymbols {
splitHeaders(data.classes);
Map<String, Map<Character, String>> package2Version2Module = new HashMap<>();
Map<String, Set<FileData>> directory2FileData = new TreeMap<>();
for (ModuleDescription md : data.modules.values()) {
for (ModuleHeaderDescription mhd : md.header) {
List<String> versionsList =
Collections.singletonList(mhd.versions);
writeModulesForVersions(ctSymLocation,
writeModulesForVersions(directory2FileData,
md,
mhd,
versionsList);
@ -260,10 +269,36 @@ public class CreateSymbols {
Set<String> currentVersions = new HashSet<>(jointVersions);
limitJointVersion(currentVersions, e.getValue().toString());
currentVersions = currentVersions.stream().filter(vers -> !disjoint(vers, e.getValue().toString())).collect(Collectors.toSet());
writeClassesForVersions(ctSymLocation, classDescription, header, e.getKey(), currentVersions);
writeClassesForVersions(directory2FileData, classDescription, header, e.getKey(), currentVersions);
}
}
}
currentVersion = Integer.toString(Integer.parseInt(currentVersion), Character.MAX_RADIX);
currentVersion = currentVersion.toUpperCase(Locale.ROOT);
openDirectory(directory2FileData, currentVersion + "/")
.add(new FileData(currentVersion + "/system-modules",
Files.readAllBytes(Paths.get(systemModules))));
try (OutputStream fos = new FileOutputStream(ctSymLocation);
OutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream jos = new ZipOutputStream(bos)) {
for (Entry<String, Set<FileData>> e : directory2FileData.entrySet()) {
jos.putNextEntry(createZipEntry(e.getKey(), timestamp));
for (FileData fd : e.getValue()) {
jos.putNextEntry(createZipEntry(fd.fileName, timestamp));
jos.write(fd.fileData);
}
}
}
}
private ZipEntry createZipEntry(String name, long timestamp) {
ZipEntry ze = new ZipEntry(name);
ze.setTime(timestamp);
return ze;
}
public static String EXTENSION = ".sig";
@ -431,7 +466,9 @@ public class CreateSymbols {
moduleList.put(desc.name, desc);
}
return new LoadDescriptions(result, moduleList, new ArrayList<>(platforms.values()));
return new LoadDescriptions(result,
moduleList,
new ArrayList<>(platforms.values()));
}
static final class LoadDescriptions {
@ -664,29 +701,29 @@ public class CreateSymbols {
return true;
}
void writeClassesForVersions(String ctSymLocation,
void writeClassesForVersions(Map<String, Set<FileData>> directory2FileData,
ClassDescription classDescription,
ClassHeaderDescription header,
String module,
Iterable<String> versions)
throws IOException {
for (String ver : versions) {
writeClass(ctSymLocation, classDescription, header, module, ver);
writeClass(directory2FileData, classDescription, header, module, ver);
}
}
void writeModulesForVersions(String ctSymLocation,
void writeModulesForVersions(Map<String, Set<FileData>> directory2FileData,
ModuleDescription moduleDescription,
ModuleHeaderDescription header,
Iterable<String> versions)
throws IOException {
for (String ver : versions) {
writeModule(ctSymLocation, moduleDescription, header, ver);
writeModule(directory2FileData, moduleDescription, header, ver);
}
}
//<editor-fold defaultstate="collapsed" desc="Class Writing">
void writeModule(String ctSymLocation,
void writeModule(Map<String, Set<FileData>> directory2FileData,
ModuleDescription moduleDescription,
ModuleHeaderDescription header,
String version) throws IOException {
@ -713,21 +750,10 @@ public class CreateSymbols {
new Method[0],
attributes);
Path outputClassFile = Paths.get(ctSymLocation,
version,
moduleDescription.name,
"module-info" + EXTENSION);
Files.createDirectories(outputClassFile.getParent());
try (OutputStream out = Files.newOutputStream(outputClassFile)) {
ClassWriter w = new ClassWriter();
w.write(classFile, out);
}
doWrite(directory2FileData, version, moduleDescription.name, "module-info" + EXTENSION, classFile);
}
void writeClass(String ctSymLocation,
void writeClass(Map<String, Set<FileData>> directory2FileData,
ClassDescription classDescription,
ClassHeaderDescription header,
String module,
@ -783,23 +809,45 @@ public class CreateSymbols {
methods.toArray(new Method[0]),
attributes);
Path outputClassFile = Paths.get(ctSymLocation, version);
if (module != null) {
outputClassFile = outputClassFile.resolve(module);
doWrite(directory2FileData, version, module, classDescription.name + EXTENSION, classFile);
}
outputClassFile = outputClassFile.resolve(classDescription.name + EXTENSION);
Files.createDirectories(outputClassFile.getParent());
try (OutputStream out = Files.newOutputStream(outputClassFile)) {
private void doWrite(Map<String, Set<FileData>> directory2FileData,
String version,
String moduleName,
String fileName,
ClassFile classFile) throws IOException {
int lastSlash = fileName.lastIndexOf('/');
String pack = lastSlash != (-1) ? fileName.substring(0, lastSlash + 1) : "/";
String directory = version + "/" + moduleName + "/" + pack;
String fullFileName = version + "/" + moduleName + "/" + fileName;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
ClassWriter w = new ClassWriter();
w.write(classFile, out);
openDirectory(directory2FileData, directory)
.add(new FileData(fullFileName, out.toByteArray()));
}
}
private Set<FileData> openDirectory(Map<String, Set<FileData>> directory2FileData,
String directory) {
Comparator<FileData> fileCompare = (fd1, fd2) -> fd1.fileName.compareTo(fd2.fileName);
return directory2FileData.computeIfAbsent(directory, d -> new TreeSet<>(fileCompare));
}
private static class FileData {
public final String fileName;
public final byte[] fileData;
public FileData(String fileName, byte[] fileData) {
this.fileName = fileName;
this.fileData = fileData;
}
}
private void addAttributes(ModuleDescription md,
ModuleHeaderDescription header,
List<CPInfo> cp,
@ -3727,23 +3775,40 @@ public class CreateSymbols {
String ctDescriptionFileExtra;
String ctDescriptionFile;
String ctSymLocation;
String timestampSpec;
String currentVersion;
String systemModules;
if (args.length == 3) {
if (args.length == 6) {
ctDescriptionFileExtra = null;
ctDescriptionFile = args[1];
ctSymLocation = args[2];
} else if (args.length == 4) {
timestampSpec = args[3];
currentVersion = args[4];
systemModules = args[5];
} else if (args.length == 7) {
ctDescriptionFileExtra = args[1];
ctDescriptionFile = args[2];
ctSymLocation = args[3];
timestampSpec = args[4];
currentVersion = args[5];
systemModules = args[6];
} else {
help();
return ;
}
long timestamp = Long.parseLong(timestampSpec);
//SOURCE_DATE_EPOCH is in seconds, convert to milliseconds:
timestamp *= 1000;
new CreateSymbols().createSymbols(ctDescriptionFileExtra,
ctDescriptionFile,
ctSymLocation);
ctSymLocation,
timestamp,
currentVersion,
systemModules);
break;
}
}

View File

@ -108,11 +108,7 @@ public class TransitiveDependencies {
allModules.add("java.base");
allModules.add("jdk.unsupported");
String version =
Integer.toString(Integer.parseInt(Source.DEFAULT.name), Character.MAX_RADIX);
version = version.toUpperCase(Locale.ROOT);
Path targetFile = Paths.get(args[0]).resolve(version).resolve("system-modules");
Path targetFile = Paths.get(args[0]);
Files.createDirectories(targetFile.getParent());

View File

@ -64,7 +64,7 @@ $(eval $(call SetupJavaCompilation, COMPILE_CREATE_SYMBOLS, \
$(COMPILECREATESYMBOLS_ADD_EXPORTS), \
))
$(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files/_the.symbols: \
$(SUPPORT_OUTPUTDIR)/symbols/ct.sym: \
$(COMPILE_CREATE_SYMBOLS) \
$(wildcard $(TOPDIR)/make/data/symbols/*) \
$(MODULE_INFOS)
@ -74,34 +74,28 @@ $(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files/_the.symbols: \
$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
$(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
build.tools.symbolgenerator.CreateSymbols \
build-ctsym \
$(CT_DATA_DESCRIPTION) \
$(@D)
build.tools.symbolgenerator.TransitiveDependencies \
$(@D)/system-modules \
$(CT_MODULESOURCEPATH) \
$(CT_MODULES)
$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
$(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
build.tools.symbolgenerator.TransitiveDependencies \
$(@D) \
$(CT_MODULESOURCEPATH) \
$(CT_MODULES)
build.tools.symbolgenerator.CreateSymbols \
build-ctsym \
$(CT_DATA_DESCRIPTION) \
$(@D)/ct.sym \
$(SOURCE_DATE_EPOCH) \
$(JDK_SOURCE_TARGET_VERSION) \
$(@D)/system-modules
$(TOUCH) $@
# Can't generate ct.sym directly into modules libs as the SetupJarArchive macro
# creates meta data files in the output dir.
$(eval $(call SetupJarArchive, CREATE_CTSYM, \
DEPENDENCIES := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files/_the.symbols, \
SRCS := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files, \
SUFFIXES := .sig system-modules, \
JAR := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym, \
))
# Copy ct.sym to the modules libs dir
$(eval $(call SetupCopyFiles, COPY_TO_LIBS, \
FILES := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym, \
DEST := $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.compiler, \
))
TARGETS += $(CREATE_CTSYM) $(COPY_TO_LIBS)
TARGETS += $(COPY_TO_LIBS)
################################################################################

View File

@ -40,6 +40,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import javax.lang.model.SourceVersion;
import javax.tools.StandardLocation;
@ -108,8 +109,10 @@ public class CanHandleClassFilesTest {
var createSymbolsClass = Class.forName("build.tools.symbolgenerator.CreateSymbols", false, cl);
var main = createSymbolsClass.getMethod("main", String[].class);
var symbols = targetDir.resolve("symbols");
var systemModules = targetDir.resolve("system-modules");
try (Writer w = Files.newBufferedWriter(symbols)) {}
try (Writer w = Files.newBufferedWriter(systemModules)) {}
main.invoke(null,
(Object) new String[] {"build-description-incremental",
@ -120,7 +123,10 @@ public class CanHandleClassFilesTest {
(Object) new String[] {"build-ctsym",
"does-not-exist",
symbols.toAbsolutePath().toString(),
targetDir.resolve("ct.sym").toAbsolutePath().toString()});
targetDir.resolve("ct.sym").toAbsolutePath().toString(),
Long.toString(System.currentTimeMillis() / 1000),
"" + SourceVersion.latest().ordinal(),
systemModules.toAbsolutePath().toString()});
}
}