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.net.URI; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Iterator; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 39 /** 40 * The Package class maintains meta information about a package. 41 * For example its sources, dependents,its pubapi and its artifacts. 42 * 43 * It might look odd that we track dependents/pubapi/artifacts on 44 * a package level, but it makes sense since recompiling a full package 45 * takes as long as recompiling a single java file in that package, 46 * if you take into account the startup time of the jvm. 47 * 48 * Also the dependency information will be much smaller (good for the javac_state file size) 49 * and it simplifies tracking artifact generation, you do not always know from which 50 * source a class file was generated, but you always know which package it belongs to. 51 * 52 * It is also educational to see package dependencies triggering recompilation of 53 * other packages. Even though the recompilation was perhaps not necessary, 54 * the visible recompilation of the dependent packages indicates how much circular 55 * dependencies your code has. 56 * 57 * <p><b>This is NOT part of any supported API. 58 * If you write code that depends on this, you do so at your own 59 * risk. This code and its internal interfaces are subject to change 60 * or deletion without notice.</b></p> 61 */ 62 public class Package implements Comparable<Package> { 63 // The module this package belongs to. (There is a legacy module with an empty string name, 64 // used for all legacy sources.) 65 private Module mod; 66 // Name of this package, module:pkg 67 // ex1 jdk.base:java.lang 68 // ex2 :java.lang (when in legacy mode) 69 private String name; 70 // The directory path to the package. If the package belongs to a module, 71 // then that module's file system name is part of the path. 72 private String dirname; 73 // This package depends on these packages. 74 private Set<String> dependencies = new HashSet<>(); 75 // This package has the following dependents, that depend on this package. 76 private Set<String> dependents = new HashSet<>(); 77 // Map from source file name to Source info object. 78 private Map<String,Source> sources = new HashMap<>(); 79 // This package generated these artifacts. 80 private Map<String,File> artifacts = new HashMap<>(); 81 // Pubapi for compiled sources 82 private List<String> pubapi_for_compiled_sources = new ArrayList<>(); 83 // Pubapi for linked classes 84 private List<String> pubapi_for_linked_classes = new ArrayList<>(); 85 // Archives that have the same timestamp as previous run, ie they are probably unchanged. 86 private Set<String> unchanged_archives = new HashSet<>(); 87 88 public Package(Module m, String n) { 89 int c = n.indexOf(":"); 90 assert(c != -1); 91 String mn = n.substring(0,c); 92 assert(m.name().equals(m.name())); 93 name = n; 94 dirname = n.replace('.', File.separatorChar); 95 if (m.name().length() > 0) { 96 // There is a module here, prefix the module dir name to the path. 97 dirname = m.dirname()+File.separatorChar+dirname; 98 } 99 } 100 101 public Module mod() { return mod; } 102 public String name() { return name; } 103 public String dirname() { return dirname; } 104 public Map<String,Source> sources() { return sources; } 105 public Map<String,File> artifacts() { return artifacts; } 106 public List<String> pubapiForCompiledSources() { return pubapi_for_compiled_sources; } 107 public List<String> pubapiForLinkedClasses() { return pubapi_for_linked_classes; } 108 109 public Set<String> dependencies() { return dependencies; } 110 public Set<String> dependents() { return dependents; } 111 112 @Override 113 public boolean equals(Object o) { 114 return (o instanceof Package) && name.equals(((Package)o).name); 115 } 116 117 @Override 118 public int hashCode() { 119 return name.hashCode(); 120 } 121 122 @Override 123 public int compareTo(Package o) { 124 return name.compareTo(o.name); 125 } 126 127 public void addSource(Source s) { 128 sources.put(s.file().getPath(), s); 129 } 130 131 public void addDependency(String d) { 132 dependencies.add(d); 133 } 134 135 public void addDependent(String d) { 136 dependents.add(d); 137 } 138 139 /** 140 * Check if we have knowledge in the javac state that 141 * describe the results of compiling this package before. 142 */ 143 public boolean existsInJavacState() { 144 return artifacts.size() > 0 || pubapi_for_compiled_sources.size() > 0; 145 } 146 147 public boolean hasPubapiForCompiledSourcesChanged(List<String> ps) { 148 Iterator<String> i = ps.iterator(); 149 Iterator<String> j = pubapi_for_compiled_sources.iterator(); 150 int line = 0; 151 while (i.hasNext() && j.hasNext()) { 152 String is = i.next(); 153 String js = j.next(); 154 if (!is.equals(js)) { 155 Log.debug("Change in pubapi for package "+name+" line "+line); 156 Log.debug("Old: "+js); 157 Log.debug("New: "+is); 158 return true; 159 } 160 line++; 161 } 162 if ((i.hasNext() && !j.hasNext() ) || 163 (!i.hasNext() && j.hasNext())) { 164 Log.debug("Change in pubapi for package "+name); 165 if (i.hasNext()) { 166 Log.debug("New has more lines!"); 167 } else { 168 Log.debug("Old has more lines!"); 169 } 170 return true; 171 } 172 return false; 173 } 174 175 public void setPubapiForCompiledSources(List<String> ps) { 176 pubapi_for_compiled_sources = ps; 177 } 178 179 public void setPubapiForLinkedClasses(List<String> ps) { 180 pubapi_for_linked_classes = ps; 181 } 182 183 public void setDependencies(Set<String> ds) { 184 dependencies = ds; 185 } 186 187 public void save(StringBuilder b) { 188 b.append("P ").append(name).append("\n"); 189 Source.saveSources(sources, b); 190 saveDependencies(b); 191 savePubapi(b); 192 saveArtifacts(b); 193 } 194 195 static public Package load(Module module, String l) { 196 String name = l.substring(2); 197 return new Package(module, name); 198 } 199 200 public void loadDependency(String l) { 201 String n = l.substring(2); 202 addDependency(n); 203 } 204 205 public void loadPubapi(String l) { 206 char c = l.charAt(2); 207 String pi = l.substring(4); 208 switch (c) { 209 case 'C' : pubapi_for_compiled_sources.add(pi); 210 break; 211 case 'Z' : pubapi_for_linked_classes.add(pi); 212 break; 213 } 214 } 215 216 public void saveDependencies(StringBuilder b) { 217 List<String> sorted_dependencies = new ArrayList<>(); 218 for (String key : dependencies) { 219 sorted_dependencies.add(key); 220 } 221 Collections.sort(sorted_dependencies); 222 for (String a : sorted_dependencies) { 223 b.append("D "+a+"\n"); 224 } 225 } 226 227 public void savePubapi(StringBuilder b) { 228 for (String l : pubapi_for_compiled_sources) { 229 b.append("I C "+l+"\n"); 230 } 231 for (String l : pubapi_for_linked_classes) { 232 b.append("I Z "+l+"\n"); 233 } 234 } 235 236 public static void savePackages(Map<String,Package> packages, StringBuilder b) { 237 List<String> sorted_source_packages = new ArrayList<>(); 238 // First add all packages that have components from from our own sources. 239 for (String key : packages.keySet() ) { 240 Package p = packages.get(key); 241 if (p.sources().size() > 0) { 242 sorted_source_packages.add(key); 243 } 244 } 245 Collections.sort(sorted_source_packages); 246 247 List<String> sorted_classpath_packages = new ArrayList<>(); 248 // Second add all packages found on the classpath only. 249 for (String key : packages.keySet() ) { 250 Package p = packages.get(key); 251 if (p.sources().size() == 0) { 252 sorted_classpath_packages.add(key); 253 } 254 } 255 Collections.sort(sorted_classpath_packages); 256 257 for (String s : sorted_source_packages) { 258 Package p = packages.get(s); 259 p.save(b); 260 } 261 for (String s : sorted_classpath_packages) { 262 Package p = packages.get(s); 263 p.save(b); 264 } 265 } 266 267 public void addArtifact(String a) { 268 artifacts.put(a, new File(a)); 269 } 270 271 public void addArtifact(File f) { 272 artifacts.put(f.getPath(), f); 273 } 274 275 public void addArtifacts(Set<URI> as) { 276 for (URI u : as) { 277 addArtifact(new File(u)); 278 } 279 } 280 281 public void setArtifacts(Set<URI> as) { 282 assert(!artifacts.isEmpty()); 283 artifacts = new HashMap<>(); 284 addArtifacts(as); 285 } 286 287 public void loadArtifact(String l) { 288 // Find next space after "A ". 289 int dp = l.indexOf(' ',2); 290 String fn = l.substring(2,dp); 291 long last_modified = Long.parseLong(l.substring(dp+1)); 292 File f = new File(fn); 293 if (f.exists() && f.lastModified() != last_modified) { 294 // Hmm, the artifact on disk does not have the same last modified 295 // timestamp as the information from the build database. 296 // We no longer trust the artifact on disk. Delete it. 297 // The smart javac wrapper will then rebuild the artifact. 298 Log.debug("Removing "+f.getPath()+" since its timestamp does not match javac_state."); 299 f.delete(); 300 } 301 artifacts.put(f.getPath(), f); 302 } 303 304 public void saveArtifacts(StringBuilder b) { 305 List<File> sorted_artifacts = new ArrayList<>(); 306 for (File f : artifacts.values()) { 307 sorted_artifacts.add(f); 308 } 309 Collections.sort(sorted_artifacts); 310 for (File f : sorted_artifacts) { 311 // The last modified information is only used 312 // to detect tampering with the output dir. 313 // If the outputdir has been modified, not by javac, 314 // then a mismatch will be detected in the last modified 315 // timestamps stored in the build database compared 316 // to the timestamps on disk and the artifact will be deleted. 317 318 b.append("A "+f.getPath()+" "+f.lastModified()+"\n"); 319 } 320 } 321 322 /** 323 * Always clean out a tainted package before it is recompiled. 324 */ 325 public void deleteArtifacts() { 326 for (File a : artifacts.values()) { 327 a.delete(); 328 } 329 } 330 331 /** 332 * Extract the classes stored in the pubapi. 333 */ 334 public Set<String> getClassesFromClasspathPubapi() { 335 Set<String> set = new HashSet<String>(); 336 337 for (String s : pubapi_for_linked_classes) { 338 if (s.startsWith(" TYPE ")) { 339 set.add(s.substring(6, s.indexOf(' ', 6))); 340 } 341 } 342 return set; 343 } 344 345 346 }