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

View File

@ -108,11 +108,7 @@ public class TransitiveDependencies {
allModules.add("java.base"); allModules.add("java.base");
allModules.add("jdk.unsupported"); allModules.add("jdk.unsupported");
String version = Path targetFile = Paths.get(args[0]);
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");
Files.createDirectories(targetFile.getParent()); Files.createDirectories(targetFile.getParent());

View File

@ -64,7 +64,7 @@ $(eval $(call SetupJavaCompilation, COMPILE_CREATE_SYMBOLS, \
$(COMPILECREATESYMBOLS_ADD_EXPORTS), \ $(COMPILECREATESYMBOLS_ADD_EXPORTS), \
)) ))
$(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files/_the.symbols: \ $(SUPPORT_OUTPUTDIR)/symbols/ct.sym: \
$(COMPILE_CREATE_SYMBOLS) \ $(COMPILE_CREATE_SYMBOLS) \
$(wildcard $(TOPDIR)/make/data/symbols/*) \ $(wildcard $(TOPDIR)/make/data/symbols/*) \
$(MODULE_INFOS) $(MODULE_INFOS)
@ -74,34 +74,28 @@ $(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files/_the.symbols: \
$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
$(COMPILECREATESYMBOLS_ADD_EXPORTS) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
build.tools.symbolgenerator.CreateSymbols \ build.tools.symbolgenerator.TransitiveDependencies \
build-ctsym \ $(@D)/system-modules \
$(CT_DATA_DESCRIPTION) \ $(CT_MODULESOURCEPATH) \
$(@D) $(CT_MODULES)
$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
$(COMPILECREATESYMBOLS_ADD_EXPORTS) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
build.tools.symbolgenerator.TransitiveDependencies \ build.tools.symbolgenerator.CreateSymbols \
$(@D) \ build-ctsym \
$(CT_MODULESOURCEPATH) \ $(CT_DATA_DESCRIPTION) \
$(CT_MODULES) $(@D)/ct.sym \
$(SOURCE_DATE_EPOCH) \
$(JDK_SOURCE_TARGET_VERSION) \
$(@D)/system-modules
$(TOUCH) $@ $(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 # Copy ct.sym to the modules libs dir
$(eval $(call SetupCopyFiles, COPY_TO_LIBS, \ $(eval $(call SetupCopyFiles, COPY_TO_LIBS, \
FILES := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym, \ FILES := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym, \
DEST := $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.compiler, \ 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.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.lang.model.SourceVersion;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
@ -108,8 +109,10 @@ public class CanHandleClassFilesTest {
var createSymbolsClass = Class.forName("build.tools.symbolgenerator.CreateSymbols", false, cl); var createSymbolsClass = Class.forName("build.tools.symbolgenerator.CreateSymbols", false, cl);
var main = createSymbolsClass.getMethod("main", String[].class); var main = createSymbolsClass.getMethod("main", String[].class);
var symbols = targetDir.resolve("symbols"); var symbols = targetDir.resolve("symbols");
var systemModules = targetDir.resolve("system-modules");
try (Writer w = Files.newBufferedWriter(symbols)) {} try (Writer w = Files.newBufferedWriter(symbols)) {}
try (Writer w = Files.newBufferedWriter(systemModules)) {}
main.invoke(null, main.invoke(null,
(Object) new String[] {"build-description-incremental", (Object) new String[] {"build-description-incremental",
@ -120,7 +123,10 @@ public class CanHandleClassFilesTest {
(Object) new String[] {"build-ctsym", (Object) new String[] {"build-ctsym",
"does-not-exist", "does-not-exist",
symbols.toAbsolutePath().toString(), 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()});
} }
} }