langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Source.java
changeset 34991 ff8be37d1164
parent 31115 8d8e98052d5d
child 36526 3b41f1c69604
equal deleted inserted replaced
34918:80f67512daa1 34991:ff8be37d1164
    24  */
    24  */
    25 
    25 
    26 package com.sun.tools.sjavac;
    26 package com.sun.tools.sjavac;
    27 
    27 
    28 import java.io.File;
    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;
    29 import java.util.Set;
    37 import java.util.Set;
    30 import java.util.Collections;
    38 import java.util.Collections;
    31 import java.util.List;
    39 import java.util.List;
    32 import java.util.ArrayList;
    40 import java.util.ArrayList;
    33 import java.util.Map;
    41 import java.util.Map;
       
    42 import java.util.regex.PatternSyntaxException;
    34 
    43 
    35 /** A Source object maintains information about a source file.
    44 /** A Source object maintains information about a source file.
    36  * For example which package it belongs to and kind of source it is.
    45  * For example which package it belongs to and kind of source it is.
    37  * The class also knows how to find source files (scanRoot) given include/exclude
    46  * The class also knows how to find source files (scanRoot) given include/exclude
    38  * patterns and a root.
    47  * patterns and a root.
    54     private String suffix;
    63     private String suffix;
    55     // When this source file was last_modified
    64     // When this source file was last_modified
    56     private long lastModified;
    65     private long lastModified;
    57     // The source File.
    66     // The source File.
    58     private File file;
    67     private File file;
    59     // The source root under which file resides.
       
    60     private File root;
       
    61     // If the source is generated.
    68     // If the source is generated.
    62     private boolean isGenerated;
    69     private boolean isGenerated;
    63     // If the source is only linked to, not compiled.
    70     // If the source is only linked to, not compiled.
    64     private boolean linkedOnly;
    71     private boolean linkedOnly;
    65 
    72 
    76     @Override
    83     @Override
    77     public int hashCode() {
    84     public int hashCode() {
    78         return name.hashCode();
    85         return name.hashCode();
    79     }
    86     }
    80 
    87 
    81     public Source(Module m, String n, File f, File r) {
    88     public Source(Module m, String n, File f) {
    82         name = n;
    89         name = n;
    83         int dp = n.lastIndexOf(".");
    90         int dp = n.lastIndexOf(".");
    84         if (dp != -1) {
    91         if (dp != -1) {
    85             suffix = n.substring(dp);
    92             suffix = n.substring(dp);
    86         } else {
    93         } else {
    87             suffix = "";
    94             suffix = "";
    88         }
    95         }
    89         file = f;
    96         file = f;
    90         root = r;
       
    91         lastModified = f.lastModified();
    97         lastModified = f.lastModified();
    92         linkedOnly = false;
    98         linkedOnly = false;
    93     }
    99     }
    94 
   100 
    95     public Source(Package p, String n, long lm) {
   101     public Source(Package p, String n, long lm) {
   100             suffix = n.substring(dp);
   106             suffix = n.substring(dp);
   101         } else {
   107         } else {
   102             suffix = "";
   108             suffix = "";
   103         }
   109         }
   104         file = null;
   110         file = null;
   105         root = null;
       
   106         lastModified = lm;
   111         lastModified = lm;
   107         linkedOnly = false;
   112         linkedOnly = false;
   108         int ls = n.lastIndexOf('/');
   113         int ls = n.lastIndexOf('/');
   109     }
   114     }
   110 
   115 
   111     public String name() { return name; }
   116     public String name() { return name; }
   112     public String suffix() { return suffix; }
   117     public String suffix() { return suffix; }
   113     public Package pkg() { return pkg; }
   118     public Package pkg() { return pkg; }
   114     public File   file() { return file; }
   119     public File   file() { return file; }
   115     public File   root() { return root; }
       
   116     public long lastModified() {
   120     public long lastModified() {
   117         return lastModified;
   121         return lastModified;
   118     }
   122     }
   119 
   123 
   120     public void setPackage(Package p) {
   124     public void setPackage(Package p) {
   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 }