1 /* 2 * Copyright (c) 2012, 2016, 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.io.IOException; 30 import java.nio.file.FileSystem; 31 import java.nio.file.FileVisitResult; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.PathMatcher; 35 import java.nio.file.SimpleFileVisitor; 36 import java.nio.file.attribute.BasicFileAttributes; 37 import java.util.Set; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.ArrayList; 41 import java.util.Map; 42 import java.util.regex.PatternSyntaxException; 43 44 /** A Source object maintains information about a source file. 45 * For example which package it belongs to and kind of source it is. 46 * The class also knows how to find source files (scanRoot) given include/exclude 47 * patterns and a root. 48 * 49 * <p><b>This is NOT part of any supported API. 50 * If you write code that depends on this, you do so at your own risk. 51 * This code and its internal interfaces are subject to change or 52 * deletion without notice.</b> 53 */ 54 public class Source implements Comparable<Source> { 55 // The package the source belongs to. 56 private Package pkg; 57 // Name of this source file, relative its source root. 58 // For example: java/lang/Object.java 59 // Or if the source file is inside a module: 60 // jdk.base/java/lang/Object.java 61 private String name; 62 // What kind of file is this. 63 private String suffix; 64 // When this source file was last_modified 65 private long lastModified; 66 // The source File. 67 private File file; 68 // If the source is generated. 69 private boolean isGenerated; 70 // If the source is only linked to, not compiled. 71 private boolean linkedOnly; 72 73 @Override 74 public boolean equals(Object o) { 75 return (o instanceof Source) && name.equals(((Source)o).name); 76 } 77 78 @Override 79 public int compareTo(Source o) { 80 return name.compareTo(o.name); 81 } 82 83 @Override 84 public int hashCode() { 85 return name.hashCode(); 86 } 87 88 public Source(Module m, String n, File f) { 89 name = n; 90 int dp = n.lastIndexOf("."); 91 if (dp != -1) { 92 suffix = n.substring(dp); 93 } else { 94 suffix = ""; 95 } 96 file = f; 97 lastModified = f.lastModified(); 98 linkedOnly = false; 99 } 100 101 public Source(Package p, String n, long lm) { 102 pkg = p; 103 name = n; 104 int dp = n.lastIndexOf("."); 105 if (dp != -1) { 106 suffix = n.substring(dp); 107 } else { 108 suffix = ""; 109 } 110 file = null; 111 lastModified = lm; 112 linkedOnly = false; 113 int ls = n.lastIndexOf('/'); 114 } 115 116 public String name() { return name; } 117 public String suffix() { return suffix; } 118 public Package pkg() { return pkg; } 119 public File file() { return file; } 120 public long lastModified() { 121 return lastModified; 122 } 123 124 public void setPackage(Package p) { 125 pkg = p; 126 } 127 128 public void markAsGenerated() { 129 isGenerated = true; 130 } 131 132 public boolean isGenerated() { 133 return isGenerated; 134 } 135 136 public void markAsLinkedOnly() { 137 linkedOnly = true; 138 } 139 140 public boolean isLinkedOnly() { 141 return linkedOnly; 142 } 143 144 private void save(StringBuilder b) { 145 String CL = linkedOnly?"L":"C"; 146 String GS = isGenerated?"G":"S"; 147 b.append(GS+" "+CL+" "+name+" "+file.lastModified()+"\n"); 148 } 149 // Parse a line that looks like this: 150 // S C /code/alfa/A.java 1357631228000 151 static public Source load(Package lastPackage, String l, boolean isGenerated) { 152 int sp = l.indexOf(' ',4); 153 if (sp == -1) return null; 154 String name = l.substring(4,sp); 155 long last_modified = Long.parseLong(l.substring(sp+1)); 156 157 boolean isLinkedOnly = false; 158 if (l.charAt(2) == 'L') { 159 isLinkedOnly = true; 160 } else if (l.charAt(2) == 'C') { 161 isLinkedOnly = false; 162 } else return null; 163 164 Source s = new Source(lastPackage, name, last_modified); 165 s.file = new File(name); 166 if (isGenerated) s.markAsGenerated(); 167 if (isLinkedOnly) s.markAsLinkedOnly(); 168 return s; 169 } 170 171 public static void saveSources(Map<String,Source> sources, StringBuilder b) { 172 List<String> sorted_sources = new ArrayList<>(); 173 for (String key : sources.keySet()) { 174 sorted_sources.add(key); 175 } 176 Collections.sort(sorted_sources); 177 for (String key : sorted_sources) { 178 Source s = sources.get(key); 179 s.save(b); 180 } 181 } 182 183 /** 184 * Recurse into the directory root and find all files matching the excl/incl/exclfiles/inclfiles rules. 185 * Detects the existence of module-info.java files and presumes that the directory it resides in 186 * is the name of the current module. 187 */ 188 static public void scanRoot(File root, 189 Set<String> suffixes, 190 List<String> excludes, 191 List<String> includes, 192 Map<String,Source> foundFiles, 193 Map<String,Module> foundModules, 194 final Module currentModule, 195 boolean permitSourcesWithoutPackage, 196 boolean inGensrc, 197 boolean inLinksrc) 198 throws IOException, ProblemException { 199 200 if (root == null) 201 return; 202 203 FileSystem fs = root.toPath().getFileSystem(); 204 205 if (includes.isEmpty()) { 206 includes = Collections.singletonList("**"); 207 } 208 209 List<PathMatcher> includeMatchers = createPathMatchers(fs, includes); 210 List<PathMatcher> excludeMatchers = createPathMatchers(fs, excludes); 211 212 Files.walkFileTree(root.toPath(), new SimpleFileVisitor<Path>() { 213 @Override 214 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 215 216 Path relToRoot = root.toPath().relativize(file); 217 218 if (includeMatchers.stream().anyMatch(im -> im.matches(relToRoot)) 219 && excludeMatchers.stream().noneMatch(em -> em.matches(relToRoot)) 220 && suffixes.contains(Util.fileSuffix(file))) { 221 222 // TODO: Test this. 223 Source existing = foundFiles.get(file); 224 if (existing != null) { 225 throw new IOException("You have already added the file "+file+" from "+existing.file().getPath()); 226 } 227 existing = currentModule.lookupSource(file.toString()); 228 if (existing != null) { 229 230 // Oops, the source is already added, could be ok, could be not, lets check. 231 if (inLinksrc) { 232 // So we are collecting sources for linking only. 233 if (existing.isLinkedOnly()) { 234 // Ouch, this one is also for linking only. Bad. 235 throw new IOException("You have already added the link only file " + file + " from " + existing.file().getPath()); 236 } 237 // Ok, the existing source is to be compiled. Thus this link only is redundant 238 // since all compiled are also linked to. Continue to the next source. 239 // But we need to add the source, so that it will be visible to linking, 240 // if not the multi core compile will fail because a JavaCompiler cannot 241 // find the necessary dependencies for its part of the source. 242 foundFiles.put(file.toString(), existing); 243 } else { 244 // We are looking for sources to compile, if we find an existing to be compiled 245 // source with the same name, it is an internal error, since we must 246 // find the sources to be compiled before we find the sources to be linked to. 247 throw new IOException("Internal error: Double add of file " + file + " from " + existing.file().getPath()); 248 } 249 250 } else { 251 252 ////////////////////////////////////////////////////////////// 253 // Add source 254 Source s = new Source(currentModule, file.toString(), file.toFile()); 255 if (inGensrc) { 256 s.markAsGenerated(); 257 } 258 if (inLinksrc) { 259 s.markAsLinkedOnly(); 260 } 261 String pkg = packageOfJavaFile(root.toPath(), file); 262 pkg = currentModule.name() + ":" + pkg; 263 foundFiles.put(file.toString(), s); 264 currentModule.addSource(pkg, s); 265 ////////////////////////////////////////////////////////////// 266 } 267 } 268 269 return FileVisitResult.CONTINUE; 270 } 271 }); 272 } 273 274 private static List<PathMatcher> createPathMatchers(FileSystem fs, List<String> patterns) { 275 List<PathMatcher> matchers = new ArrayList<>(); 276 for (String pattern : patterns) { 277 try { 278 matchers.add(fs.getPathMatcher("glob:" + pattern)); 279 } catch (PatternSyntaxException e) { 280 Log.error("Invalid pattern: " + pattern); 281 throw e; 282 } 283 } 284 return matchers; 285 } 286 287 private static String packageOfJavaFile(Path sourceRoot, Path javaFile) { 288 Path javaFileDir = javaFile.getParent(); 289 Path packageDir = sourceRoot.relativize(javaFileDir); 290 List<String> separateDirs = new ArrayList<>(); 291 for (Path pathElement : packageDir) { 292 separateDirs.add(pathElement.getFileName().toString()); 293 } 294 return String.join(".", separateDirs); 295 } 296 297 @Override 298 public String toString() { 299 return String.format("%s[pkg: %s, name: %s, suffix: %s, file: %s, isGenerated: %b, linkedOnly: %b]", 300 getClass().getSimpleName(), 301 pkg, 302 name, 303 suffix, 304 file, 305 isGenerated, 306 linkedOnly); 307 } 308 }