1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.sjavac; 27 28 import java.io.File; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 37 /** 38 * The build state class captures the source code and generated artifacts 39 * from a build. There are usually two build states, the previous one (prev), 40 * loaded from the javac_state file, and the current one (now). 41 * 42 * <p><b>This is NOT part of any supported API. 43 * If you write code that depends on this, you do so at your own 44 * risk. This code and its internal interfaces are subject to change 45 * or deletion without notice.</b></p> 46 */ 47 public class BuildState { 48 private Map<String,Module> modules = new HashMap<>(); 49 private Map<String,Package> packages = new HashMap<>(); 50 private Map<String,Source> sources = new HashMap<>(); 51 private Map<String,File> artifacts = new HashMap<>(); 52 // Map from package to a set of packages that depend on said package. 53 private Map<String,Set<String>> dependents = new HashMap<>(); 54 // All Archives that are found to have the same timestamps as in the javac_state file are stored here. 55 // If a archive is not found here, it is either new, or its timestamps has changed. 56 private Set<String> archives = new HashSet<>(); 57 58 public Map<String,Module> modules() { return modules; } 59 public Map<String,Package> packages() { return packages; } 60 public Map<String,Source> sources() { return sources; } 61 public Map<String,File> artifacts() { return artifacts; } 62 public Map<String,Set<String>> dependents() { return dependents; } 63 public Set<String> archives() { return archives; } 64 65 /** 66 * Lookup a module from a name. Create the module if it does 67 * not exist yet. 68 */ 69 public Module lookupModule(String mod) { 70 Module m = modules.get(mod); 71 if (m == null) { 72 m = new Module(mod, "???"); 73 modules.put(mod, m); 74 } 75 return m; 76 } 77 78 /** 79 * Find a module from a given package name. For example: 80 * The package name "base:java.lang" will fetch the module named "base". 81 * The package name ":java.net" will fetch the default module. 82 */ 83 Module findModuleFromPackageName(String pkg) { 84 int cp = pkg.indexOf(':'); 85 assert(cp != -1); 86 String mod = pkg.substring(0, cp); 87 return lookupModule(mod); 88 } 89 90 /** 91 * Store references to all packages, sources and artifacts for all modules 92 * into the build state. I.e. flatten the module tree structure 93 * into global maps stored in the BuildState for easy access. 94 * 95 * @param m The set of modules. 96 */ 97 public void flattenPackagesSourcesAndArtifacts(Map<String,Module> m) { 98 modules = m; 99 // Extract all the found packages. 100 for (Module i : modules.values()) { 101 for (Map.Entry<String,Package> j : i.packages().entrySet()) { 102 Package p = packages.get(j.getKey()); 103 // Check that no two different packages are stored under same name. 104 assert(p == null || p == j.getValue()); 105 if (p == null) { 106 p = j.getValue(); 107 packages.put(j.getKey(),j.getValue()); 108 } 109 for (Map.Entry<String,Source> k : p.sources().entrySet()) { 110 Source s = sources.get(k.getKey()); 111 // Check that no two different sources are stored under same name. 112 assert(s == null || s == k.getValue()); 113 if (s == null) { 114 s = k.getValue(); 115 sources.put(k.getKey(), k.getValue()); 116 } 117 } 118 for (Map.Entry<String,File> g : p.artifacts().entrySet()) { 119 File f = artifacts.get(g.getKey()); 120 // Check that no two artifacts are stored under the same file. 121 assert(f == null || f == g.getValue()); 122 if (f == null) { 123 f = g.getValue(); 124 artifacts.put(g.getKey(), g.getValue()); 125 } 126 } 127 } 128 } 129 } 130 131 /** 132 * Store references to all artifacts found in the module tree into the maps 133 * stored in the build state. 134 * 135 * @param m The set of modules. 136 */ 137 public void flattenArtifacts(Map<String,Module> m) { 138 modules = m; 139 // Extract all the found packages. 140 for (Module i : modules.values()) { 141 for (Map.Entry<String,Package> j : i.packages().entrySet()) { 142 Package p = packages.get(j.getKey()); 143 // Check that no two different packages are stored under same name. 144 assert(p == null || p == j.getValue()); 145 p = j.getValue(); 146 packages.put(j.getKey(),j.getValue()); 147 for (Map.Entry<String,File> g : p.artifacts().entrySet()) { 148 File f = artifacts.get(g.getKey()); 149 // Check that no two artifacts are stored under the same file. 150 assert(f == null || f == g.getValue()); 151 artifacts.put(g.getKey(), g.getValue()); 152 } 153 } 154 } 155 } 156 157 /** 158 * Calculate the package dependents (ie the reverse of the dependencies). 159 */ 160 public void calculateDependents() { 161 dependents = new HashMap<>(); 162 for (String s : packages.keySet()) { 163 Package p = packages.get(s); 164 for (String d : p.dependencies()) { 165 Set<String> ss = dependents.get(d); 166 if (ss == null) { 167 ss = new HashSet<>(); 168 dependents.put(d, ss); 169 } 170 // Add the dependent information to the global dependent map. 171 ss.add(s); 172 Package dp = packages.get(d); 173 // Also add the dependent information to the package specific map. 174 // Normally, you do not compile java.lang et al. Therefore 175 // there are several packages that p depends upon that you 176 // do not have in your state database. This is perfectly fine. 177 if (dp != null) { 178 // But this package did exist in the state database. 179 dp.addDependent(p.name()); 180 } 181 } 182 } 183 } 184 185 /** 186 * Verify that the setModules method above did the right thing when 187 * running through the module->package->source structure. 188 */ 189 public void checkInternalState(String msg, boolean linkedOnly, Map<String,Source> srcs) { 190 boolean baad = false; 191 Map<String,Source> original = new HashMap<>(); 192 Map<String,Source> calculated = new HashMap<>(); 193 194 for (String s : sources.keySet()) { 195 Source ss = sources.get(s); 196 if (ss.isLinkedOnly() == linkedOnly) { 197 calculated.put(s,ss); 198 } 199 } 200 for (String s : srcs.keySet()) { 201 Source ss = srcs.get(s); 202 if (ss.isLinkedOnly() == linkedOnly) { 203 original.put(s,ss); 204 } 205 } 206 if (original.size() != calculated.size()) { 207 Log.error("INTERNAL ERROR "+msg+" original and calculated are not the same size!"); 208 baad = true; 209 } 210 if (!original.keySet().equals(calculated.keySet())) { 211 Log.error("INTERNAL ERROR "+msg+" original and calculated do not have the same domain!"); 212 baad = true; 213 } 214 if (!baad) { 215 for (String s : original.keySet()) { 216 Source s1 = original.get(s); 217 Source s2 = calculated.get(s); 218 if (s1 == null || s2 == null || !s1.equals(s2)) { 219 Log.error("INTERNAL ERROR "+msg+" original and calculated have differing elements for "+s); 220 } 221 baad = true; 222 } 223 } 224 if (baad) { 225 for (String s : original.keySet()) { 226 Source ss = original.get(s); 227 Source sss = calculated.get(s); 228 if (sss == null) { 229 Log.error("The file "+s+" does not exist in calculated tree of sources."); 230 } 231 } 232 for (String s : calculated.keySet()) { 233 Source ss = calculated.get(s); 234 Source sss = original.get(s); 235 if (sss == null) { 236 Log.error("The file "+s+" does not exist in original set of found sources."); 237 } 238 } 239 } 240 } 241 242 /** 243 * Load a module from the javac state file. 244 */ 245 public Module loadModule(String l) { 246 Module m = Module.load(l); 247 modules.put(m.name(), m); 248 return m; 249 } 250 251 /** 252 * Load a package from the javac state file. 253 */ 254 public Package loadPackage(Module lastModule, String l) { 255 Package p = Package.load(lastModule, l); 256 lastModule.addPackage(p); 257 packages.put(p.name(), p); 258 return p; 259 } 260 261 /** 262 * Load a source from the javac state file. 263 */ 264 public Source loadSource(Package lastPackage, String l, boolean is_generated) { 265 Source s = Source.load(lastPackage, l, is_generated); 266 lastPackage.addSource(s); 267 sources.put(s.name(), s); 268 return s; 269 } 270 271 /** 272 * Add to archives, but only if the timestamp is the same, ie the archive is 273 * probably unchanged. 274 */ 275 public void loadArchiveTimestamp(String l) { 276 int p = l.indexOf(' ', 2); 277 String archive = l.substring(2, p); 278 long timestamp = Long.parseLong(l.substring(p+1)); 279 File f = new File(archive); 280 if (f.lastModified() == timestamp) { 281 Log.trace("Same timestamp for "+archive); 282 archives.add(archive); 283 } else { 284 Log.debug("Timestamp changed for "+archive); 285 } 286 } 287 288 /** 289 * Save archive with timestamp info. 290 */ 291 public void saveArchiveTimestamps(StringBuilder b) { 292 List<String> sorted = new ArrayList<>(); 293 sorted.addAll(archives); 294 Collections.sort(sorted); 295 for (String a : sorted) { 296 File f = new File(a); 297 b.append("Z "+a+" "+f.lastModified()+"\n"); 298 } 299 } 300 301 /** 302 * During an incremental compile we need to copy the old javac state 303 * information about packages that were not recompiled. 304 */ 305 public void copyPackagesExcept(BuildState prev, Set<String> recompiled, Set<String> removed) { 306 for (String pkg : prev.packages().keySet()) { 307 // Do not copy recompiled or removed packages. 308 if (recompiled.contains(pkg) || removed.contains(pkg)) continue; 309 Module mnew = findModuleFromPackageName(pkg); 310 Package pprev = prev.packages().get(pkg); 311 mnew.addPackage(pprev); 312 // Do not forget to update the flattened data. 313 packages.put(pkg, pprev); 314 } 315 } 316 }