/** * Compare the calculate source list, with an explicit list, usually supplied from the makefile. * Used to detect bugs where the makefile and sjavac have different opinions on which files should * be compiled. */ public void compareWithMakefileList(File makefileSourceList) throws ProblemException { // If we are building on win32 using for example cygwin the paths in the makefile source list // might be /cygdrive/c/.... which does not match c:\.... // We need to adjust our calculated sources to be identical, if necessary. 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()) { calculatedSources.add(s.file().getPath()); } } // 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!"); } } for (String s : calculatedSources) { if (!listedSources.contains(s)) { throw new ProblemException( "The smart javac wrapper calculated source " + s + " was not listed by the makefiles!"); } } }
/** * For all packages, find all sources belonging to the package, group the sources based on their * transformers and apply the transformers on each source code group. */ private boolean perform(File outputDir, Map<String, Transformer> suffixRules) { boolean rc = true; // Group sources based on transforms. A source file can only belong to a single transform. Map<Transformer, Map<String, Set<URI>>> groupedSources = new HashMap<>(); for (Source src : now.sources().values()) { Transformer t = suffixRules.get(src.suffix()); if (t != null) { if (taintedPackages.contains(src.pkg().name()) && !src.isLinkedOnly()) { addFileToTransform(groupedSources, t, src); } } } // Go through the transforms and transform them. for (Map.Entry<Transformer, Map<String, Set<URI>>> e : groupedSources.entrySet()) { Transformer t = e.getKey(); Map<String, Set<URI>> srcs = e.getValue(); // 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( srcs, visibleSrcs, visibleClasses, prev.dependents(), outputDir.toURI(), packageArtifacts, packageDependencies, packagePublicApis, 0, isIncremental(), numCores, out, err); if (!r) rc = false; for (String p : srcs.keySet()) { recompiledPackages.add(p); } // The transform is done! Extract all the artifacts and store the info into the Package // objects. for (Map.Entry<String, Set<URI>> a : packageArtifacts.entrySet()) { Module mnow = now.findModuleFromPackageName(a.getKey()); mnow.addArtifacts(a.getKey(), a.getValue()); } // Extract all the dependencies and store the info into the Package objects. 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. packagesWithChangedPublicApis.add(a.getKey()); Log.info("The pubapi of " + Util.justPackageName(a.getKey()) + " has changed!"); } } } } return rc; }