src/share/classes/com/sun/tools/sjavac/JavacState.java

Print this page

        

*** 94,103 **** --- 94,106 ---- private Set<String> taintedModules; // The set of all packages that has been recompiled. // Copy over the javac_state for the packages that did not need recompilation, // verbatim from the previous (prev) to the new (now) build state. private Set<String> recompiledPackages; + // The set of all classpath packages and their classes, + // for which either the timestamps or the pubapi have changed. + private Map<String,Set<String>> changedClasspathPackages; // The output directories filled with tasty artifacts. private File binDir, gensrcDir, headerDir, stateDir; // The current status of the file system.
*** 125,135 **** private CompileJavaPackages compileJavaPackages = new CompileJavaPackages(); // Where to send stdout and stderr. private PrintStream out, err; ! JavacState(Options options, boolean removeJavacState, PrintStream o, PrintStream e) { out = o; err = e; numCores = options.getNumCores(); theArgs = options.getStateArgsString(); binDir = Util.pathToFile(options.getDestDir()); --- 128,141 ---- private CompileJavaPackages compileJavaPackages = new CompileJavaPackages(); // Where to send stdout and stderr. private PrintStream out, err; ! private Options options; ! ! JavacState(Options ops, boolean removeJavacState, PrintStream o, PrintStream e) { ! options = ops; out = o; err = e; numCores = options.getNumCores(); theArgs = options.getStateArgsString(); binDir = Util.pathToFile(options.getDestDir());
*** 156,165 **** --- 162,172 ---- } prev = new BuildState(); now = new BuildState(); taintedPackages = new HashSet<>(); recompiledPackages = new HashSet<>(); + changedClasspathPackages = new HashMap<>(); packagesWithChangedPublicApis = new HashSet<>(); } public BuildState prev() { return prev; } public BuildState now() { return now; }
*** 265,298 **** /** * Save the javac_state file. */ public void save() throws IOException { if (!needsSaving) return; try (FileWriter out = new FileWriter(javacState)) { StringBuilder b = new StringBuilder(); long millisNow = System.currentTimeMillis(); Date d = new Date(millisNow); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); ! b.append("# javac_state ver 0.3 generated "+millisNow+" "+df.format(d)+"\n"); b.append("# This format might change at any time. Please do not depend on it.\n"); b.append("# M module\n"); b.append("# P package\n"); b.append("# S C source_tobe_compiled timestamp\n"); b.append("# S L link_only_source timestamp\n"); b.append("# G C generated_source timestamp\n"); b.append("# A artifact timestamp\n"); b.append("# D dependency\n"); ! b.append("# I pubapi\n"); b.append("# R arguments\n"); b.append("R ").append(theArgs).append("\n"); // Copy over the javac_state for the packages that did not need recompilation. now.copyPackagesExcept(prev, recompiledPackages, new HashSet<String>()); ! // Save the packages, ie package names, dependencies, pubapis and artifacts! ! // I.e. the lot. Module.saveModules(now.modules(), b); String s = b.toString(); out.write(s, 0, s.length()); } } --- 272,326 ---- /** * Save the javac_state file. */ public void save() throws IOException { if (!needsSaving) return; + Log.debug("Saving the javac_state file."); try (FileWriter out = new FileWriter(javacState)) { StringBuilder b = new StringBuilder(); long millisNow = System.currentTimeMillis(); Date d = new Date(millisNow); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); ! b.append("# javac_state ver 0.4 generated "+millisNow+" "+df.format(d)+"\n"); b.append("# This format might change at any time. Please do not depend on it.\n"); b.append("# M module\n"); b.append("# P package\n"); b.append("# S C source_tobe_compiled timestamp\n"); b.append("# S L link_only_source timestamp\n"); b.append("# G C generated_source timestamp\n"); b.append("# A artifact timestamp\n"); b.append("# D dependency\n"); ! b.append("# I C pubapi when compiled from source\n"); ! // The pubapi of compiled source is extracted almost for free when ! // the compilation is done. ! // ! // Should we have pubapi when linked from source? ! // No, because a linked source might not be entirely compiled because of ! // performance reasons, thus the full pubapi might not be available for free. ! // Instead, monitor the timestamp of linked sources, when the timestamp change ! // always force a recompile of dependents even though it might not be necessary. ! b.append("# I Z pubapi when linked as classes\n"); ! // The pubapi of linked classes can easily be constructed from the referenced classes. ! // However this pubapi contains only a subset of the classes actually public in the package. ! // Because: 1) we cannot easily find all classes 2) we do not want to, we are satisfied in ! // only tracking the actually referred classes. ! b.append("# Z archive timestamp\n"); ! // When referred classes are stored in a jar/zip, use this timestamp to shortcut ! // and avoid testing all internal classes in the jar, if the timestamp of the jar itself ! // is unchanged. b.append("# R arguments\n"); b.append("R ").append(theArgs).append("\n"); // Copy over the javac_state for the packages that did not need recompilation. now.copyPackagesExcept(prev, recompiledPackages, new HashSet<String>()); ! // Recreate pubapi:s and timestamps for classpath packages that have changed. ! addToClasspathPubapis(changedClasspathPackages); ! // Save the packages, ie package names, dependencies, pubapis and artifacts! I.e. the lot. Module.saveModules(now.modules(), b); + // Save the archive timestamps. + now.saveArchiveTimestamps(b); String s = b.toString(); out.write(s, 0, s.length()); } }
*** 329,338 **** --- 357,369 ---- } else if (c == 'I') { if (lastModule == null || lastPackage == null) { syntaxError = true; break; } lastPackage.loadPubapi(l); } else + if (c == 'Z') { + db.prev.loadArchiveTimestamp(l); + } else if (c == 'A') { if (lastModule == null || lastPackage == null) { syntaxError = true; break; } lastPackage.loadArtifact(l); } else if (c == 'S') {
*** 352,362 **** if (c == '#') { if (l.startsWith("# javac_state ver ")) { int sp = l.indexOf(" ", 18); if (sp != -1) { String ver = l.substring(18,sp); ! if (!ver.equals("0.3")) { break; } foundCorrectVerNr = true; } } --- 383,393 ---- if (c == '#') { if (l.startsWith("# javac_state ver ")) { int sp = l.indexOf(" ", 18); if (sp != -1) { String ver = l.substring(18,sp); ! if (!ver.equals("0.4")) { break; } foundCorrectVerNr = true; } }
*** 406,416 **** } } } /** ! * This packages need recompilation. */ public Set<String> taintedPackages() { return taintedPackages; } --- 437,447 ---- } } } /** ! * These packages need recompilation. */ public Set<String> taintedPackages() { return taintedPackages; }
*** 495,504 **** --- 526,630 ---- } } } /** + * Compare the javac_state recorded public apis of packages on the classpath + * with the actual public apis on the classpath. + */ + public void taintPackagesDependingOnChangedClasspathPackages() { + Compile comp = new Compile(options); + Set<String> tainteds = new HashSet<String>(); + + for (Package pkg : prev.packages().values()) { + List<String> current = new ArrayList<String>(); + Iterator<String> i = current.iterator(); + boolean tainted = false; + boolean skip = false; + for (String s : pkg.pubapiForLinkedClasses()) { + if (skip && !s.startsWith("PUBAPI ")) { + // Skipping, because timestamp or hash checked out ok. Assume no change here! + continue; + } + skip = false; + // Check if we have found a new class. + if (s.startsWith("PUBAPI ")) { + if (i.hasNext()) { + // Previous api had not ended! Change is detected! + tainted = true; + break; + } + // Extract the class name, hash, file and timestamp from the PUBAPI line + int p = s.indexOf(' ', 7); + int pp = s.indexOf(' ', p+1); + String cln = s.substring(7, p); + String hash = s.substring(p+1, pp); + String loc = s.substring(pp+1); // loc == file and timestamp + String archive = Util.extractArchive(loc); + if (archive != null && prev.archives().contains(archive)) { + // If it existed, then the timestamp for the archive + // is unchanged. Lets skip testing this class inside the archive! + Log.debug("Assume "+cln+" unchanged since "+archive+" is unchanged"); + skip = true; + current = new ArrayList<String>(); + i = current.iterator(); + continue; + } + // The archive timestamp has changed, or is new. + // Compare the prev classLocInfo with the current classLocInfo + String cmp = comp.getClassLocInfo(cln); + if (cmp.equals(loc)) { + // Equal means that the come from the same class/zip file + // and the timestamp is the same. Assume equal! + Log.debug("Assume "+cln+" unchanged since "+loc+" is unchanged"); + skip = true; + current = new ArrayList<String>(); + i = current.iterator(); + continue; + } + // The timestamps differ, lets check the pubapi. + Log.debug("Timestamp changed for "+cln+" now checking if pubapi is the same."); + // Add the package to changedClasspathPackages because this + // will trigger a regeneration the package information to javac_state + // thus updating the timestamps. + Util.addToMapSet(pkg.name(), cln, changedClasspathPackages); + needsSaving = true; + current = comp.getPubapi(cln, now.archives()); + i = current.iterator(); + } + if (i.hasNext()) { + String ss = i.next(); + if (s.startsWith("PUBAPI ") && ss.startsWith("PUBAPI ")) { + int p = s.indexOf(' ', 7); + int pp = s.indexOf(' ', p+1); + s = s.substring(0, pp); + ss = ss.substring(0, pp); + if (s.equals(ss)) { + // The pubapi of a class has identical hash! + // We assume it is equals! + Log.debug("Assume "+s.substring(0, pp)+" unchanged since its hash is unchanged"); + skip = true; + current = new ArrayList<String>(); + i = current.iterator(); + } else { + // The pubapi hash is not identical! Change is detected! + tainted = true; + } + } + } + } + if (tainted) { + Log.info("The pubapi of "+Util.justPackageName(pkg.name())+" has changed!"); + tainteds.add(pkg.name()); + } else if (pkg.pubapiForLinkedClasses().size() > 0) { + Log.debug("The pubapi of "+Util.justPackageName(pkg.name())+" was unchanged!"); + } + } + taintPackagesDependingOnChangedPackages(tainteds, new HashSet<String>()); + } + + /** * Scan all output dirs for artifacts and remove those files (artifacts?) * that are not recognized as such, in the javac_state file. */ public void removeUnidentifiedArtifacts() { Set<File> allKnownArtifacts = new HashSet<>();
*** 707,728 **** // These maps need to be synchronized since multiple threads will be writing results into them. Map<String,Set<URI>> packageArtifacts = Collections.synchronizedMap(new HashMap<String,Set<URI>>()); Map<String,Set<String>> packageDependencies = Collections.synchronizedMap(new HashMap<String,Set<String>>()); ! Map<String,String> packagePublicApis = ! Collections.synchronizedMap(new HashMap<String, String>()); boolean r = t.transform(javacService, srcs, visibleSrcs, visibleClasses, prev.dependents(), outputDir.toURI(), packageArtifacts, packageDependencies, packagePublicApis, 0, isIncremental(), numCores, out, err); --- 833,859 ---- // These maps need to be synchronized since multiple threads will be writing results into them. Map<String,Set<URI>> packageArtifacts = Collections.synchronizedMap(new HashMap<String,Set<URI>>()); Map<String,Set<String>> packageDependencies = Collections.synchronizedMap(new HashMap<String,Set<String>>()); ! Map<String,List<String>> packagePublicApis = ! Collections.synchronizedMap(new HashMap<String, List<String>>()); ! // Map from package name to set of classes. The classes are a subset of all classes ! // within the package. The subset are those that our code has directly referenced. ! Map<String,Set<String>> classpathPackageDependencies = ! Collections.synchronizedMap(new HashMap<String, Set<String>>()); boolean r = t.transform(javacService, srcs, visibleSrcs, visibleClasses, prev.dependents(), outputDir.toURI(), packageArtifacts, packageDependencies, packagePublicApis, + classpathPackageDependencies, 0, isIncremental(), numCores, out, err);
*** 740,756 **** for (Map.Entry<String,Set<String>> a : packageDependencies.entrySet()) { Set<String> deps = a.getValue(); Module mnow = now.findModuleFromPackageName(a.getKey()); mnow.setDependencies(a.getKey(), deps); } // Extract all the pubapis and store the info into the Package objects. ! for (Map.Entry<String,String> a : packagePublicApis.entrySet()) { Module mprev = prev.findModuleFromPackageName(a.getKey()); ! List<String> pubapi = Package.pubapiToList(a.getValue()); Module mnow = now.findModuleFromPackageName(a.getKey()); ! mnow.setPubapi(a.getKey(), pubapi); ! if (mprev.hasPubapiChanged(a.getKey(), pubapi)) { // Aha! The pubapi of this package has changed! // It can also be a new compile from scratch. if (mprev.lookupPackage(a.getKey()).existsInJavacState()) { // This is an incremental compile! The pubapi // did change. Trigger recompilation of dependents. --- 871,905 ---- for (Map.Entry<String,Set<String>> a : packageDependencies.entrySet()) { Set<String> deps = a.getValue(); Module mnow = now.findModuleFromPackageName(a.getKey()); mnow.setDependencies(a.getKey(), deps); } + // With two threads compiling our sources, sources compiled by a second thread, might look like + // classpath dependencies to the first thread or vice versa. We cannot remove such fake classpath dependencies + // until the end of the compilation since the knowledge of what is compiled does not exist until now. + for (String pkg : packagePublicApis.keySet()) { + classpathPackageDependencies.remove(pkg); + } + // Also, if we doing an incremental compile, then references outside of the small recompiled set, + // will also look like classpath deps, lets remove them as well. + for (String pkg : prev.packages().keySet()) { + Package p = prev.packages().get(pkg); + if (p.pubapiForLinkedClasses().size() == 0) { + classpathPackageDependencies.remove(pkg); + } + } + // Extract all classpath package classes and store the public ap + // into the Package object. + addToClasspathPubapis(classpathPackageDependencies); + // Extract all the pubapis and store the info into the Package objects. ! for (Map.Entry<String,List<String>> a : packagePublicApis.entrySet()) { Module mprev = prev.findModuleFromPackageName(a.getKey()); ! List<String> pubapi = a.getValue(); Module mnow = now.findModuleFromPackageName(a.getKey()); ! mnow.setPubapiForCompiledSources(a.getKey(), pubapi); ! if (mprev.hasPubapiForCompiledSourcesChanged(a.getKey(), pubapi)) { // Aha! The pubapi of this package has changed! // It can also be a new compile from scratch. if (mprev.lookupPackage(a.getKey()).existsInJavacState()) { // This is an incremental compile! The pubapi // did change. Trigger recompilation of dependents.
*** 799,809 **** boolean mightNeedRewriting = File.pathSeparatorChar == ';'; if (makefileSourceList == null) return; Set<String> calculatedSources = new HashSet<>(); ! Set<String> listedSources = new HashSet<>(); // Create a set of filenames with full paths. for (Source s : now.sources().values()) { // Don't include link only sources when comparing sources to compile if (!s.isLinkedOnly()) { --- 948,958 ---- boolean mightNeedRewriting = File.pathSeparatorChar == ';'; if (makefileSourceList == null) return; Set<String> calculatedSources = new HashSet<>(); ! Set<String> listedSources = SourceLocation.loadList(makefileSourceList); // Create a set of filenames with full paths. for (Source s : now.sources().values()) { // Don't include link only sources when comparing sources to compile if (!s.isLinkedOnly()) {
*** 811,851 **** if (mightNeedRewriting) path = Util.normalizeDriveLetter(path); calculatedSources.add(path); } } ! // Read in the file and create another set of filenames with full paths. ! try { ! BufferedReader in = new BufferedReader(new FileReader(makefileSourceList)); ! for (;;) { ! String l = in.readLine(); ! if (l==null) break; ! l = l.trim(); ! if (mightNeedRewriting) { ! if (l.indexOf(":") == 1 && l.indexOf("\\") == 2) { ! // Everything a-ok, the format is already C:\foo\bar ! } else if (l.indexOf(":") == 1 && l.indexOf("/") == 2) { ! // The format is C:/foo/bar, rewrite into the above format. ! l = l.replaceAll("/","\\\\"); ! } else if (l.charAt(0) == '/' && l.indexOf("/",1) != -1) { ! // The format might be: /cygdrive/c/foo/bar, rewrite into the above format. ! // Do not hardcode the name cygdrive here. ! int slash = l.indexOf("/",1); ! l = l.replaceAll("/","\\\\"); ! l = ""+l.charAt(slash+1)+":"+l.substring(slash+2); ! } ! if (Character.isLowerCase(l.charAt(0))) { ! l = Character.toUpperCase(l.charAt(0))+l.substring(1); ! } ! } ! listedSources.add(l); ! } ! } catch (FileNotFoundException e) { ! throw new ProblemException("Could not open "+makefileSourceList.getPath()+" since it does not exist!"); ! } catch (IOException e) { ! throw new ProblemException("Could not read "+makefileSourceList.getPath()); ! } for (String s : listedSources) { if (!calculatedSources.contains(s)) { throw new ProblemException("The makefile listed source "+s+" was not calculated by the smart javac wrapper!"); } --- 960,970 ---- if (mightNeedRewriting) path = Util.normalizeDriveLetter(path); calculatedSources.add(path); } } ! for (String s : listedSources) { if (!calculatedSources.contains(s)) { throw new ProblemException("The makefile listed source "+s+" was not calculated by the smart javac wrapper!"); }
*** 855,860 **** --- 974,1007 ---- if (!listedSources.contains(s)) { throw new ProblemException("The smart javac wrapper calculated source "+s+" was not listed by the makefiles!"); } } } + + /** + * Add the classes in deps, to the pubapis of the Packages. + * The pubapis are stored within the corresponding Package in now. + */ + public void addToClasspathPubapis(Map<String, Set<String>> deps) { + Compile comp = new Compile(options); + // Extract all the pubapis of the classes inside deps and + // store the info into the corresponding Package objects. + for (Map.Entry<String,Set<String>> a : deps.entrySet()) { + String pkg = a.getKey(); + Module mnow = now.findModuleFromPackageName(pkg); + Set<String> classes = new HashSet<>(); + classes.addAll(a.getValue()); + classes.addAll(mnow.lookupPackage(pkg).getClassesFromClasspathPubapi()); + List<String> sorted_classes = new ArrayList<>(); + for (String key : classes) { + sorted_classes.add(key); + } + Collections.sort(sorted_classes); + + List<String> pubapis = new ArrayList<>(); + for (String s : sorted_classes) { + pubapis.addAll(comp.getPubapi(s, now.archives())); + } + mnow.setPubapiForLinkedClasses(a.getKey(), pubapis); + } + } }