changeset 59324:a0a21978f3b9

8241616: Timestamps on ct.sym entries lead to non-reproducible builds Summary: Generate ct.sym in a reproducible way Reviewed-by: ihse
author jlahoda
date Wed, 29 Apr 2020 18:35:14 +0200
parents 69f6b9a8594f
children 1ba9a9a3f948
files make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java make/modules/jdk.compiler/Gendata.gmk test/langtools/tools/javac/platform/CanHandleClassFilesTest.java
diffstat 4 files changed, 125 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java	Thu May 14 15:27:17 2020 +0200
+++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java	Wed Apr 29 18:35:14 2020 +0200
@@ -33,9 +33,11 @@
                                   .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.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.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 @@
      * {@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 @@
         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 @@
                     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 @@
             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 @@
         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 @@
                 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,21 +809,43 @@
                 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);
+    }
+
+    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()));
         }
-
-        outputClassFile = outputClassFile.resolve(classDescription.name + EXTENSION);
-
-        Files.createDirectories(outputClassFile.getParent());
-
-        try (OutputStream out = Files.newOutputStream(outputClassFile)) {
-            ClassWriter w = new ClassWriter();
-
-            w.write(classFile, out);
+    }
+
+    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,
@@ -3727,23 +3775,40 @@
                 String ctDescriptionFileExtra;
                 String ctDescriptionFile;
                 String ctSymLocation;
-
-                if (args.length == 3) {
+                String timestampSpec;
+                String currentVersion;
+                String systemModules;
+
+                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;
         }
     }
--- a/make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java	Thu May 14 15:27:17 2020 +0200
+++ b/make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java	Wed Apr 29 18:35:14 2020 +0200
@@ -108,11 +108,7 @@
         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());
 
--- a/make/modules/jdk.compiler/Gendata.gmk	Thu May 14 15:27:17 2020 +0200
+++ b/make/modules/jdk.compiler/Gendata.gmk	Wed Apr 29 18:35:14 2020 +0200
@@ -64,7 +64,7 @@
         $(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 @@
 	$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
 	    $(COMPILECREATESYMBOLS_ADD_EXPORTS) \
 	    -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
+	    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.CreateSymbols \
 	    build-ctsym \
 	    $(CT_DATA_DESCRIPTION) \
-	    $(@D)
-	$(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
-	    $(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-	    -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
-	    build.tools.symbolgenerator.TransitiveDependencies \
-	    $(@D) \
-	    $(CT_MODULESOURCEPATH) \
-	    $(CT_MODULES)
+	    $(@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)
 
 ################################################################################
--- a/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java	Thu May 14 15:27:17 2020 +0200
+++ b/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java	Wed Apr 29 18:35:14 2020 +0200
@@ -40,6 +40,7 @@
 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 @@
             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 @@
                         (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()});
         }
     }