Source.java revision 2958:27da0c3ac83a
1106813Ssimokawa/*
2113584Ssimokawa * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3106813Ssimokawa * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4106813Ssimokawa *
5106813Ssimokawa * This code is free software; you can redistribute it and/or modify it
6106813Ssimokawa * under the terms of the GNU General Public License version 2 only, as
7106813Ssimokawa * published by the Free Software Foundation.  Oracle designates this
8106813Ssimokawa * particular file as subject to the "Classpath" exception as provided
9106813Ssimokawa * by Oracle in the LICENSE file that accompanied this code.
10106813Ssimokawa *
11106813Ssimokawa * This code is distributed in the hope that it will be useful, but WITHOUT
12106813Ssimokawa * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13106813Ssimokawa * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14106813Ssimokawa * version 2 for more details (a copy is included in the LICENSE file that
15106813Ssimokawa * accompanied this code).
16106813Ssimokawa *
17106813Ssimokawa * You should have received a copy of the GNU General Public License version
18106813Ssimokawa * 2 along with this work; if not, write to the Free Software Foundation,
19106813Ssimokawa * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20106813Ssimokawa *
21106813Ssimokawa * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22106813Ssimokawa * or visit www.oracle.com if you need additional information or have any
23106813Ssimokawa * questions.
24106813Ssimokawa */
25106813Ssimokawa
26106813Ssimokawapackage com.sun.tools.sjavac;
27106813Ssimokawa
28106813Ssimokawaimport java.io.File;
29106813Ssimokawaimport java.util.Set;
30106813Ssimokawaimport java.util.Collections;
31106813Ssimokawaimport java.util.List;
32106813Ssimokawaimport java.util.ArrayList;
33106813Ssimokawaimport java.util.Map;
34106813Ssimokawa
35106813Ssimokawa/** A Source object maintains information about a source file.
36106813Ssimokawa * For example which package it belongs to and kind of source it is.
37106813Ssimokawa * The class also knows how to find source files (scanRoot) given include/exclude
38106813Ssimokawa * patterns and a root.
39106813Ssimokawa *
40106813Ssimokawa *  <p><b>This is NOT part of any supported API.
41106813Ssimokawa *  If you write code that depends on this, you do so at your own risk.
42120660Ssimokawa *  This code and its internal interfaces are subject to change or
43120660Ssimokawa *  deletion without notice.</b>
44120660Ssimokawa */
45120660Ssimokawapublic class Source implements Comparable<Source> {
46120660Ssimokawa    // The package the source belongs to.
47106813Ssimokawa   private Package pkg;
48106813Ssimokawa    // Name of this source file, relative its source root.
49106813Ssimokawa    // For example: java/lang/Object.java
50106813Ssimokawa    // Or if the source file is inside a module:
51106813Ssimokawa    // jdk.base/java/lang/Object.java
52106813Ssimokawa    private String name;
53106813Ssimokawa    // What kind of file is this.
54118455Ssimokawa    private String suffix;
55113584Ssimokawa    // When this source file was last_modified
56106813Ssimokawa    private long lastModified;
57106813Ssimokawa    // The source File.
58106813Ssimokawa    private File file;
59106813Ssimokawa    // The source root under which file resides.
60106813Ssimokawa    private File root;
61113584Ssimokawa    // If the source is generated.
62106813Ssimokawa    private boolean isGenerated;
63109282Ssimokawa    // If the source is only linked to, not compiled.
64106813Ssimokawa    private boolean linkedOnly;
65106813Ssimokawa
66106813Ssimokawa    @Override
67106813Ssimokawa    public boolean equals(Object o) {
68106813Ssimokawa        return (o instanceof Source) && name.equals(((Source)o).name);
69106813Ssimokawa    }
70106813Ssimokawa
71106813Ssimokawa    @Override
72106813Ssimokawa    public int compareTo(Source o) {
73106813Ssimokawa        return name.compareTo(o.name);
74120660Ssimokawa    }
75106813Ssimokawa
76126080Sphk    @Override
77111942Ssimokawa    public int hashCode() {
78126080Sphk        return name.hashCode();
79111815Sphk    }
80111815Sphk
81111815Sphk    public Source(Module m, String n, File f, File r) {
82111815Sphk        name = n;
83111815Sphk        int dp = n.lastIndexOf(".");
84111815Sphk        if (dp != -1) {
85111815Sphk            suffix = n.substring(dp);
86120660Ssimokawa        } else {
87111815Sphk            suffix = "";
88126080Sphk        }
89111942Ssimokawa        file = f;
90111942Ssimokawa        root = r;
91120660Ssimokawa        lastModified = f.lastModified();
92118455Ssimokawa        linkedOnly = false;
93111942Ssimokawa    }
94106813Ssimokawa
95106813Ssimokawa    public Source(Package p, String n, long lm) {
96118293Ssimokawa        pkg = p;
97118293Ssimokawa        name = n;
98118293Ssimokawa        int dp = n.lastIndexOf(".");
99118293Ssimokawa        if (dp != -1) {
100118293Ssimokawa            suffix = n.substring(dp);
101118293Ssimokawa        } else {
102106813Ssimokawa            suffix = "";
103118293Ssimokawa        }
104118293Ssimokawa        file = null;
105118293Ssimokawa        root = null;
106118293Ssimokawa        lastModified = lm;
107118293Ssimokawa        linkedOnly = false;
108118293Ssimokawa        int ls = n.lastIndexOf('/');
109118293Ssimokawa    }
110118293Ssimokawa
111118293Ssimokawa    public String name() { return name; }
112118293Ssimokawa    public String suffix() { return suffix; }
113118293Ssimokawa    public Package pkg() { return pkg; }
114118293Ssimokawa    public File   file() { return file; }
115118293Ssimokawa    public File   root() { return root; }
116118293Ssimokawa    public long lastModified() {
117118293Ssimokawa        return lastModified;
118118293Ssimokawa    }
119118293Ssimokawa
120118293Ssimokawa    public void setPackage(Package p) {
121118293Ssimokawa        pkg = p;
122118293Ssimokawa    }
123118293Ssimokawa
124118293Ssimokawa    public void markAsGenerated() {
125118293Ssimokawa        isGenerated = true;
126118293Ssimokawa    }
127118293Ssimokawa
128118293Ssimokawa    public boolean isGenerated() {
129118293Ssimokawa        return isGenerated;
130118293Ssimokawa    }
131118293Ssimokawa
132118293Ssimokawa    public void markAsLinkedOnly() {
133118293Ssimokawa        linkedOnly = true;
134118293Ssimokawa    }
135118293Ssimokawa
136118293Ssimokawa    public boolean isLinkedOnly() {
137118293Ssimokawa        return linkedOnly;
138118293Ssimokawa    }
139118293Ssimokawa
140118293Ssimokawa    private void save(StringBuilder b) {
141118293Ssimokawa        String CL = linkedOnly?"L":"C";
142118293Ssimokawa        String GS = isGenerated?"G":"S";
143118293Ssimokawa        b.append(GS+" "+CL+" "+name+" "+file.lastModified()+"\n");
144118293Ssimokawa    }
145118293Ssimokawa    // Parse a line that looks like this:
146118293Ssimokawa    // S C /code/alfa/A.java 1357631228000
147118293Ssimokawa    static public Source load(Package lastPackage, String l, boolean isGenerated) {
148118293Ssimokawa        int sp = l.indexOf(' ',4);
149118293Ssimokawa        if (sp == -1) return null;
150118293Ssimokawa        String name = l.substring(4,sp);
151118293Ssimokawa        long last_modified = Long.parseLong(l.substring(sp+1));
152118293Ssimokawa
153118293Ssimokawa        boolean isLinkedOnly = false;
154118293Ssimokawa        if (l.charAt(2) == 'L') {
155118293Ssimokawa            isLinkedOnly = true;
156118293Ssimokawa        } else if (l.charAt(2) == 'C') {
157118293Ssimokawa            isLinkedOnly = false;
158118293Ssimokawa        } else return null;
159118293Ssimokawa
160118293Ssimokawa        Source s = new Source(lastPackage, name, last_modified);
161118293Ssimokawa        s.file = new File(name);
162118293Ssimokawa        if (isGenerated) s.markAsGenerated();
163118293Ssimokawa        if (isLinkedOnly) s.markAsLinkedOnly();
164118293Ssimokawa        return s;
165118293Ssimokawa    }
166118293Ssimokawa
167106813Ssimokawa    public static void saveSources(Map<String,Source> sources, StringBuilder b) {
168106813Ssimokawa        List<String> sorted_sources = new ArrayList<>();
169106813Ssimokawa        for (String key : sources.keySet()) {
170106813Ssimokawa            sorted_sources.add(key);
171122227Ssimokawa        }
172122227Ssimokawa        Collections.sort(sorted_sources);
173122227Ssimokawa        for (String key : sorted_sources) {
174118293Ssimokawa            Source s = sources.get(key);
175118293Ssimokawa            s.save(b);
176118293Ssimokawa        }
177118293Ssimokawa    }
178118455Ssimokawa
179118455Ssimokawa    /**
180118455Ssimokawa     * Recurse into the directory root and find all files matchine the excl/incl/exclfiles/inclfiles rules.
181118455Ssimokawa     * Detects the existence of module-info.java files and presumes that the directory it resides in
182118293Ssimokawa     * is the name of the current module.
183118293Ssimokawa     */
184118293Ssimokawa    static public void scanRoot(File root,
185118455Ssimokawa                                Set<String> suffixes,
186118455Ssimokawa                                List<String> excludes, List<String> includes,
187118293Ssimokawa                                List<String> excludeFiles, List<String> includeFiles,
188118293Ssimokawa                                Map<String,Source> foundFiles,
189118293Ssimokawa                                Map<String,Module> foundModules,
190106813Ssimokawa                                Module currentModule,
191106813Ssimokawa                                boolean permitSourcesWithoutPackage,
192106813Ssimokawa                                boolean inGensrc,
193106813Ssimokawa                                boolean inLinksrc)
194106813Ssimokawa        throws ProblemException {
195106813Ssimokawa
196106813Ssimokawa        if (root == null) return;
197118293Ssimokawa        int root_prefix = root.getPath().length()+1;
198118293Ssimokawa        // This is the root source directory, it must not contain any Java sources files
199106813Ssimokawa        // because we do not allow Java source files without a package.
200106813Ssimokawa        // (Unless of course --permit-sources-without-package has been specified.)
201106813Ssimokawa        // It might contain other source files however, (for -tr and -copy) these will
202106813Ssimokawa        // always be included, since no package pattern can match the root directory.
203106813Ssimokawa        currentModule = addFilesInDir(root, root_prefix, root, suffixes, permitSourcesWithoutPackage,
204106813Ssimokawa                                       excludeFiles, includeFiles,
205106813Ssimokawa                                       foundFiles, foundModules, currentModule,
206106813Ssimokawa                                       inGensrc, inLinksrc);
207106813Ssimokawa
208118293Ssimokawa        File[] dirfiles = root.listFiles();
209118293Ssimokawa        for (File d : dirfiles) {
210118293Ssimokawa            if (d.isDirectory()) {
211118293Ssimokawa                // Descend into the directory structure.
212118293Ssimokawa                scanDirectory(d, root_prefix, root, suffixes,
213118293Ssimokawa                              excludes, includes, excludeFiles, includeFiles,
214118293Ssimokawa                              foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
215118293Ssimokawa            }
216118293Ssimokawa        }
217118293Ssimokawa    }
218118293Ssimokawa
219118293Ssimokawa    /**
220118293Ssimokawa     * Test if a path matches any of the patterns given.
221118293Ssimokawa     * The pattern foo/bar matches only foo/bar
222118293Ssimokawa     * The pattern foo/* matches foo/bar and foo/bar/zoo etc
223118293Ssimokawa     */
224118293Ssimokawa    static private boolean hasMatch(String path, List<String> patterns) {
225118293Ssimokawa
226118293Ssimokawa        // Convert Windows '\' to '/' for the sake of comparing with the patterns
227118293Ssimokawa        path = path.replace(File.separatorChar, '/');
228118293Ssimokawa
229118293Ssimokawa        for (String p : patterns) {
230118293Ssimokawa            // Exact match
231118293Ssimokawa            if (p.equals(path))
232118293Ssimokawa                return true;
233118293Ssimokawa
234118293Ssimokawa            // Single dot the end matches this package and all its subpackages.
235118293Ssimokawa            if (p.endsWith("/*")) {
236118293Ssimokawa                // Remove the wildcard
237118293Ssimokawa                String patprefix = p.substring(0,p.length()-2);
238118293Ssimokawa                // Does the path start with the pattern prefix?
239118293Ssimokawa                if (path.startsWith(patprefix)) {
240118293Ssimokawa                    // If the path has the same length as the pattern prefix, then it is a match.
241118293Ssimokawa                    // If the path is longer, then make sure that
242106813Ssimokawa                    // the next part of the path starts with a dot (.) to prevent
243118293Ssimokawa                    // wildcard matching in the middle of a package name.
244118293Ssimokawa                    if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='/') {
245106813Ssimokawa                        return true;
246118293Ssimokawa                    }
247118293Ssimokawa                }
248118293Ssimokawa            }
249118293Ssimokawa        }
250118293Ssimokawa        return false;
251118293Ssimokawa    }
252118293Ssimokawa
253118293Ssimokawa    /**
254118293Ssimokawa     * Matches patterns with the asterisk first. */
255118293Ssimokawa     // The pattern foo/bar.java only matches foo/bar.java
256118293Ssimokawa     // The pattern */bar.java matches foo/bar.java and zoo/bar.java etc
257106813Ssimokawa    static private boolean hasFileMatch(String path, List<String> patterns) {
258118293Ssimokawa        // Convert Windows '\' to '/' for the sake of comparing with the patterns
259118293Ssimokawa        path = path.replace(File.separatorChar, '/');
260106813Ssimokawa
261106813Ssimokawa        path = Util.normalizeDriveLetter(path);
262106813Ssimokawa        for (String p : patterns) {
263106813Ssimokawa            // Exact match
264106813Ssimokawa            if (p.equals(path)) {
265106813Ssimokawa                return true;
266106813Ssimokawa            }
267106813Ssimokawa            // Single dot the end matches this package and all its subpackages.
268106813Ssimokawa            if (p.startsWith("*")) {
269106813Ssimokawa                // Remove the wildcard
270106813Ssimokawa                String patsuffix = p.substring(1);
271106813Ssimokawa                // Does the path start with the pattern prefix?
272106813Ssimokawa                if (path.endsWith(patsuffix)) {
273106813Ssimokawa                    return true;
274106813Ssimokawa                }
275106813Ssimokawa            }
276106813Ssimokawa        }
277106813Ssimokawa        return false;
278120660Ssimokawa    }
279106813Ssimokawa
280106813Ssimokawa    /**
281106813Ssimokawa     * Add the files in the directory, assuming that the file has not been excluded.
282118293Ssimokawa     * Returns a fresh Module object, if this was a dir with a module-info.java file.
283118293Ssimokawa     */
284118293Ssimokawa    static private Module addFilesInDir(File dir, int rootPrefix, File root,
285106813Ssimokawa                                        Set<String> suffixes, boolean allow_javas,
286106813Ssimokawa                                        List<String> excludeFiles, List<String> includeFiles,
287106813Ssimokawa                                        Map<String,Source> foundFiles,
288113584Ssimokawa                                        Map<String,Module> foundModules,
289109988Ssimokawa                                        Module currentModule,
290106813Ssimokawa                                        boolean inGensrc,
291109988Ssimokawa                                        boolean inLinksrc)
292106813Ssimokawa        throws ProblemException
293106813Ssimokawa    {
294106813Ssimokawa        for (File f : dir.listFiles()) {
295106813Ssimokawa
296106813Ssimokawa            if (!f.isFile())
297106813Ssimokawa                continue;
298109988Ssimokawa
299109988Ssimokawa            boolean should_add =
300109988Ssimokawa                (excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles))
301106813Ssimokawa                && (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles));
302106813Ssimokawa
303111748Sdes            if (!should_add)
304109988Ssimokawa                continue;
305109988Ssimokawa
306109988Ssimokawa            if (!allow_javas && f.getName().endsWith(".java")) {
307109988Ssimokawa                throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+
308106813Ssimokawa                                           ", please remove "+f.getName());
309109988Ssimokawa            }
310109988Ssimokawa            // Extract the file name relative the root.
311120660Ssimokawa            String fn = f.getPath().substring(rootPrefix);
312113584Ssimokawa            // Extract the package name.
313106813Ssimokawa            int sp = fn.lastIndexOf(File.separatorChar);
314106813Ssimokawa            String pkg = "";
315106813Ssimokawa            if (sp != -1) {
316106813Ssimokawa                pkg = fn.substring(0,sp).replace(File.separatorChar,'.');
317120660Ssimokawa            }
318120660Ssimokawa            // Is this a module-info.java file?
319106813Ssimokawa            if (fn.endsWith("module-info.java")) {
320120660Ssimokawa                // Aha! We have recursed into a module!
321120660Ssimokawa                if (!currentModule.name().equals("")) {
322113584Ssimokawa                    throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn);
323120660Ssimokawa                }
324106813Ssimokawa                String module_name = fn.substring(0,fn.length()-16);
325109988Ssimokawa                currentModule = new Module(module_name, f.getPath());
326109988Ssimokawa                foundModules.put(module_name, currentModule);
327113584Ssimokawa            }
328113584Ssimokawa            // Extract the suffix.
329106813Ssimokawa            int dp = fn.lastIndexOf(".");
330106813Ssimokawa            String suffix = "";
331113584Ssimokawa            if (dp > 0) {
332106813Ssimokawa                suffix = fn.substring(dp);
333106813Ssimokawa            }
334106813Ssimokawa            // Should the file be added?
335109988Ssimokawa            if (suffixes.contains(suffix)) {
336113584Ssimokawa                Source of = foundFiles.get(f.getPath());
337106813Ssimokawa                if (of != null) {
338106813Ssimokawa                    throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath());
339106813Ssimokawa                }
340106813Ssimokawa                of = currentModule.lookupSource(f.getPath());
341106813Ssimokawa                if (of != null) {
342118293Ssimokawa                    // Oups, the source is already added, could be ok, could be not, lets check.
343106813Ssimokawa                    if (inLinksrc) {
344106813Ssimokawa                        // So we are collecting sources for linking only.
345109988Ssimokawa                        if (of.isLinkedOnly()) {
346109988Ssimokawa                            // Ouch, this one is also for linking only. Bad.
347109988Ssimokawa                            throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath());
348109988Ssimokawa                        }
349106813Ssimokawa                        // Ok, the existing source is to be compiled. Thus this link only is redundant
350106813Ssimokawa                        // since all compiled are also linked to. Continue to the next source.
351106813Ssimokawa                        // But we need to add the source, so that it will be visible to linking,
352106813Ssimokawa                        // if not the multi core compile will fail because a JavaCompiler cannot
353106813Ssimokawa                        // find the necessary dependencies for its part of the source.
354106813Ssimokawa                        foundFiles.put(f.getPath(), of);
355106813Ssimokawa                        continue;
356106813Ssimokawa                    } else {
357106813Ssimokawa                        // We are looking for sources to compile, if we find an existing to be compiled
358106813Ssimokawa                        // source with the same name, it is an internal error, since we must
359106813Ssimokawa                        // find the sources to be compiled before we find the sources to be linked to.
360106813Ssimokawa                        throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath());
361106813Ssimokawa                    }
362106813Ssimokawa                }
363106813Ssimokawa                Source s = new Source(currentModule, f.getPath(), f, root);
364106813Ssimokawa                if (inGensrc) s.markAsGenerated();
365120660Ssimokawa                if (inLinksrc) {
366106813Ssimokawa                    s.markAsLinkedOnly();
367106813Ssimokawa                }
368106813Ssimokawa                pkg = currentModule.name()+":"+pkg;
369118293Ssimokawa                foundFiles.put(f.getPath(), s);
370118293Ssimokawa                currentModule.addSource(pkg, s);
371118293Ssimokawa            }
372106813Ssimokawa        }
373113802Ssimokawa        return currentModule;
374113802Ssimokawa    }
375113802Ssimokawa
376106813Ssimokawa    static private void scanDirectory(File dir, int rootPrefix, File root,
377113802Ssimokawa                                      Set<String> suffixes,
378106813Ssimokawa                                      List<String> excludes, List<String> includes,
379113802Ssimokawa                                      List<String> excludeFiles, List<String> includeFiles,
380113802Ssimokawa                                      Map<String,Source> foundFiles,
381113802Ssimokawa                                      Map<String,Module> foundModules,
382118293Ssimokawa                                      Module currentModule, boolean inGensrc, boolean inLinksrc)
383113802Ssimokawa        throws ProblemException {
384113802Ssimokawa
385113802Ssimokawa        String path = "";
386113802Ssimokawa        // Remove the root prefix from the dir path
387113802Ssimokawa        if (dir.getPath().length() > rootPrefix) {
388109988Ssimokawa            path = dir.getPath().substring(rootPrefix);
389113802Ssimokawa        }
390113802Ssimokawa        // Should this package directory be included and not excluded?
391106813Ssimokawa        if ((includes==null || includes.isEmpty() || hasMatch(path, includes)) &&
392106813Ssimokawa            (excludes==null || excludes.isEmpty() || !hasMatch(path, excludes))) {
393106813Ssimokawa            // Add the source files.
394113802Ssimokawa            currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles,
395113802Ssimokawa                                          foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
396113802Ssimokawa        }
397113802Ssimokawa
398113802Ssimokawa        for (File d : dir.listFiles()) {
399113802Ssimokawa            if (d.isDirectory()) {
400113802Ssimokawa                // Descend into the directory structure.
401113802Ssimokawa                scanDirectory(d, rootPrefix, root, suffixes,
402113802Ssimokawa                              excludes, includes, excludeFiles, includeFiles,
403113802Ssimokawa                              foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
404113802Ssimokawa            }
405118293Ssimokawa        }
406113802Ssimokawa    }
407113802Ssimokawa}
408113802Ssimokawa