BuildState.java revision 2593:035b01d356ee
1/*
2 * Copyright (c) 2012, 2014, 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
26package com.sun.tools.sjavac;
27
28import java.io.File;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Map;
32import java.util.Set;
33
34import com.sun.tools.javac.util.Assert;
35
36/**
37 * The build state class captures the source code and generated artifacts
38 * from a build. There are usually two build states, the previous one (prev),
39 * loaded from the javac_state file, and the current one (now).
40 *
41 *  <p><b>This is NOT part of any supported API.
42 *  If you write code that depends on this, you do so at your own risk.
43 *  This code and its internal interfaces are subject to change or
44 *  deletion without notice.</b>
45 */
46public class BuildState {
47    private Map<String,Module> modules = new HashMap<>();
48    private Map<String,Package> packages = new HashMap<>();
49    private Map<String,Source> sources = new HashMap<>();
50    private Map<String,File> artifacts = new HashMap<>();
51    // Map from package to a set of packages that depend on said package.
52    private Map<String,Set<String>> dependents = new HashMap<>();
53
54    public  Map<String,Module> modules() { return modules; }
55    public  Map<String,Package> packages() { return packages; }
56    public  Map<String,Source> sources() { return sources; }
57    public  Map<String,File> artifacts() { return artifacts; }
58    public  Map<String,Set<String>> dependents() { return dependents; }
59
60    /**
61     * Lookup a module from a name. Create the module if it does
62     * not exist yet.
63     */
64    public Module lookupModule(String mod) {
65        Module m = modules.get(mod);
66        if (m == null) {
67            m = new Module(mod, "???");
68            modules.put(mod, m);
69        }
70        return m;
71    }
72
73    /**
74     * Find a module from a given package name. For example:
75     * The package name "base:java.lang" will fetch the module named "base".
76     * The package name ":java.net" will fetch the default module.
77     */
78    Module findModuleFromPackageName(String pkg) {
79        int cp = pkg.indexOf(':');
80        Assert.check(cp != -1);
81        String mod = pkg.substring(0, cp);
82        return lookupModule(mod);
83    }
84
85    /**
86     * Store references to all packages, sources and artifacts for all modules
87     * into the build state. I.e. flatten the module tree structure
88     * into global maps stored in the BuildState for easy access.
89     *
90     * @param m The set of modules.
91     */
92    public void flattenPackagesSourcesAndArtifacts(Map<String,Module> m) {
93        modules = m;
94        // Extract all the found packages.
95        for (Module i : modules.values()) {
96            for (Map.Entry<String,Package> j : i.packages().entrySet()) {
97                Package p = packages.get(j.getKey());
98                // Check that no two different packages are stored under same name.
99                Assert.check(p == null || p == j.getValue());
100                if (p == null) {
101                    p = j.getValue();
102                    packages.put(j.getKey(),j.getValue());
103                }
104                for (Map.Entry<String,Source> k : p.sources().entrySet()) {
105                    Source s = sources.get(k.getKey());
106                    // Check that no two different sources are stored under same name.
107                    Assert.check(s == null || s == k.getValue());
108                    if (s == null) {
109                        s = k.getValue();
110                        sources.put(k.getKey(), k.getValue());
111                    }
112                }
113                for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
114                    File f = artifacts.get(g.getKey());
115                    // Check that no two artifacts are stored under the same file.
116                    Assert.check(f == null || f == g.getValue());
117                    if (f == null) {
118                        f = g.getValue();
119                        artifacts.put(g.getKey(), g.getValue());
120                    }
121                }
122            }
123        }
124    }
125
126    /**
127     * Store references to all artifacts found in the module tree into the maps
128     * stored in the build state.
129     *
130     * @param m The set of modules.
131     */
132    public void flattenArtifacts(Map<String,Module> m) {
133        modules = m;
134        // Extract all the found packages.
135        for (Module i : modules.values()) {
136            for (Map.Entry<String,Package> j : i.packages().entrySet()) {
137                Package p = packages.get(j.getKey());
138                // Check that no two different packages are stored under same name.
139                Assert.check(p == null || p == j.getValue());
140                p = j.getValue();
141                packages.put(j.getKey(),j.getValue());
142                for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
143                    File f = artifacts.get(g.getKey());
144                    // Check that no two artifacts are stored under the same file.
145                    Assert.check(f == null || f == g.getValue());
146                    artifacts.put(g.getKey(), g.getValue());
147                }
148            }
149        }
150    }
151
152    /**
153     * Calculate the package dependents (ie the reverse of the dependencies).
154     */
155    public void calculateDependents() {
156        dependents = new HashMap<>();
157        for (String s : packages.keySet()) {
158            Package p = packages.get(s);
159            for (String d : p.dependencies()) {
160                Set<String> ss = dependents.get(d);
161                if (ss == null) {
162                    ss = new HashSet<>();
163                    dependents.put(d, ss);
164                }
165                // Add the dependent information to the global dependent map.
166                ss.add(s);
167                Package dp = packages.get(d);
168                // Also add the dependent information to the package specific map.
169                // Normally, you do not compile java.lang et al. Therefore
170                // there are several packages that p depends upon that you
171                // do not have in your state database. This is perfectly fine.
172                if (dp != null) {
173                    // But this package did exist in the state database.
174                    dp.addDependent(p.name());
175                }
176            }
177        }
178    }
179
180    /**
181     * Verify that the setModules method above did the right thing when
182     * running through the module->package->source structure.
183     */
184    public void checkInternalState(String msg, boolean linkedOnly, Map<String,Source> srcs) {
185        boolean baad = false;
186        Map<String,Source> original = new HashMap<>();
187        Map<String,Source> calculated = new HashMap<>();
188
189        for (String s : sources.keySet()) {
190            Source ss = sources.get(s);
191            if (ss.isLinkedOnly() == linkedOnly) {
192                calculated.put(s,ss);
193            }
194        }
195        for (String s : srcs.keySet()) {
196            Source ss = srcs.get(s);
197            if (ss.isLinkedOnly() == linkedOnly) {
198                original.put(s,ss);
199            }
200        }
201        if (original.size() != calculated.size()) {
202            Log.error("INTERNAL ERROR "+msg+" original and calculated are not the same size!");
203            baad = true;
204        }
205        if (!original.keySet().equals(calculated.keySet())) {
206            Log.error("INTERNAL ERROR "+msg+" original and calculated do not have the same domain!");
207            baad = true;
208        }
209        if (!baad) {
210            for (String s : original.keySet()) {
211                Source s1 = original.get(s);
212                Source s2 = calculated.get(s);
213                if (s1 == null || s2 == null || !s1.equals(s2)) {
214                    Log.error("INTERNAL ERROR "+msg+" original and calculated have differing elements for "+s);
215                }
216                baad = true;
217            }
218        }
219        if (baad) {
220            for (String s : original.keySet()) {
221                Source ss = original.get(s);
222                Source sss = calculated.get(s);
223                if (sss == null) {
224                    Log.error("The file "+s+" does not exist in calculated tree of sources.");
225                }
226            }
227            for (String s : calculated.keySet()) {
228                Source ss = calculated.get(s);
229                Source sss = original.get(s);
230                if (sss == null) {
231                    Log.error("The file "+s+" does not exist in original set of found sources.");
232                }
233            }
234        }
235    }
236
237    /**
238     * Load a module from the javac state file.
239     */
240    public Module loadModule(String l) {
241        Module m = Module.load(l);
242        modules.put(m.name(), m);
243        return m;
244    }
245
246    /**
247     * Load a package from the javac state file.
248     */
249    public Package loadPackage(Module lastModule, String l) {
250        Package p = Package.load(lastModule, l);
251        lastModule.addPackage(p);
252        packages.put(p.name(), p);
253        return p;
254    }
255
256    /**
257     * Load a source from the javac state file.
258     */
259    public Source loadSource(Package lastPackage, String l, boolean is_generated) {
260        Source s = Source.load(lastPackage, l, is_generated);
261        lastPackage.addSource(s);
262        sources.put(s.name(), s);
263        return s;
264    }
265
266    /**
267     * During an incremental compile we need to copy the old javac state
268     * information about packages that were not recompiled.
269     */
270    public void copyPackagesExcept(BuildState prev, Set<String> recompiled, Set<String> removed) {
271        for (String pkg : prev.packages().keySet()) {
272            // Do not copy recompiled or removed packages.
273            if (recompiled.contains(pkg) || removed.contains(pkg)) continue;
274            Module mnew = findModuleFromPackageName(pkg);
275            Package pprev = prev.packages().get(pkg);
276            mnew.addPackage(pprev);
277            // Do not forget to update the flattened data.
278            packages.put(pkg, pprev);
279        }
280    }
281}
282