Thu, 20 Jun 2013 08:45:43 +0100
8016613: javac should avoid source 8 only analysis when compiling for source 7
Reviewed-by: jjg
Contributed-by: maurizio.cimadamore@oracle.com
1 /*
2 * Copyright (c) 2012, 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 */
26 package com.sun.tools.sjavac;
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;
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<String>();
75 // This package has the following dependents, that depend on this package.
76 private Set<String> dependents = new HashSet<String>();
77 // This is the public api of this package.
78 private List<String> pubapi = new ArrayList<String>();
79 // Map from source file name to Source info object.
80 private Map<String,Source> sources = new HashMap<String,Source>();
81 // This package generated these artifacts.
82 private Map<String,File> artifacts = new HashMap<String,File>();
84 public Package(Module m, String n) {
85 int c = n.indexOf(":");
86 assert(c != -1);
87 String mn = n.substring(0,c);
88 assert(m.name().equals(m.name()));
89 name = n;
90 dirname = n.replace('.', File.separatorChar);
91 if (m.name().length() > 0) {
92 // There is a module here, prefix the module dir name to the path.
93 dirname = m.dirname()+File.separatorChar+dirname;
94 }
95 }
97 public Module mod() { return mod; }
98 public String name() { return name; }
99 public String dirname() { return dirname; }
100 public Map<String,Source> sources() { return sources; }
101 public Map<String,File> artifacts() { return artifacts; }
102 public List<String> pubapi() { return pubapi; }
104 public Set<String> dependencies() { return dependencies; }
105 public Set<String> dependents() { return dependents; }
107 @Override
108 public boolean equals(Object o) {
109 return (o instanceof Package) && name.equals(((Package)o).name);
110 }
112 @Override
113 public int hashCode() {
114 return name.hashCode();
115 }
117 @Override
118 public int compareTo(Package o) {
119 return name.compareTo(o.name);
120 }
122 public void addSource(Source s) {
123 sources.put(s.file().getPath(), s);
124 }
126 public void addDependency(String d) {
127 dependencies.add(d);
128 }
130 public void addDependent(String d) {
131 dependents.add(d);
132 }
134 public void addPubapi(String p) {
135 pubapi.add(p);
136 }
138 /**
139 * Check if we have knowledge in the javac state that
140 * describe the results of compiling this package before.
141 */
142 public boolean existsInJavacState() {
143 return artifacts.size() > 0 || pubapi.size() > 0;
144 }
146 public static List<String> pubapiToList(String ps)
147 {
148 String[] lines = ps.split("\n");
149 List<String> r = new ArrayList<String>();
150 for (String l : lines) {
151 r.add(l);
152 }
153 return r;
154 }
156 public boolean hasPubapiChanged(List<String> ps) {
157 Iterator<String> i = ps.iterator();
158 Iterator<String> j = pubapi.iterator();
159 int line = 0;
160 while (i.hasNext() && j.hasNext()) {
161 String is = i.next();
162 String js = j.next();
163 if (!is.equals(js)) {
164 Log.debug("Change in pubapi for package "+name+" line "+line);
165 Log.debug("Old: "+js);
166 Log.debug("New: "+is);
167 return true;
168 }
169 line++;
170 }
171 if ((i.hasNext() && !j.hasNext() ) ||
172 (!i.hasNext() && j.hasNext())) {
173 Log.debug("Change in pubapi for package "+name);
174 if (i.hasNext()) {
175 Log.debug("New has more lines!");
176 } else {
177 Log.debug("Old has more lines!");
178 }
179 return true;
180 }
181 return false;
182 }
184 public void setPubapi(List<String> ps) {
185 pubapi = ps;
186 }
188 public void setDependencies(Set<String> ds) {
189 dependencies = ds;
190 }
192 public void save(StringBuilder b) {
193 b.append("P ").append(name).append("\n");
194 Source.saveSources(sources, b);
195 saveDependencies(b);
196 savePubapi(b);
197 saveArtifacts(b);
198 }
200 static public Package load(Module module, String l) {
201 String name = l.substring(2);
202 return new Package(module, name);
203 }
205 public void loadDependency(String l) {
206 String n = l.substring(2);
207 addDependency(n);
208 }
210 public void loadPubapi(String l) {
211 String pi = l.substring(2);
212 addPubapi(pi);
213 }
215 public void saveDependencies(StringBuilder b) {
216 List<String> sorted_dependencies = new ArrayList<String>();
217 for (String key : dependencies) {
218 sorted_dependencies.add(key);
219 }
220 Collections.sort(sorted_dependencies);
221 for (String a : sorted_dependencies) {
222 b.append("D "+a+"\n");
223 }
224 }
226 public void savePubapi(StringBuilder b) {
227 for (String l : pubapi) {
228 b.append("I "+l+"\n");
229 }
230 }
232 public static void savePackages(Map<String,Package> packages, StringBuilder b) {
233 List<String> sorted_packages = new ArrayList<String>();
234 for (String key : packages.keySet() ) {
235 sorted_packages.add(key);
236 }
237 Collections.sort(sorted_packages);
238 for (String s : sorted_packages) {
239 Package p = packages.get(s);
240 p.save(b);
241 }
242 }
244 public void addArtifact(String a) {
245 artifacts.put(a, new File(a));
246 }
248 public void addArtifact(File f) {
249 artifacts.put(f.getPath(), f);
250 }
252 public void addArtifacts(Set<URI> as) {
253 for (URI u : as) {
254 addArtifact(new File(u));
255 }
256 }
258 public void setArtifacts(Set<URI> as) {
259 assert(!artifacts.isEmpty());
260 artifacts = new HashMap<String,File>();
261 addArtifacts(as);
262 }
264 public void loadArtifact(String l) {
265 // Find next space after "A ".
266 int dp = l.indexOf(' ',2);
267 String fn = l.substring(2,dp);
268 long last_modified = Long.parseLong(l.substring(dp+1));
269 File f = new File(fn);
270 if (f.exists() && f.lastModified() != last_modified) {
271 // Hmm, the artifact on disk does not have the same last modified
272 // timestamp as the information from the build database.
273 // We no longer trust the artifact on disk. Delete it.
274 // The smart javac wrapper will then rebuild the artifact.
275 Log.debug("Removing "+f.getPath()+" since its timestamp does not match javac_state.");
276 f.delete();
277 }
278 artifacts.put(f.getPath(), f);
279 }
281 public void saveArtifacts(StringBuilder b) {
282 List<File> sorted_artifacts = new ArrayList<File>();
283 for (File f : artifacts.values()) {
284 sorted_artifacts.add(f);
285 }
286 Collections.sort(sorted_artifacts);
287 for (File f : sorted_artifacts) {
288 // The last modified information is only used
289 // to detect tampering with the output dir.
290 // If the outputdir has been modified, not by javac,
291 // then a mismatch will be detected in the last modified
292 // timestamps stored in the build database compared
293 // to the timestamps on disk and the artifact will be deleted.
295 b.append("A "+f.getPath()+" "+f.lastModified()+"\n");
296 }
297 }
299 /**
300 * Always clean out a tainted package before it is recompiled.
301 */
302 public void deleteArtifacts() {
303 for (File a : artifacts.values()) {
304 a.delete();
305 }
306 }
307 }