1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/sjavac/JavacState.java Wed Apr 27 01:34:52 2016 +0800 1.3 @@ -0,0 +1,860 @@ 1.4 +/* 1.5 + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.tools.sjavac; 1.30 + 1.31 +import java.io.*; 1.32 +import java.util.Collections; 1.33 +import java.util.Date; 1.34 +import java.util.Set; 1.35 +import java.util.HashSet; 1.36 +import java.util.List; 1.37 +import java.util.Map; 1.38 +import java.util.HashMap; 1.39 +import java.text.SimpleDateFormat; 1.40 +import java.net.URI; 1.41 +import java.util.*; 1.42 + 1.43 +/** 1.44 + * The javac state class maintains the previous (prev) and the current (now) 1.45 + * build states and everything else that goes into the javac_state file. 1.46 + * 1.47 + * <p><b>This is NOT part of any supported API. 1.48 + * If you write code that depends on this, you do so at your own 1.49 + * risk. This code and its internal interfaces are subject to change 1.50 + * or deletion without notice.</b></p> 1.51 + */ 1.52 +public class JavacState 1.53 +{ 1.54 + // The arguments to the compile. If not identical, then it cannot 1.55 + // be an incremental build! 1.56 + String theArgs; 1.57 + // The number of cores limits how many threads are used for heavy concurrent work. 1.58 + int numCores; 1.59 + 1.60 + // The bin_dir/javac_state 1.61 + private String javacStateFilename; 1.62 + private File javacState; 1.63 + 1.64 + // The previous build state is loaded from javac_state 1.65 + private BuildState prev; 1.66 + // The current build state is constructed during the build, 1.67 + // then saved as the new javac_state. 1.68 + private BuildState now; 1.69 + 1.70 + // Something has changed in the javac_state. It needs to be saved! 1.71 + private boolean needsSaving; 1.72 + // If this is a new javac_state file, then do not print unnecessary messages. 1.73 + private boolean newJavacState; 1.74 + 1.75 + // These are packages where something has changed and the package 1.76 + // needs to be recompiled. Actions that trigger recompilation: 1.77 + // * source belonging to the package has changed 1.78 + // * artifact belonging to the package is lost, or its timestamp has been changed. 1.79 + // * an unknown artifact has appeared, we simply delete it, but we also trigger a recompilation. 1.80 + // * a package that is tainted, taints all packages that depend on it. 1.81 + private Set<String> taintedPackages; 1.82 + // After a compile, the pubapis are compared with the pubapis stored in the javac state file. 1.83 + // Any packages where the pubapi differ are added to this set. 1.84 + // Later we use this set and the dependency information to taint dependent packages. 1.85 + private Set<String> packagesWithChangedPublicApis; 1.86 + // When a module-info.java file is changed, taint the module, 1.87 + // then taint all modules that depend on that that module. 1.88 + // A module dependency can occur directly through a require, or 1.89 + // indirectly through a module that does a public export for the first tainted module. 1.90 + // When all modules are tainted, then taint all packages belonging to these modules. 1.91 + // Then rebuild. It is perhaps possible (and valuable?) to do a more finegrained examination of the 1.92 + // change in module-info.java, but that will have to wait. 1.93 + private Set<String> taintedModules; 1.94 + // The set of all packages that has been recompiled. 1.95 + // Copy over the javac_state for the packages that did not need recompilation, 1.96 + // verbatim from the previous (prev) to the new (now) build state. 1.97 + private Set<String> recompiledPackages; 1.98 + 1.99 + // The output directories filled with tasty artifacts. 1.100 + private File binDir, gensrcDir, headerDir; 1.101 + 1.102 + // The current status of the file system. 1.103 + private Set<File> binArtifacts; 1.104 + private Set<File> gensrcArtifacts; 1.105 + private Set<File> headerArtifacts; 1.106 + 1.107 + // The status of the sources. 1.108 + Set<Source> removedSources = null; 1.109 + Set<Source> addedSources = null; 1.110 + Set<Source> modifiedSources = null; 1.111 + 1.112 + // Visible sources for linking. These are the only 1.113 + // ones that -sourcepath is allowed to see. 1.114 + Set<URI> visibleSrcs; 1.115 + 1.116 + // Visible classes for linking. These are the only 1.117 + // ones that -classpath is allowed to see. 1.118 + // It maps from a classpath root to the set of visible classes for that root. 1.119 + // If the set is empty, then all classes are visible for that root. 1.120 + // It can also map from a jar file to the set of visible classes for that jar file. 1.121 + Map<URI,Set<String>> visibleClasses; 1.122 + 1.123 + // Setup two transforms that always exist. 1.124 + private CopyFile copyFiles = new CopyFile(); 1.125 + private CompileJavaPackages compileJavaPackages = new CompileJavaPackages(); 1.126 + 1.127 + // Where to send stdout and stderr. 1.128 + private PrintStream out, err; 1.129 + 1.130 + JavacState(String[] args, File bd, File gd, File hd, boolean permitUnidentifiedArtifacts, boolean removeJavacState, 1.131 + PrintStream o, PrintStream e) { 1.132 + out = o; 1.133 + err = e; 1.134 + numCores = Main.findNumberOption(args, "-j"); 1.135 + theArgs = ""; 1.136 + for (String a : removeArgsNotAffectingState(args)) { 1.137 + theArgs = theArgs+a+" "; 1.138 + } 1.139 + binDir = bd; 1.140 + gensrcDir = gd; 1.141 + headerDir = hd; 1.142 + javacStateFilename = binDir.getPath()+File.separator+"javac_state"; 1.143 + javacState = new File(javacStateFilename); 1.144 + if (removeJavacState && javacState.exists()) { 1.145 + javacState.delete(); 1.146 + } 1.147 + newJavacState = false; 1.148 + if (!javacState.exists()) { 1.149 + newJavacState = true; 1.150 + // If there is no javac_state then delete the contents of all the artifact dirs! 1.151 + // We do not want to risk building a broken incremental build. 1.152 + // BUT since the makefiles still copy things straight into the bin_dir et al, 1.153 + // we avoid deleting files here, if the option --permit-unidentified-classes was supplied. 1.154 + if (!permitUnidentifiedArtifacts) { 1.155 + deleteContents(binDir); 1.156 + deleteContents(gensrcDir); 1.157 + deleteContents(headerDir); 1.158 + } 1.159 + needsSaving = true; 1.160 + } 1.161 + prev = new BuildState(); 1.162 + now = new BuildState(); 1.163 + taintedPackages = new HashSet<String>(); 1.164 + recompiledPackages = new HashSet<String>(); 1.165 + packagesWithChangedPublicApis = new HashSet<String>(); 1.166 + } 1.167 + 1.168 + public BuildState prev() { return prev; } 1.169 + public BuildState now() { return now; } 1.170 + 1.171 + /** 1.172 + * Remove args not affecting the state. 1.173 + */ 1.174 + static String[] removeArgsNotAffectingState(String[] args) { 1.175 + String[] out = new String[args.length]; 1.176 + int j = 0; 1.177 + for (int i = 0; i<args.length; ++i) { 1.178 + if (args[i].equals("-j")) { 1.179 + // Just skip it and skip following value 1.180 + i++; 1.181 + } else if (args[i].startsWith("--server:")) { 1.182 + // Just skip it. 1.183 + } else if (args[i].startsWith("--log=")) { 1.184 + // Just skip it. 1.185 + } else if (args[i].equals("--compare-found-sources")) { 1.186 + // Just skip it and skip verify file name 1.187 + i++; 1.188 + } else { 1.189 + // Copy argument. 1.190 + out[j] = args[i]; 1.191 + j++; 1.192 + } 1.193 + } 1.194 + String[] ret = new String[j]; 1.195 + System.arraycopy(out, 0, ret, 0, j); 1.196 + return ret; 1.197 + } 1.198 + 1.199 + /** 1.200 + * Specify which sources are visible to the compiler through -sourcepath. 1.201 + */ 1.202 + public void setVisibleSources(Map<String,Source> vs) { 1.203 + visibleSrcs = new HashSet<URI>(); 1.204 + for (String s : vs.keySet()) { 1.205 + Source src = vs.get(s); 1.206 + visibleSrcs.add(src.file().toURI()); 1.207 + } 1.208 + } 1.209 + 1.210 + /** 1.211 + * Specify which classes are visible to the compiler through -classpath. 1.212 + */ 1.213 + public void setVisibleClasses(Map<String,Source> vs) { 1.214 + visibleSrcs = new HashSet<URI>(); 1.215 + for (String s : vs.keySet()) { 1.216 + Source src = vs.get(s); 1.217 + visibleSrcs.add(src.file().toURI()); 1.218 + } 1.219 + } 1.220 + /** 1.221 + * Returns true if this is an incremental build. 1.222 + */ 1.223 + public boolean isIncremental() { 1.224 + return !prev.sources().isEmpty(); 1.225 + } 1.226 + 1.227 + /** 1.228 + * Find all artifacts that exists on disk. 1.229 + */ 1.230 + public void findAllArtifacts() { 1.231 + binArtifacts = findAllFiles(binDir); 1.232 + gensrcArtifacts = findAllFiles(gensrcDir); 1.233 + headerArtifacts = findAllFiles(headerDir); 1.234 + } 1.235 + 1.236 + /** 1.237 + * Lookup the artifacts generated for this package in the previous build. 1.238 + */ 1.239 + private Map<String,File> fetchPrevArtifacts(String pkg) { 1.240 + Package p = prev.packages().get(pkg); 1.241 + if (p != null) { 1.242 + return p.artifacts(); 1.243 + } 1.244 + return new HashMap<String,File>(); 1.245 + } 1.246 + 1.247 + /** 1.248 + * Delete all prev artifacts in the currently tainted packages. 1.249 + */ 1.250 + public void deleteClassArtifactsInTaintedPackages() { 1.251 + for (String pkg : taintedPackages) { 1.252 + Map<String,File> arts = fetchPrevArtifacts(pkg); 1.253 + for (File f : arts.values()) { 1.254 + if (f.exists() && f.getName().endsWith(".class")) { 1.255 + f.delete(); 1.256 + } 1.257 + } 1.258 + } 1.259 + } 1.260 + 1.261 + /** 1.262 + * Mark the javac_state file to be in need of saving and as a side effect, 1.263 + * it gets a new timestamp. 1.264 + */ 1.265 + private void needsSaving() { 1.266 + needsSaving = true; 1.267 + } 1.268 + 1.269 + /** 1.270 + * Save the javac_state file. 1.271 + */ 1.272 + public void save() throws IOException { 1.273 + if (!needsSaving) return; 1.274 + try (FileWriter out = new FileWriter(javacStateFilename)) { 1.275 + StringBuilder b = new StringBuilder(); 1.276 + long millisNow = System.currentTimeMillis(); 1.277 + Date d = new Date(millisNow); 1.278 + SimpleDateFormat df = 1.279 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); 1.280 + b.append("# javac_state ver 0.3 generated "+millisNow+" "+df.format(d)+"\n"); 1.281 + b.append("# This format might change at any time. Please do not depend on it.\n"); 1.282 + b.append("# M module\n"); 1.283 + b.append("# P package\n"); 1.284 + b.append("# S C source_tobe_compiled timestamp\n"); 1.285 + b.append("# S L link_only_source timestamp\n"); 1.286 + b.append("# G C generated_source timestamp\n"); 1.287 + b.append("# A artifact timestamp\n"); 1.288 + b.append("# D dependency\n"); 1.289 + b.append("# I pubapi\n"); 1.290 + b.append("# R arguments\n"); 1.291 + b.append("R ").append(theArgs).append("\n"); 1.292 + 1.293 + // Copy over the javac_state for the packages that did not need recompilation. 1.294 + now.copyPackagesExcept(prev, recompiledPackages, new HashSet<String>()); 1.295 + // Save the packages, ie package names, dependencies, pubapis and artifacts! 1.296 + // I.e. the lot. 1.297 + Module.saveModules(now.modules(), b); 1.298 + 1.299 + String s = b.toString(); 1.300 + out.write(s, 0, s.length()); 1.301 + } 1.302 + } 1.303 + 1.304 + /** 1.305 + * Load a javac_state file. 1.306 + */ 1.307 + public static JavacState load(String[] args, File binDir, File gensrcDir, File headerDir, 1.308 + boolean permitUnidentifiedArtifacts, PrintStream out, PrintStream err) { 1.309 + JavacState db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, false, out, err); 1.310 + Module lastModule = null; 1.311 + Package lastPackage = null; 1.312 + Source lastSource = null; 1.313 + boolean noFileFound = false; 1.314 + boolean foundCorrectVerNr = false; 1.315 + boolean newCommandLine = false; 1.316 + boolean syntaxError = false; 1.317 + 1.318 + try (BufferedReader in = new BufferedReader(new FileReader(db.javacStateFilename))) { 1.319 + for (;;) { 1.320 + String l = in.readLine(); 1.321 + if (l==null) break; 1.322 + if (l.length()>=3 && l.charAt(1) == ' ') { 1.323 + char c = l.charAt(0); 1.324 + if (c == 'M') { 1.325 + lastModule = db.prev.loadModule(l); 1.326 + } else 1.327 + if (c == 'P') { 1.328 + if (lastModule == null) { syntaxError = true; break; } 1.329 + lastPackage = db.prev.loadPackage(lastModule, l); 1.330 + } else 1.331 + if (c == 'D') { 1.332 + if (lastModule == null || lastPackage == null) { syntaxError = true; break; } 1.333 + lastPackage.loadDependency(l); 1.334 + } else 1.335 + if (c == 'I') { 1.336 + if (lastModule == null || lastPackage == null) { syntaxError = true; break; } 1.337 + lastPackage.loadPubapi(l); 1.338 + } else 1.339 + if (c == 'A') { 1.340 + if (lastModule == null || lastPackage == null) { syntaxError = true; break; } 1.341 + lastPackage.loadArtifact(l); 1.342 + } else 1.343 + if (c == 'S') { 1.344 + if (lastModule == null || lastPackage == null) { syntaxError = true; break; } 1.345 + lastSource = db.prev.loadSource(lastPackage, l, false); 1.346 + } else 1.347 + if (c == 'G') { 1.348 + if (lastModule == null || lastPackage == null) { syntaxError = true; break; } 1.349 + lastSource = db.prev.loadSource(lastPackage, l, true); 1.350 + } else 1.351 + if (c == 'R') { 1.352 + String ncmdl = "R "+db.theArgs; 1.353 + if (!l.equals(ncmdl)) { 1.354 + newCommandLine = true; 1.355 + } 1.356 + } else 1.357 + if (c == '#') { 1.358 + if (l.startsWith("# javac_state ver ")) { 1.359 + int sp = l.indexOf(" ", 18); 1.360 + if (sp != -1) { 1.361 + String ver = l.substring(18,sp); 1.362 + if (!ver.equals("0.3")) { 1.363 + break; 1.364 + } 1.365 + foundCorrectVerNr = true; 1.366 + } 1.367 + } 1.368 + } 1.369 + } 1.370 + } 1.371 + } catch (FileNotFoundException e) { 1.372 + // Silently create a new javac_state file. 1.373 + noFileFound = true; 1.374 + } catch (IOException e) { 1.375 + Log.info("Dropping old javac_state because of errors when reading it."); 1.376 + db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err); 1.377 + foundCorrectVerNr = true; 1.378 + newCommandLine = false; 1.379 + syntaxError = false; 1.380 + } 1.381 + if (foundCorrectVerNr == false && !noFileFound) { 1.382 + Log.info("Dropping old javac_state since it is of an old version."); 1.383 + db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err); 1.384 + } else 1.385 + if (newCommandLine == true && !noFileFound) { 1.386 + Log.info("Dropping old javac_state since a new command line is used!"); 1.387 + db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err); 1.388 + } else 1.389 + if (syntaxError == true) { 1.390 + Log.info("Dropping old javac_state since it contains syntax errors."); 1.391 + db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err); 1.392 + } 1.393 + db.prev.calculateDependents(); 1.394 + return db; 1.395 + } 1.396 + 1.397 + /** 1.398 + * Mark a java package as tainted, ie it needs recompilation. 1.399 + */ 1.400 + public void taintPackage(String name, String because) { 1.401 + if (!taintedPackages.contains(name)) { 1.402 + if (because != null) Log.debug("Tainting "+Util.justPackageName(name)+" because "+because); 1.403 + // It has not been tainted before. 1.404 + taintedPackages.add(name); 1.405 + needsSaving(); 1.406 + Package nowp = now.packages().get(name); 1.407 + if (nowp != null) { 1.408 + for (String d : nowp.dependents()) { 1.409 + taintPackage(d, because); 1.410 + } 1.411 + } 1.412 + } 1.413 + } 1.414 + 1.415 + /** 1.416 + * This packages need recompilation. 1.417 + */ 1.418 + public Set<String> taintedPackages() { 1.419 + return taintedPackages; 1.420 + } 1.421 + 1.422 + /** 1.423 + * Clean out the tainted package set, used after the first round of compiles, 1.424 + * prior to propagating dependencies. 1.425 + */ 1.426 + public void clearTaintedPackages() { 1.427 + taintedPackages = new HashSet<String>(); 1.428 + } 1.429 + 1.430 + /** 1.431 + * Go through all sources and check which have been removed, added or modified 1.432 + * and taint the corresponding packages. 1.433 + */ 1.434 + public void checkSourceStatus(boolean check_gensrc) { 1.435 + removedSources = calculateRemovedSources(); 1.436 + for (Source s : removedSources) { 1.437 + if (!s.isGenerated() || check_gensrc) { 1.438 + taintPackage(s.pkg().name(), "source "+s.name()+" was removed"); 1.439 + } 1.440 + } 1.441 + 1.442 + addedSources = calculateAddedSources(); 1.443 + for (Source s : addedSources) { 1.444 + String msg = null; 1.445 + if (isIncremental()) { 1.446 + // When building from scratch, there is no point 1.447 + // printing "was added" for every file since all files are added. 1.448 + // However for an incremental build it makes sense. 1.449 + msg = "source "+s.name()+" was added"; 1.450 + } 1.451 + if (!s.isGenerated() || check_gensrc) { 1.452 + taintPackage(s.pkg().name(), msg); 1.453 + } 1.454 + } 1.455 + 1.456 + modifiedSources = calculateModifiedSources(); 1.457 + for (Source s : modifiedSources) { 1.458 + if (!s.isGenerated() || check_gensrc) { 1.459 + taintPackage(s.pkg().name(), "source "+s.name()+" was modified"); 1.460 + } 1.461 + } 1.462 + } 1.463 + 1.464 + /** 1.465 + * Acquire the compile_java_packages suffix rule for .java files. 1.466 + */ 1.467 + public Map<String,Transformer> getJavaSuffixRule() { 1.468 + Map<String,Transformer> sr = new HashMap<String,Transformer>(); 1.469 + sr.put(".java", compileJavaPackages); 1.470 + return sr; 1.471 + } 1.472 + 1.473 + /** 1.474 + * Acquire the copying transform. 1.475 + */ 1.476 + public Transformer getCopier() { 1.477 + return copyFiles; 1.478 + } 1.479 + 1.480 + /** 1.481 + * If artifacts have gone missing, force a recompile of the packages 1.482 + * they belong to. 1.483 + */ 1.484 + public void taintPackagesThatMissArtifacts() { 1.485 + for (Package pkg : prev.packages().values()) { 1.486 + for (File f : pkg.artifacts().values()) { 1.487 + if (!f.exists()) { 1.488 + // Hmm, the artifact on disk does not exist! Someone has removed it.... 1.489 + // Lets rebuild the package. 1.490 + taintPackage(pkg.name(), ""+f+" is missing."); 1.491 + } 1.492 + } 1.493 + } 1.494 + } 1.495 + 1.496 + /** 1.497 + * Propagate recompilation through the dependency chains. 1.498 + * Avoid re-tainting packages that have already been compiled. 1.499 + */ 1.500 + public void taintPackagesDependingOnChangedPackages(Set<String> pkgs, Set<String> recentlyCompiled) { 1.501 + for (Package pkg : prev.packages().values()) { 1.502 + for (String dep : pkg.dependencies()) { 1.503 + if (pkgs.contains(dep) && !recentlyCompiled.contains(pkg.name())) { 1.504 + taintPackage(pkg.name(), " its depending on "+dep); 1.505 + } 1.506 + } 1.507 + } 1.508 + } 1.509 + 1.510 + /** 1.511 + * Scan all output dirs for artifacts and remove those files (artifacts?) 1.512 + * that are not recognized as such, in the javac_state file. 1.513 + */ 1.514 + public void removeUnidentifiedArtifacts() { 1.515 + Set<File> allKnownArtifacts = new HashSet<File>(); 1.516 + for (Package pkg : prev.packages().values()) { 1.517 + for (File f : pkg.artifacts().values()) { 1.518 + allKnownArtifacts.add(f); 1.519 + } 1.520 + } 1.521 + // Do not forget about javac_state.... 1.522 + allKnownArtifacts.add(javacState); 1.523 + 1.524 + for (File f : binArtifacts) { 1.525 + if (!allKnownArtifacts.contains(f)) { 1.526 + Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state."); 1.527 + f.delete(); 1.528 + } 1.529 + } 1.530 + for (File f : headerArtifacts) { 1.531 + if (!allKnownArtifacts.contains(f)) { 1.532 + Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state."); 1.533 + f.delete(); 1.534 + } 1.535 + } 1.536 + for (File f : gensrcArtifacts) { 1.537 + if (!allKnownArtifacts.contains(f)) { 1.538 + Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state."); 1.539 + f.delete(); 1.540 + } 1.541 + } 1.542 + } 1.543 + 1.544 + /** 1.545 + * Remove artifacts that are no longer produced when compiling! 1.546 + */ 1.547 + public void removeSuperfluousArtifacts(Set<String> recentlyCompiled) { 1.548 + // Nothing to do, if nothing was recompiled. 1.549 + if (recentlyCompiled.size() == 0) return; 1.550 + 1.551 + for (String pkg : now.packages().keySet()) { 1.552 + // If this package has not been recompiled, skip the check. 1.553 + if (!recentlyCompiled.contains(pkg)) continue; 1.554 + Collection<File> arts = now.artifacts().values(); 1.555 + for (File f : fetchPrevArtifacts(pkg).values()) { 1.556 + if (!arts.contains(f)) { 1.557 + Log.debug("Removing "+f.getPath()+" since it is now superfluous!"); 1.558 + if (f.exists()) f.delete(); 1.559 + } 1.560 + } 1.561 + } 1.562 + } 1.563 + 1.564 + /** 1.565 + * Return those files belonging to prev, but not now. 1.566 + */ 1.567 + private Set<Source> calculateRemovedSources() { 1.568 + Set<Source> removed = new HashSet<Source>(); 1.569 + for (String src : prev.sources().keySet()) { 1.570 + if (now.sources().get(src) == null) { 1.571 + removed.add(prev.sources().get(src)); 1.572 + } 1.573 + } 1.574 + return removed; 1.575 + } 1.576 + 1.577 + /** 1.578 + * Return those files belonging to now, but not prev. 1.579 + */ 1.580 + private Set<Source> calculateAddedSources() { 1.581 + Set<Source> added = new HashSet<Source>(); 1.582 + for (String src : now.sources().keySet()) { 1.583 + if (prev.sources().get(src) == null) { 1.584 + added.add(now.sources().get(src)); 1.585 + } 1.586 + } 1.587 + return added; 1.588 + } 1.589 + 1.590 + /** 1.591 + * Return those files where the timestamp is newer. 1.592 + * If a source file timestamp suddenly is older than what is known 1.593 + * about it in javac_state, then consider it modified, but print 1.594 + * a warning! 1.595 + */ 1.596 + private Set<Source> calculateModifiedSources() { 1.597 + Set<Source> modified = new HashSet<Source>(); 1.598 + for (String src : now.sources().keySet()) { 1.599 + Source n = now.sources().get(src); 1.600 + Source t = prev.sources().get(src); 1.601 + if (prev.sources().get(src) != null) { 1.602 + if (t != null) { 1.603 + if (n.lastModified() > t.lastModified()) { 1.604 + modified.add(n); 1.605 + } else if (n.lastModified() < t.lastModified()) { 1.606 + modified.add(n); 1.607 + Log.warn("The source file "+n.name()+" timestamp has moved backwards in time."); 1.608 + } 1.609 + } 1.610 + } 1.611 + } 1.612 + return modified; 1.613 + } 1.614 + 1.615 + /** 1.616 + * Recursively delete a directory and all its contents. 1.617 + */ 1.618 + private static void deleteContents(File dir) { 1.619 + if (dir != null && dir.exists()) { 1.620 + for (File f : dir.listFiles()) { 1.621 + if (f.isDirectory()) { 1.622 + deleteContents(f); 1.623 + } 1.624 + f.delete(); 1.625 + } 1.626 + } 1.627 + } 1.628 + 1.629 + /** 1.630 + * Run the copy translator only. 1.631 + */ 1.632 + public void performCopying(File binDir, Map<String,Transformer> suffixRules) { 1.633 + Map<String,Transformer> sr = new HashMap<String,Transformer>(); 1.634 + for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) { 1.635 + if (e.getValue() == copyFiles) { 1.636 + sr.put(e.getKey(), e.getValue()); 1.637 + } 1.638 + } 1.639 + perform(binDir, sr); 1.640 + } 1.641 + 1.642 + /** 1.643 + * Run all the translators that translate into java source code. 1.644 + * I.e. all translators that are not copy nor compile_java_source. 1.645 + */ 1.646 + public void performTranslation(File gensrcDir, Map<String,Transformer> suffixRules) { 1.647 + Map<String,Transformer> sr = new HashMap<String,Transformer>(); 1.648 + for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) { 1.649 + if (e.getValue() != copyFiles && 1.650 + e.getValue() != compileJavaPackages) { 1.651 + sr.put(e.getKey(), e.getValue()); 1.652 + } 1.653 + } 1.654 + perform(gensrcDir, sr); 1.655 + } 1.656 + 1.657 + /** 1.658 + * Compile all the java sources. Return true, if it needs to be called again! 1.659 + */ 1.660 + public boolean performJavaCompilations(File binDir, 1.661 + String serverSettings, 1.662 + String[] args, 1.663 + Set<String> recentlyCompiled, 1.664 + boolean[] rcValue) { 1.665 + Map<String,Transformer> suffixRules = new HashMap<String,Transformer>(); 1.666 + suffixRules.put(".java", compileJavaPackages); 1.667 + compileJavaPackages.setExtra(serverSettings); 1.668 + compileJavaPackages.setExtra(args); 1.669 + 1.670 + rcValue[0] = perform(binDir, suffixRules); 1.671 + recentlyCompiled.addAll(taintedPackages()); 1.672 + clearTaintedPackages(); 1.673 + boolean again = !packagesWithChangedPublicApis.isEmpty(); 1.674 + taintPackagesDependingOnChangedPackages(packagesWithChangedPublicApis, recentlyCompiled); 1.675 + packagesWithChangedPublicApis = new HashSet<String>(); 1.676 + return again && rcValue[0]; 1.677 + } 1.678 + 1.679 + /** 1.680 + * Store the source into the set of sources belonging to the given transform. 1.681 + */ 1.682 + private void addFileToTransform(Map<Transformer,Map<String,Set<URI>>> gs, Transformer t, Source s) { 1.683 + Map<String,Set<URI>> fs = gs.get(t); 1.684 + if (fs == null) { 1.685 + fs = new HashMap<String,Set<URI>>(); 1.686 + gs.put(t, fs); 1.687 + } 1.688 + Set<URI> ss = fs.get(s.pkg().name()); 1.689 + if (ss == null) { 1.690 + ss = new HashSet<URI>(); 1.691 + fs.put(s.pkg().name(), ss); 1.692 + } 1.693 + ss.add(s.file().toURI()); 1.694 + } 1.695 + 1.696 + /** 1.697 + * For all packages, find all sources belonging to the package, group the sources 1.698 + * based on their transformers and apply the transformers on each source code group. 1.699 + */ 1.700 + private boolean perform(File outputDir, Map<String,Transformer> suffixRules) 1.701 + { 1.702 + boolean rc = true; 1.703 + // Group sources based on transforms. A source file can only belong to a single transform. 1.704 + Map<Transformer,Map<String,Set<URI>>> groupedSources = new HashMap<Transformer,Map<String,Set<URI>>>(); 1.705 + for (Source src : now.sources().values()) { 1.706 + Transformer t = suffixRules.get(src.suffix()); 1.707 + if (t != null) { 1.708 + if (taintedPackages.contains(src.pkg().name()) && !src.isLinkedOnly()) { 1.709 + addFileToTransform(groupedSources, t, src); 1.710 + } 1.711 + } 1.712 + } 1.713 + // Go through the transforms and transform them. 1.714 + for (Map.Entry<Transformer,Map<String,Set<URI>>> e : groupedSources.entrySet()) { 1.715 + Transformer t = e.getKey(); 1.716 + Map<String,Set<URI>> srcs = e.getValue(); 1.717 + // These maps need to be synchronized since multiple threads will be writing results into them. 1.718 + Map<String,Set<URI>> packageArtifacts = Collections.synchronizedMap(new HashMap<String,Set<URI>>()); 1.719 + Map<String,Set<String>> packageDependencies = Collections.synchronizedMap(new HashMap<String,Set<String>>()); 1.720 + Map<String,String> packagePublicApis = Collections.synchronizedMap(new HashMap<String,String>()); 1.721 + 1.722 + boolean r = t.transform(srcs, 1.723 + visibleSrcs, 1.724 + visibleClasses, 1.725 + prev.dependents(), 1.726 + outputDir.toURI(), 1.727 + packageArtifacts, 1.728 + packageDependencies, 1.729 + packagePublicApis, 1.730 + 0, 1.731 + isIncremental(), 1.732 + numCores, 1.733 + out, 1.734 + err); 1.735 + if (!r) rc = false; 1.736 + 1.737 + for (String p : srcs.keySet()) { 1.738 + recompiledPackages.add(p); 1.739 + } 1.740 + // The transform is done! Extract all the artifacts and store the info into the Package objects. 1.741 + for (Map.Entry<String,Set<URI>> a : packageArtifacts.entrySet()) { 1.742 + Module mnow = now.findModuleFromPackageName(a.getKey()); 1.743 + mnow.addArtifacts(a.getKey(), a.getValue()); 1.744 + } 1.745 + // Extract all the dependencies and store the info into the Package objects. 1.746 + for (Map.Entry<String,Set<String>> a : packageDependencies.entrySet()) { 1.747 + Set<String> deps = a.getValue(); 1.748 + Module mnow = now.findModuleFromPackageName(a.getKey()); 1.749 + mnow.setDependencies(a.getKey(), deps); 1.750 + } 1.751 + // Extract all the pubapis and store the info into the Package objects. 1.752 + for (Map.Entry<String,String> a : packagePublicApis.entrySet()) { 1.753 + Module mprev = prev.findModuleFromPackageName(a.getKey()); 1.754 + List<String> pubapi = Package.pubapiToList(a.getValue()); 1.755 + Module mnow = now.findModuleFromPackageName(a.getKey()); 1.756 + mnow.setPubapi(a.getKey(), pubapi); 1.757 + if (mprev.hasPubapiChanged(a.getKey(), pubapi)) { 1.758 + // Aha! The pubapi of this package has changed! 1.759 + // It can also be a new compile from scratch. 1.760 + if (mprev.lookupPackage(a.getKey()).existsInJavacState()) { 1.761 + // This is an incremental compile! The pubapi 1.762 + // did change. Trigger recompilation of dependents. 1.763 + packagesWithChangedPublicApis.add(a.getKey()); 1.764 + Log.info("The pubapi of "+Util.justPackageName(a.getKey())+" has changed!"); 1.765 + } 1.766 + } 1.767 + } 1.768 + } 1.769 + return rc; 1.770 + } 1.771 + 1.772 + /** 1.773 + * Utility method to recursively find all files below a directory. 1.774 + */ 1.775 + private static Set<File> findAllFiles(File dir) { 1.776 + Set<File> foundFiles = new HashSet<File>(); 1.777 + if (dir == null) { 1.778 + return foundFiles; 1.779 + } 1.780 + recurse(dir, foundFiles); 1.781 + return foundFiles; 1.782 + } 1.783 + 1.784 + private static void recurse(File dir, Set<File> foundFiles) { 1.785 + for (File f : dir.listFiles()) { 1.786 + if (f.isFile()) { 1.787 + foundFiles.add(f); 1.788 + } else if (f.isDirectory()) { 1.789 + recurse(f, foundFiles); 1.790 + } 1.791 + } 1.792 + } 1.793 + 1.794 + /** 1.795 + * Compare the calculate source list, with an explicit list, usually supplied from the makefile. 1.796 + * Used to detect bugs where the makefile and sjavac have different opinions on which files 1.797 + * should be compiled. 1.798 + */ 1.799 + public void compareWithMakefileList(File makefileSourceList) 1.800 + throws ProblemException 1.801 + { 1.802 + // If we are building on win32 using for example cygwin the paths in the makefile source list 1.803 + // might be /cygdrive/c/.... which does not match c:\.... 1.804 + // We need to adjust our calculated sources to be identical, if necessary. 1.805 + boolean mightNeedRewriting = File.pathSeparatorChar == ';'; 1.806 + 1.807 + if (makefileSourceList == null) return; 1.808 + 1.809 + Set<String> calculatedSources = new HashSet<String>(); 1.810 + Set<String> listedSources = new HashSet<String>(); 1.811 + 1.812 + // Create a set of filenames with full paths. 1.813 + for (Source s : now.sources().values()) { 1.814 + // Don't include link only sources when comparing sources to compile 1.815 + if (!s.isLinkedOnly()) { 1.816 + calculatedSources.add(s.file().getPath()); 1.817 + } 1.818 + } 1.819 + // Read in the file and create another set of filenames with full paths. 1.820 + try { 1.821 + BufferedReader in = new BufferedReader(new FileReader(makefileSourceList)); 1.822 + for (;;) { 1.823 + String l = in.readLine(); 1.824 + if (l==null) break; 1.825 + l = l.trim(); 1.826 + if (mightNeedRewriting) { 1.827 + if (l.indexOf(":") == 1 && l.indexOf("\\") == 2) { 1.828 + // Everything a-ok, the format is already C:\foo\bar 1.829 + } else if (l.indexOf(":") == 1 && l.indexOf("/") == 2) { 1.830 + // The format is C:/foo/bar, rewrite into the above format. 1.831 + l = l.replaceAll("/","\\\\"); 1.832 + } else if (l.charAt(0) == '/' && l.indexOf("/",1) != -1) { 1.833 + // The format might be: /cygdrive/c/foo/bar, rewrite into the above format. 1.834 + // Do not hardcode the name cygdrive here. 1.835 + int slash = l.indexOf("/",1); 1.836 + l = l.replaceAll("/","\\\\"); 1.837 + l = ""+l.charAt(slash+1)+":"+l.substring(slash+2); 1.838 + } 1.839 + if (Character.isLowerCase(l.charAt(0))) { 1.840 + l = Character.toUpperCase(l.charAt(0))+l.substring(1); 1.841 + } 1.842 + } 1.843 + listedSources.add(l); 1.844 + } 1.845 + } catch (FileNotFoundException e) { 1.846 + throw new ProblemException("Could not open "+makefileSourceList.getPath()+" since it does not exist!"); 1.847 + } catch (IOException e) { 1.848 + throw new ProblemException("Could not read "+makefileSourceList.getPath()); 1.849 + } 1.850 + 1.851 + for (String s : listedSources) { 1.852 + if (!calculatedSources.contains(s)) { 1.853 + throw new ProblemException("The makefile listed source "+s+" was not calculated by the smart javac wrapper!"); 1.854 + } 1.855 + } 1.856 + 1.857 + for (String s : calculatedSources) { 1.858 + if (!listedSources.contains(s)) { 1.859 + throw new ProblemException("The smart javac wrapper calculated source "+s+" was not listed by the makefiles!"); 1.860 + } 1.861 + } 1.862 + } 1.863 +}