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