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);
+ }
+ }
}