181 * Detects the existence of module-info.java files and presumes that the directory it resides in |
185 * Detects the existence of module-info.java files and presumes that the directory it resides in |
182 * is the name of the current module. |
186 * is the name of the current module. |
183 */ |
187 */ |
184 static public void scanRoot(File root, |
188 static public void scanRoot(File root, |
185 Set<String> suffixes, |
189 Set<String> suffixes, |
186 List<String> excludes, List<String> includes, |
190 List<String> excludes, |
187 List<String> excludeFiles, List<String> includeFiles, |
191 List<String> includes, |
188 Map<String,Source> foundFiles, |
192 Map<String,Source> foundFiles, |
189 Map<String,Module> foundModules, |
193 Map<String,Module> foundModules, |
190 Module currentModule, |
194 final Module currentModule, |
191 boolean permitSourcesWithoutPackage, |
195 boolean permitSourcesWithoutPackage, |
192 boolean inGensrc, |
196 boolean inGensrc, |
193 boolean inLinksrc) |
197 boolean inLinksrc) |
194 throws ProblemException { |
198 throws IOException, ProblemException { |
195 |
199 |
196 if (root == null) return; |
200 if (root == null) |
197 int root_prefix = root.getPath().length()+1; |
201 return; |
198 // This is the root source directory, it must not contain any Java sources files |
202 |
199 // because we do not allow Java source files without a package. |
203 FileSystem fs = root.toPath().getFileSystem(); |
200 // (Unless of course --permit-sources-without-package has been specified.) |
204 |
201 // It might contain other source files however, (for -tr and -copy) these will |
205 if (includes.isEmpty()) { |
202 // always be included, since no package pattern can match the root directory. |
206 includes = Collections.singletonList("**"); |
203 currentModule = addFilesInDir(root, root_prefix, root, suffixes, permitSourcesWithoutPackage, |
207 } |
204 excludeFiles, includeFiles, |
208 |
205 foundFiles, foundModules, currentModule, |
209 List<PathMatcher> includeMatchers = createPathMatchers(fs, includes); |
206 inGensrc, inLinksrc); |
210 List<PathMatcher> excludeMatchers = createPathMatchers(fs, excludes); |
207 |
211 |
208 File[] dirfiles = root.listFiles(); |
212 Files.walkFileTree(root.toPath(), new SimpleFileVisitor<Path>() { |
209 for (File d : dirfiles) { |
213 @Override |
210 if (d.isDirectory()) { |
214 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
211 // Descend into the directory structure. |
215 |
212 scanDirectory(d, root_prefix, root, suffixes, |
216 Path relToRoot = root.toPath().relativize(file); |
213 excludes, includes, excludeFiles, includeFiles, |
217 |
214 foundFiles, foundModules, currentModule, inGensrc, inLinksrc); |
218 if (includeMatchers.stream().anyMatch(im -> im.matches(relToRoot)) |
215 } |
219 && excludeMatchers.stream().noneMatch(em -> em.matches(relToRoot)) |
216 } |
220 && suffixes.contains(Util.fileSuffix(file))) { |
217 } |
221 |
218 |
222 // TODO: Test this. |
219 /** |
223 Source existing = foundFiles.get(file); |
220 * Test if a path matches any of the patterns given. |
224 if (existing != null) { |
221 * The pattern foo/bar matches only foo/bar |
225 throw new IOException("You have already added the file "+file+" from "+existing.file().getPath()); |
222 * The pattern foo/* matches foo/bar and foo/bar/zoo etc |
226 } |
223 */ |
227 existing = currentModule.lookupSource(file.toString()); |
224 static private boolean hasMatch(String path, List<String> patterns) { |
228 if (existing != null) { |
225 |
229 |
226 // Convert Windows '\' to '/' for the sake of comparing with the patterns |
230 // Oups, the source is already added, could be ok, could be not, lets check. |
227 path = path.replace(File.separatorChar, '/'); |
231 if (inLinksrc) { |
228 |
232 // So we are collecting sources for linking only. |
229 for (String p : patterns) { |
233 if (existing.isLinkedOnly()) { |
230 // Exact match |
234 // Ouch, this one is also for linking only. Bad. |
231 if (p.equals(path)) |
235 throw new IOException("You have already added the link only file " + file + " from " + existing.file().getPath()); |
232 return true; |
236 } |
233 |
237 // Ok, the existing source is to be compiled. Thus this link only is redundant |
234 // Single dot the end matches this package and all its subpackages. |
238 // since all compiled are also linked to. Continue to the next source. |
235 if (p.endsWith("/*")) { |
239 // But we need to add the source, so that it will be visible to linking, |
236 // Remove the wildcard |
240 // if not the multi core compile will fail because a JavaCompiler cannot |
237 String patprefix = p.substring(0,p.length()-2); |
241 // find the necessary dependencies for its part of the source. |
238 // Does the path start with the pattern prefix? |
242 foundFiles.put(file.toString(), existing); |
239 if (path.startsWith(patprefix)) { |
243 } else { |
240 // If the path has the same length as the pattern prefix, then it is a match. |
244 // We are looking for sources to compile, if we find an existing to be compiled |
241 // If the path is longer, then make sure that |
245 // source with the same name, it is an internal error, since we must |
242 // the next part of the path starts with a dot (.) to prevent |
246 // find the sources to be compiled before we find the sources to be linked to. |
243 // wildcard matching in the middle of a package name. |
247 throw new IOException("Internal error: Double add of file " + file + " from " + existing.file().getPath()); |
244 if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='/') { |
248 } |
245 return true; |
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 ////////////////////////////////////////////////////////////// |
246 } |
266 } |
247 } |
267 } |
|
268 |
|
269 return FileVisitResult.CONTINUE; |
248 } |
270 } |
249 } |
271 }); |
250 return false; |
272 } |
251 } |
273 |
252 |
274 private static List<PathMatcher> createPathMatchers(FileSystem fs, List<String> patterns) { |
253 /** |
275 List<PathMatcher> matchers = new ArrayList<>(); |
254 * Matches patterns with the asterisk first. */ |
276 for (String pattern : patterns) { |
255 // The pattern foo/bar.java only matches foo/bar.java |
277 try { |
256 // The pattern */bar.java matches foo/bar.java and zoo/bar.java etc |
278 matchers.add(fs.getPathMatcher("glob:" + pattern)); |
257 static private boolean hasFileMatch(String path, List<String> patterns) { |
279 } catch (PatternSyntaxException e) { |
258 // Convert Windows '\' to '/' for the sake of comparing with the patterns |
280 Log.error("Invalid pattern: " + pattern); |
259 path = path.replace(File.separatorChar, '/'); |
281 throw e; |
260 |
|
261 path = Util.normalizeDriveLetter(path); |
|
262 for (String p : patterns) { |
|
263 // Exact match |
|
264 if (p.equals(path)) { |
|
265 return true; |
|
266 } |
282 } |
267 // Single dot the end matches this package and all its subpackages. |
283 } |
268 if (p.startsWith("*")) { |
284 return matchers; |
269 // Remove the wildcard |
285 } |
270 String patsuffix = p.substring(1); |
286 |
271 // Does the path start with the pattern prefix? |
287 private static String packageOfJavaFile(Path sourceRoot, Path javaFile) { |
272 if (path.endsWith(patsuffix)) { |
288 Path javaFileDir = javaFile.getParent(); |
273 return true; |
289 Path packageDir = sourceRoot.relativize(javaFileDir); |
274 } |
290 List<String> separateDirs = new ArrayList<>(); |
275 } |
291 for (Path pathElement : packageDir) { |
276 } |
292 separateDirs.add(pathElement.getFileName().toString()); |
277 return false; |
293 } |
278 } |
294 return String.join(".", separateDirs); |
279 |
295 } |
280 /** |
296 |
281 * Add the files in the directory, assuming that the file has not been excluded. |
297 @Override |
282 * Returns a fresh Module object, if this was a dir with a module-info.java file. |
298 public String toString() { |
283 */ |
299 return String.format("%s[pkg: %s, name: %s, suffix: %s, file: %s, isGenerated: %b, linkedOnly: %b]", |
284 static private Module addFilesInDir(File dir, int rootPrefix, File root, |
300 getClass().getSimpleName(), |
285 Set<String> suffixes, boolean allow_javas, |
301 pkg, |
286 List<String> excludeFiles, List<String> includeFiles, |
302 name, |
287 Map<String,Source> foundFiles, |
303 suffix, |
288 Map<String,Module> foundModules, |
304 file, |
289 Module currentModule, |
305 isGenerated, |
290 boolean inGensrc, |
306 linkedOnly); |
291 boolean inLinksrc) |
|
292 throws ProblemException |
|
293 { |
|
294 for (File f : dir.listFiles()) { |
|
295 |
|
296 if (!f.isFile()) |
|
297 continue; |
|
298 |
|
299 boolean should_add = |
|
300 (excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles)) |
|
301 && (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles)); |
|
302 |
|
303 if (!should_add) |
|
304 continue; |
|
305 |
|
306 if (!allow_javas && f.getName().endsWith(".java")) { |
|
307 throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+ |
|
308 ", please remove "+f.getName()); |
|
309 } |
|
310 // Extract the file name relative the root. |
|
311 String fn = f.getPath().substring(rootPrefix); |
|
312 // Extract the package name. |
|
313 int sp = fn.lastIndexOf(File.separatorChar); |
|
314 String pkg = ""; |
|
315 if (sp != -1) { |
|
316 pkg = fn.substring(0,sp).replace(File.separatorChar,'.'); |
|
317 } |
|
318 // Is this a module-info.java file? |
|
319 if (fn.endsWith("module-info.java")) { |
|
320 // Aha! We have recursed into a module! |
|
321 if (!currentModule.name().equals("")) { |
|
322 throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn); |
|
323 } |
|
324 String module_name = fn.substring(0,fn.length()-16); |
|
325 currentModule = new Module(module_name, f.getPath()); |
|
326 foundModules.put(module_name, currentModule); |
|
327 } |
|
328 // Extract the suffix. |
|
329 int dp = fn.lastIndexOf("."); |
|
330 String suffix = ""; |
|
331 if (dp > 0) { |
|
332 suffix = fn.substring(dp); |
|
333 } |
|
334 // Should the file be added? |
|
335 if (suffixes.contains(suffix)) { |
|
336 Source of = foundFiles.get(f.getPath()); |
|
337 if (of != null) { |
|
338 throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath()); |
|
339 } |
|
340 of = currentModule.lookupSource(f.getPath()); |
|
341 if (of != null) { |
|
342 // Oups, the source is already added, could be ok, could be not, lets check. |
|
343 if (inLinksrc) { |
|
344 // So we are collecting sources for linking only. |
|
345 if (of.isLinkedOnly()) { |
|
346 // Ouch, this one is also for linking only. Bad. |
|
347 throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath()); |
|
348 } |
|
349 // Ok, the existing source is to be compiled. Thus this link only is redundant |
|
350 // since all compiled are also linked to. Continue to the next source. |
|
351 // But we need to add the source, so that it will be visible to linking, |
|
352 // if not the multi core compile will fail because a JavaCompiler cannot |
|
353 // find the necessary dependencies for its part of the source. |
|
354 foundFiles.put(f.getPath(), of); |
|
355 continue; |
|
356 } else { |
|
357 // We are looking for sources to compile, if we find an existing to be compiled |
|
358 // source with the same name, it is an internal error, since we must |
|
359 // find the sources to be compiled before we find the sources to be linked to. |
|
360 throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath()); |
|
361 } |
|
362 } |
|
363 Source s = new Source(currentModule, f.getPath(), f, root); |
|
364 if (inGensrc) s.markAsGenerated(); |
|
365 if (inLinksrc) { |
|
366 s.markAsLinkedOnly(); |
|
367 } |
|
368 pkg = currentModule.name()+":"+pkg; |
|
369 foundFiles.put(f.getPath(), s); |
|
370 currentModule.addSource(pkg, s); |
|
371 } |
|
372 } |
|
373 return currentModule; |
|
374 } |
|
375 |
|
376 static private void scanDirectory(File dir, int rootPrefix, File root, |
|
377 Set<String> suffixes, |
|
378 List<String> excludes, List<String> includes, |
|
379 List<String> excludeFiles, List<String> includeFiles, |
|
380 Map<String,Source> foundFiles, |
|
381 Map<String,Module> foundModules, |
|
382 Module currentModule, boolean inGensrc, boolean inLinksrc) |
|
383 throws ProblemException { |
|
384 |
|
385 String path = ""; |
|
386 // Remove the root prefix from the dir path |
|
387 if (dir.getPath().length() > rootPrefix) { |
|
388 path = dir.getPath().substring(rootPrefix); |
|
389 } |
|
390 // Should this package directory be included and not excluded? |
|
391 if ((includes==null || includes.isEmpty() || hasMatch(path, includes)) && |
|
392 (excludes==null || excludes.isEmpty() || !hasMatch(path, excludes))) { |
|
393 // Add the source files. |
|
394 currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles, |
|
395 foundFiles, foundModules, currentModule, inGensrc, inLinksrc); |
|
396 } |
|
397 |
|
398 for (File d : dir.listFiles()) { |
|
399 if (d.isDirectory()) { |
|
400 // Descend into the directory structure. |
|
401 scanDirectory(d, rootPrefix, root, suffixes, |
|
402 excludes, includes, excludeFiles, includeFiles, |
|
403 foundFiles, foundModules, currentModule, inGensrc, inLinksrc); |
|
404 } |
|
405 } |
|
406 } |
307 } |
407 } |
308 } |