langtools/src/share/classes/com/sun/tools/sjavac/Main.java
changeset 24067 76e7b6bbbd85
parent 22163 3651128c74eb
child 25299 b4a7dcd657f5
equal deleted inserted replaced
24066:1dfb66929538 24067:76e7b6bbbd85
     1 /*
     1 /*
     2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package com.sun.tools.sjavac;
    26 package com.sun.tools.sjavac;
    27 
    27 
    28 import java.io.File;
       
    29 import java.io.IOException;
    28 import java.io.IOException;
    30 import java.io.PrintStream;
    29 import java.io.PrintStream;
    31 import java.util.*;
    30 import java.util.*;
    32 import java.util.regex.Matcher;
    31 import java.nio.file.Path;
    33 import java.util.regex.Pattern;
    32 import java.nio.file.Files;
    34 
    33 
       
    34 import com.sun.tools.sjavac.options.Options;
       
    35 import com.sun.tools.sjavac.options.SourceLocation;
    35 import com.sun.tools.sjavac.server.JavacServer;
    36 import com.sun.tools.sjavac.server.JavacServer;
    36 
    37 
    37 /**
    38 /**
    38  * The main class of the smart javac wrapper tool.
    39  * The main class of the smart javac wrapper tool.
    39  *
    40  *
   149          sjavac -x "com.foo.*" -src root1:root2:root3  )
   150          sjavac -x "com.foo.*" -src root1:root2:root3  )
   150 
   151 
   151         The resulting classes are written into bin.
   152         The resulting classes are written into bin.
   152     */
   153     */
   153 
   154 
   154     // This is the final destination for classes and copied files.
       
   155     private File bin_dir;
       
   156     // This is where the annotation process will put generated sources.
       
   157     private File gensrc_dir;
       
   158     // This is where javac -h puts the generated c-header files.
       
   159     private File header_dir;
       
   160 
       
   161     // This file contains the list of sources genereated by the makefile.
       
   162     // We double check that our calculated list of sources matches this list,
       
   163     // if not, then we terminate with an error!
       
   164     private File makefile_source_list;
       
   165     // The challenging task to manage an incremental build is done by javac_state.
       
   166     private JavacState javac_state;
   155     private JavacState javac_state;
   167 
       
   168     // The suffix rules tells you for example, that .java files should be compiled,
       
   169     // and .html files should be copied and .properties files be translated.
       
   170     Map<String,Transformer> suffix_rules;
       
   171 
   156 
   172     public static void main(String... args)  {
   157     public static void main(String... args)  {
   173         if (args.length > 0 && args[0].startsWith("--startserver:")) {
   158         if (args.length > 0 && args[0].startsWith("--startserver:")) {
   174             if (args.length>1) {
   159             if (args.length>1) {
   175                 Log.error("When spawning a background server, only a single --startserver argument is allowed.");
   160                 Log.error("When spawning a background server, only a single --startserver argument is allowed.");
   197                            "Warning!\n"+
   182                            "Warning!\n"+
   198                            "This tool might disappear at any time, and its command line options might change at any time!");
   183                            "This tool might disappear at any time, and its command line options might change at any time!");
   199     }
   184     }
   200 
   185 
   201     public int go(String[] args, PrintStream out, PrintStream err) {
   186     public int go(String[] args, PrintStream out, PrintStream err) {
       
   187 
       
   188         Log.initializeLog(out, err);
       
   189 
       
   190         Options options;
   202         try {
   191         try {
   203             if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
   192             options = Options.parseArgs(args);
   204                 printHelp();
   193         } catch (IllegalArgumentException e) {
   205                 return 0;
   194             Log.error(e.getMessage());
   206             }
   195             return -1;
   207 
   196         }
   208             Log.setLogLevel(findLogLevel(args), out, err);
   197 
   209             String server_settings = Util.findServerSettings(args);
   198         Log.setLogLevel(options.getLogLevel());
   210             args = verifyImplicitOption(args);
   199 
   211             // Find the source root directories, and add the -src option before these, if not there already.
   200         if (!validateOptions(options))
   212             args = addSrcBeforeDirectories(args);
   201             return -1;
   213             // Check that there is at least one -src supplied.
   202 
   214             checkSrcOption(args);
   203         if (!createIfMissing(options.getDestDir()))
   215             // Check that there is one -d supplied.
   204             return -1;
   216             bin_dir = findDirectoryOption(args,"-d","output", true, false, true);
   205 
   217             gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);
   206         Path gensrc = options.getGenSrcDir();
   218             header_dir = findDirectoryOption(args,"-h","headers", false, false, true);
   207         if (gensrc != null && !createIfMissing(gensrc))
   219             makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);
   208             return -1;
   220 
   209 
   221             // Load the prev build state database.
   210         Path hdrdir = options.getHeaderDir();
   222             javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
   211         if (hdrdir != null && !createIfMissing(hdrdir))
   223                     findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
   212             return -1;
   224 
   213 
   225             // Setup the suffix rules from the command line.
   214         // Load the prev build state database.
   226             suffix_rules = javac_state.getJavaSuffixRule();
   215         javac_state = JavacState.load(options, out, err);
   227             findTranslateOptions(args, suffix_rules);
   216 
   228             if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {
   217         // Setup the suffix rules from the command line.
   229                 Log.error("You have translators but no gensrc dir (-s) specified!");
   218         Map<String, Transformer> suffixRules = new HashMap<>();
   230                 return -1;
   219 
   231             }
   220         // Handling of .java-compilation
   232             findCopyOptions(args, suffix_rules);
   221         suffixRules.putAll(javac_state.getJavaSuffixRule());
   233 
   222 
   234             // All found modules are put here.
   223         // Handling of -copy and -tr
   235             Map<String,Module> modules = new HashMap<>();
   224         suffixRules.putAll(options.getTranslationRules());
   236             // We start out in the legacy empty no-name module.
   225 
   237             // As soon as we stumble on a module-info.java file we change to that module.
   226         // All found modules are put here.
   238             Module current_module = new Module("", "");
   227         Map<String,Module> modules = new HashMap<>();
   239             modules.put("", current_module);
   228         // We start out in the legacy empty no-name module.
   240 
   229         // As soon as we stumble on a module-info.java file we change to that module.
   241             // Find all sources, use the suffix rules to know which files are sources.
   230         Module current_module = new Module("", "");
   242             Map<String,Source> sources = new HashMap<>();
   231         modules.put("", current_module);
   243             // Find the files, this will automatically populate the found modules
   232 
   244             // with found packages where the sources are found!
   233         // Find all sources, use the suffix rules to know which files are sources.
   245             findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);
   234         Map<String,Source> sources = new HashMap<>();
   246 
   235 
   247             if (sources.isEmpty()) {
   236         // Find the files, this will automatically populate the found modules
   248                 Log.error("Found nothing to compile!");
   237         // with found packages where the sources are found!
   249                 return -1;
   238         findSourceFiles(options.getSources(),
   250             }
   239                         suffixRules.keySet(),
   251 
   240                         sources,
   252             // Create a map of all source files that are available for linking. Both -src and
   241                         modules,
   253             // -sourcepath point to such files. It is possible to specify multiple
   242                         current_module,
   254             // -sourcepath options to enable different filtering rules. If the
   243                         options.isDefaultPackagePermitted(),
   255             // filters are the same for multiple sourcepaths, they may be concatenated
   244                         false);
   256             // using :(;). Before sending the list of sourcepaths to javac, they are
   245 
   257             // all concatenated. The list created here is used by the SmartFileWrapper to
   246         if (sources.isEmpty()) {
   258             // make sure only the correct sources are actually available.
   247             Log.error("Found nothing to compile!");
   259             // We might find more modules here as well.
   248             return -1;
   260             Map<String,Source> sources_to_link_to = new HashMap<>();
   249         }
   261             findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true);
   250 
   262             findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
   251         // Create a map of all source files that are available for linking. Both -src and
   263             // Rewrite the -src option to make it through to the javac instances.
   252         // -sourcepath point to such files. It is possible to specify multiple
   264             rewriteOptions(args, "-src", "-sourcepath");
   253         // -sourcepath options to enable different filtering rules. If the
   265 
   254         // filters are the same for multiple sourcepaths, they may be concatenated
   266             // Find all class files allowable for linking.
   255         // using :(;). Before sending the list of sourcepaths to javac, they are
   267             // And pickup knowledge of all modules found here.
   256         // all concatenated. The list created here is used by the SmartFileWrapper to
   268             // This cannot currently filter classes inside jar files.
   257         // make sure only the correct sources are actually available.
   269 //          Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
   258         // We might find more modules here as well.
   270 //          findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
   259         Map<String,Source> sources_to_link_to = new HashMap<>();
   271 
   260 
   272             // Find all module sources allowable for linking.
   261         List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
   273 //          Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
   262         sourceResolutionLocations.addAll(options.getSources());
   274 //          findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
   263         sourceResolutionLocations.addAll(options.getSourceSearchPaths());
   275 
   264         findSourceFiles(sourceResolutionLocations,
   276             // Add the set of sources to the build database.
   265                         Collections.singleton(".java"),
   277             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   266                         sources_to_link_to,
   278             javac_state.now().checkInternalState("checking sources", false, sources);
   267                         modules,
   279             javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
   268                         current_module,
   280             javac_state.setVisibleSources(sources_to_link_to);
   269                         options.isDefaultPackagePermitted(),
   281 
   270                         true);
   282             // If there is any change in the source files, taint packages
   271 
   283             // and mark the database in need of saving.
   272         // Find all class files allowable for linking.
   284             javac_state.checkSourceStatus(false);
   273         // And pickup knowledge of all modules found here.
   285 
   274         // This cannot currently filter classes inside jar files.
   286             // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
   275 //      Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
   287             // in javac_state, simply because loading of the JavacState will clean out all artifacts
   276 //      findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
   288             // that do not match the javac_state database.
   277 
   289             javac_state.findAllArtifacts();
   278         // Find all module sources allowable for linking.
   290 
   279 //      Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
   291             // Remove unidentified artifacts from the bin, gensrc and header dirs.
   280 //      findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
   292             // (Unless we allow them to be there.)
   281 
   293             // I.e. artifacts that are not known according to the build database (javac_state).
   282         // Add the set of sources to the build database.
   294             // For examples, files that have been manually copied into these dirs.
   283         javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   295             // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
   284         javac_state.now().checkInternalState("checking sources", false, sources);
   296             // in javac_state) have already been removed when the javac_state was loaded.
   285         javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
   297             if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
   286         javac_state.setVisibleSources(sources_to_link_to);
   298                 javac_state.removeUnidentifiedArtifacts();
   287 
   299             }
   288         // If there is any change in the source files, taint packages
   300             // Go through all sources and taint all packages that miss artifacts.
   289         // and mark the database in need of saving.
   301             javac_state.taintPackagesThatMissArtifacts();
   290         javac_state.checkSourceStatus(false);
   302 
   291 
   303             // Now clean out all known artifacts belonging to tainted packages.
   292         // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
   304             javac_state.deleteClassArtifactsInTaintedPackages();
   293         // in javac_state, simply because loading of the JavacState will clean out all artifacts
   305             // Copy files, for example property files, images files, xml files etc etc.
   294         // that do not match the javac_state database.
   306             javac_state.performCopying(bin_dir, suffix_rules);
   295         javac_state.findAllArtifacts();
   307             // Translate files, for example compile properties or compile idls.
   296 
   308             javac_state.performTranslation(gensrc_dir, suffix_rules);
   297         // Remove unidentified artifacts from the bin, gensrc and header dirs.
   309             // Add any potentially generated java sources to the tobe compiled list.
   298         // (Unless we allow them to be there.)
   310             // (Generated sources must always have a package.)
   299         // I.e. artifacts that are not known according to the build database (javac_state).
   311             Map<String,Source> generated_sources = new HashMap<>();
   300         // For examples, files that have been manually copied into these dirs.
   312             Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
   301         // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
   313                    generated_sources, modules, current_module, false, true, false);
   302         // in javac_state) have already been removed when the javac_state was loaded.
       
   303         if (!options.isUnidentifiedArtifactPermitted()) {
       
   304             javac_state.removeUnidentifiedArtifacts();
       
   305         }
       
   306         // Go through all sources and taint all packages that miss artifacts.
       
   307         javac_state.taintPackagesThatMissArtifacts();
       
   308 
       
   309         // Now clean out all known artifacts belonging to tainted packages.
       
   310         javac_state.deleteClassArtifactsInTaintedPackages();
       
   311         // Copy files, for example property files, images files, xml files etc etc.
       
   312         javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules);
       
   313         // Translate files, for example compile properties or compile idls.
       
   314         javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules);
       
   315         // Add any potentially generated java sources to the tobe compiled list.
       
   316         // (Generated sources must always have a package.)
       
   317         Map<String,Source> generated_sources = new HashMap<>();
       
   318 
       
   319         try {
       
   320 
       
   321             Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), Util.set(".java"), null, null, null, null,
       
   322                     generated_sources, modules, current_module, false, true, false);
   314             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   323             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   315             // Recheck the the source files and their timestamps again.
   324             // Recheck the the source files and their timestamps again.
   316             javac_state.checkSourceStatus(true);
   325             javac_state.checkSourceStatus(true);
   317 
   326 
   318             // Now do a safety check that the list of source files is identical
   327             // Now do a safety check that the list of source files is identical
   319             // to the list Make believes we are compiling. If we do not get this
   328             // to the list Make believes we are compiling. If we do not get this
   320             // right, then incremental builds will fail with subtility.
   329             // right, then incremental builds will fail with subtility.
   321             // If any difference is detected, then we will fail hard here.
   330             // If any difference is detected, then we will fail hard here.
   322             // This is an important safety net.
   331             // This is an important safety net.
   323             javac_state.compareWithMakefileList(makefile_source_list);
   332             javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList()));
   324 
   333 
   325             // Do the compilations, repeatedly until no tainted packages exist.
   334             // Do the compilations, repeatedly until no tainted packages exist.
   326             boolean again;
   335             boolean again;
   327             // Collect the name of all compiled packages.
   336             // Collect the name of all compiled packages.
   328             Set<String> recently_compiled = new HashSet<>();
   337             Set<String> recently_compiled = new HashSet<>();
   329             boolean[] rc = new boolean[1];
   338             boolean[] rc = new boolean[1];
   330             do {
   339             do {
   331                 // Clean out artifacts in tainted packages.
   340                 // Clean out artifacts in tainted packages.
   332                 javac_state.deleteClassArtifactsInTaintedPackages();
   341                 javac_state.deleteClassArtifactsInTaintedPackages();
   333                 again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
   342                 again = javac_state.performJavaCompilations(options, recently_compiled, rc);
   334                 if (!rc[0]) break;
   343                 if (!rc[0]) break;
   335             } while (again);
   344             } while (again);
   336             // Only update the state if the compile went well.
   345             // Only update the state if the compile went well.
   337             if (rc[0]) {
   346             if (rc[0]) {
   338                 javac_state.save();
   347                 javac_state.save();
   349             e.printStackTrace(err);
   358             e.printStackTrace(err);
   350             return -1;
   359             return -1;
   351         }
   360         }
   352     }
   361     }
   353 
   362 
   354     /**
   363     private static boolean validateOptions(Options options) {
   355      * Are java source files passed on the command line?
   364 
   356      */
   365         String err = null;
   357     private boolean findJavaSourceFiles(String[] args) {
   366 
   358         String prev = "";
   367         if (options.getDestDir() == null) {
   359         for (String s : args) {
   368             err = "Please specify output directory.";
   360             if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
   369         } else if (options.isJavaFilesAmongJavacArgs()) {
   361                 return true;
   370             err = "Sjavac does not handle explicit compilation of single .java files.";
   362             }
   371         } else if (options.isAtFilePresent()) {
   363             prev = s;
   372             err = "Sjavac does not handle @-files.";
   364         }
   373         } else if (options.getServerConf() == null) {
   365         return false;
   374             err = "No server configuration provided.";
   366     }
   375         } else if (!options.getImplicitPolicy().equals("none")) {
   367 
   376             err = "The only allowed setting for sjavac is -implicit:none";
   368     /**
   377         } else if (options.getSources().isEmpty()) {
   369      * Is an at file passed on the command line?
   378             err = "You have to specify -src.";
   370      */
   379         } else if (options.getTranslationRules().size() > 1
   371     private boolean findAtFile(String[] args) {
   380                 && options.getGenSrcDir() == null) {
   372         for (String s : args) {
   381             err = "You have translators but no gensrc dir (-s) specified!";
   373             if (s.startsWith("@")) {
   382         }
   374                 return true;
   383 
   375             }
   384         if (err != null)
   376         }
   385             Log.error(err);
   377         return false;
   386 
   378     }
   387         return err == null;
   379 
   388 
   380     /**
   389     }
   381      * Find the log level setting.
   390 
   382      */
   391     private static boolean createIfMissing(Path dir) {
   383     private String findLogLevel(String[] args) {
   392 
   384         for (String s : args) {
   393         if (Files.isDirectory(dir))
   385             if (s.startsWith("--log=") && s.length()>6) {
   394             return true;
   386                 return s.substring(6);
   395 
   387             }
   396         if (Files.exists(dir)) {
   388             if (s.equals("-verbose")) {
   397             Log.error(dir + " is not a directory.");
   389                 return "info";
   398             return false;
   390             }
   399         }
   391         }
   400 
   392         return "info";
   401         try {
   393     }
   402             Files.createDirectories(dir);
   394 
   403         } catch (IOException e) {
   395     /**
   404             Log.error("Could not create directory: " + e.getMessage());
   396      * Remove smart javac wrapper arguments, before feeding
   405             return false;
   397      * the args to the plain javac.
   406         }
   398      */
   407 
   399     static String[] removeWrapperArgs(String[] args) {
       
   400         String[] out = new String[args.length];
       
   401         // The first source path index is remembered
       
   402         // here. So that all following can be concatenated to it.
       
   403         int source_path = -1;
       
   404         // The same for class path.
       
   405         int class_path = -1;
       
   406         // And module path.
       
   407         int module_path = -1;
       
   408         int j = 0;
       
   409         for (int i = 0; i<args.length; ++i) {
       
   410             if (args[i].equals("-src") ||
       
   411                 args[i].equals("-x") ||
       
   412                 args[i].equals("-i") ||
       
   413                 args[i].equals("-xf") ||
       
   414                 args[i].equals("-if") ||
       
   415                 args[i].equals("-copy") ||
       
   416                 args[i].equals("-tr") ||
       
   417                 args[i].equals("-j")) {
       
   418                 // Just skip it and skip following value
       
   419                 i++;
       
   420             } else if (args[i].startsWith("--server:")) {
       
   421                 // Just skip it.
       
   422             } else if (args[i].startsWith("--log=")) {
       
   423                 // Just skip it.
       
   424             } else if (args[i].equals("--permit-unidentified-artifacts")) {
       
   425                 // Just skip it.
       
   426             } else if (args[i].equals("--permit-sources-without-package")) {
       
   427                 // Just skip it.
       
   428             } else if (args[i].equals("--compare-found-sources")) {
       
   429                 // Just skip it and skip verify file name
       
   430                 i++;
       
   431             } else if (args[i].equals("-sourcepath")) {
       
   432                 if (source_path == -1) {
       
   433                     source_path = j;
       
   434                     out[j] = args[i];
       
   435                     out[j+1] = args[i+1];
       
   436                     j+=2;
       
   437                     i++;
       
   438                 } else {
       
   439                     // Skip this and its argument, but
       
   440                     // append argument to found sourcepath.
       
   441                     out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];
       
   442                     i++;
       
   443                 }
       
   444             } else if (args[i].equals("-classpath") || args[i].equals("-cp")) {
       
   445                 if (class_path == -1) {
       
   446                     class_path = j;
       
   447                     out[j] = args[i];
       
   448                     out[j+1] = args[i+1];
       
   449                     j+=2;
       
   450                     i++;
       
   451                 } else {
       
   452                     // Skip this and its argument, but
       
   453                     // append argument to found sourcepath.
       
   454                     out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];
       
   455                     i++;
       
   456                 }
       
   457             } else if (args[i].equals("-modulepath")) {
       
   458                 if (module_path == -1) {
       
   459                     module_path = j;
       
   460                     out[j] = args[i];
       
   461                     out[j+1] = args[i+1];
       
   462                     j+=2;
       
   463                     i++;
       
   464                 } else {
       
   465                     // Skip this and its argument, but
       
   466                     // append argument to found sourcepath.
       
   467                     out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];
       
   468                     i++;
       
   469                 }
       
   470              } else {
       
   471                 // Copy argument.
       
   472                 out[j] = args[i];
       
   473                 j++;
       
   474             }
       
   475         }
       
   476         String[] ret = new String[j];
       
   477         System.arraycopy(out, 0, ret, 0, j);
       
   478         return ret;
       
   479     }
       
   480 
       
   481     /**
       
   482      * Make sure directory exist, create it if not.
       
   483      */
       
   484     private static boolean makeSureExists(File dir) {
       
   485         // Make sure the dest directories exist.
       
   486         if (!dir.exists()) {
       
   487             if (!dir.mkdirs()) {
       
   488                 Log.error("Could not create the directory "+dir.getPath());
       
   489                 return false;
       
   490             }
       
   491         }
       
   492         return true;
   408         return true;
   493     }
   409     }
   494 
   410 
   495     /**
   411 
   496      * Verify that a package pattern is valid.
   412     /** Find source files in the given source locations. */
   497      */
   413     public static void findSourceFiles(List<SourceLocation> sourceLocations,
   498     private static void checkPattern(String s) throws ProblemException {
   414                                        Set<String> sourceTypes,
   499         // Package names like foo.bar.gamma are allowed, and
   415                                        Map<String,Source> foundFiles,
   500         // package names suffixed with .* like foo.bar.* are
   416                                        Map<String, Module> foundModules,
   501         // also allowed.
   417                                        Module currentModule,
   502         Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
   418                                        boolean permitSourcesInDefaultPackage,
   503         Matcher m = p.matcher(s);
   419                                        boolean inLinksrc) {
   504         if (!m.matches()) {
   420 
   505             throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
   421         for (SourceLocation source : sourceLocations) {
   506         }
   422             source.findSourceFiles(sourceTypes,
   507     }
   423                                    foundFiles,
   508 
   424                                    foundModules,
   509     /**
   425                                    currentModule,
   510      * Verify that a translate pattern is valid.
   426                                    permitSourcesInDefaultPackage,
   511      */
   427                                    inLinksrc);
   512     private static void checkTranslatePattern(String s) throws ProblemException {
   428         }
   513         // .prop=com.sun.tools.javac.smart.CompileProperties
   429     }
   514         // .idl=com.sun.corba.CompileIdl
       
   515         // .g3=antlr.CompileGrammar,debug=true
       
   516         Pattern p = Pattern.compile(
       
   517             "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+
       
   518             "(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");
       
   519         Matcher m = p.matcher(s);
       
   520         if (!m.matches()) {
       
   521             throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");
       
   522         }
       
   523     }
       
   524 
       
   525     /**
       
   526      * Verify that a copy pattern is valid.
       
   527      */
       
   528     private static void checkCopyPattern(String s) throws ProblemException {
       
   529         // .gif
       
   530         // .html
       
   531         Pattern p = Pattern.compile(
       
   532             "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");
       
   533         Matcher m = p.matcher(s);
       
   534         if (!m.matches()) {
       
   535             throw new ProblemException("The string \""+s+"\" is not a proper suffix.");
       
   536         }
       
   537     }
       
   538 
       
   539     /**
       
   540      * Verify that a source file name is valid.
       
   541      */
       
   542     private static void checkFilePattern(String s) throws ProblemException {
       
   543         // File names like foo/bar/gamma/Bar.java are allowed,
       
   544         // as well as /bar/jndi.properties as well as,
       
   545         // */bar/Foo.java
       
   546         Pattern p = null;
       
   547         if (File.separatorChar == '\\') {
       
   548             p = Pattern.compile("\\*?(.+\\\\)*.+");
       
   549         }
       
   550         else if (File.separatorChar == '/') {
       
   551             p = Pattern.compile("\\*?(.+/)*.+");
       
   552         } else {
       
   553             throw new ProblemException("This platform uses the unsupported "+File.separatorChar+
       
   554                                       " as file separator character. Please add support for it!");
       
   555         }
       
   556         Matcher m = p.matcher(s);
       
   557         if (!m.matches()) {
       
   558             throw new ProblemException("The string \""+s+"\" is not a proper file name.");
       
   559         }
       
   560     }
       
   561 
       
   562     /**
       
   563      * Scan the arguments to find an option is used.
       
   564      */
       
   565     private static boolean hasOption(String[] args, String option) {
       
   566         for (String a : args) {
       
   567             if (a.equals(option)) return true;
       
   568         }
       
   569         return false;
       
   570     }
       
   571 
       
   572     /**
       
   573      * Check if -implicit is supplied, if so check that it is none.
       
   574      * If -implicit is not supplied, supply -implicit:none
       
   575      * Only implicit:none is allowed because otherwise the multicore compilations
       
   576      * and dependency tracking will be tangled up.
       
   577      */
       
   578     private static String[] verifyImplicitOption(String[] args)
       
   579         throws ProblemException {
       
   580 
       
   581         boolean foundImplicit = false;
       
   582         for (String a : args) {
       
   583             if (a.startsWith("-implicit:")) {
       
   584                 foundImplicit = true;
       
   585                 if (!a.equals("-implicit:none")) {
       
   586                     throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");
       
   587                 }
       
   588             }
       
   589         }
       
   590         if (foundImplicit) {
       
   591             return args;
       
   592         }
       
   593         // -implicit:none not found lets add it.
       
   594         String[] newargs = new String[args.length+1];
       
   595         System.arraycopy(args,0, newargs, 0, args.length);
       
   596         newargs[args.length] = "-implicit:none";
       
   597         return newargs;
       
   598     }
       
   599 
       
   600     /**
       
   601      * Rewrite a single option into something else.
       
   602      */
       
   603     private static void rewriteOptions(String[] args, String option, String new_option) {
       
   604         for (int i=0; i<args.length; ++i) {
       
   605             if (args[i].equals(option)) {
       
   606                 args[i] = new_option;
       
   607             }
       
   608         }
       
   609     }
       
   610 
       
   611     /**
       
   612      * Scan the arguments to find an option that specifies a directory.
       
   613      * Create the directory if necessary.
       
   614      */
       
   615     private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)
       
   616         throws ProblemException, ProblemException {
       
   617         File dir = null;
       
   618         for (int i = 0; i<args.length; ++i) {
       
   619             if (args[i].equals(option)) {
       
   620                 if (dir != null) {
       
   621                     throw new ProblemException("You have already specified the "+name+" dir!");
       
   622                 }
       
   623                 if (i+1 >= args.length) {
       
   624                     throw new ProblemException("You have to specify a directory following "+option+".");
       
   625                 }
       
   626                 if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {
       
   627                     throw new ProblemException("You must only specify a single directory for "+option+".");
       
   628                 }
       
   629                 dir = new File(args[i+1]);
       
   630                 if (!dir.exists()) {
       
   631                     if (!create) {
       
   632                          throw new ProblemException("This directory does not exist: "+dir.getPath());
       
   633                     } else
       
   634                     if (!makeSureExists(dir)) {
       
   635                         throw new ProblemException("Cannot create directory "+dir.getPath());
       
   636                     }
       
   637                 }
       
   638                 if (!dir.isDirectory()) {
       
   639                     throw new ProblemException("\""+args[i+1]+"\" is not a directory.");
       
   640                 }
       
   641             }
       
   642         }
       
   643         if (dir == null && needed) {
       
   644             throw new ProblemException("You have to specify "+option);
       
   645         }
       
   646         try {
       
   647             if (dir != null)
       
   648                 return dir.getCanonicalFile();
       
   649         } catch (IOException e) {
       
   650             throw new ProblemException(""+e);
       
   651         }
       
   652         return null;
       
   653     }
       
   654 
       
   655     /**
       
   656      * Option is followed by path.
       
   657      */
       
   658     private static boolean shouldBeFollowedByPath(String o) {
       
   659         return o.equals("-s") ||
       
   660                o.equals("-h") ||
       
   661                o.equals("-d") ||
       
   662                o.equals("-sourcepath") ||
       
   663                o.equals("-classpath") ||
       
   664                o.equals("-cp") ||
       
   665                o.equals("-bootclasspath") ||
       
   666                o.equals("-src");
       
   667     }
       
   668 
       
   669     /**
       
   670      * Add -src before source root directories if not already there.
       
   671      */
       
   672     private static String[] addSrcBeforeDirectories(String[] args) {
       
   673         List<String> newargs = new ArrayList<>();
       
   674         for (int i = 0; i<args.length; ++i) {
       
   675             File dir = new File(args[i]);
       
   676             if (dir.exists() && dir.isDirectory()) {
       
   677                 if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {
       
   678                     newargs.add("-src");
       
   679                 }
       
   680             }
       
   681             newargs.add(args[i]);
       
   682         }
       
   683         return newargs.toArray(new String[0]);
       
   684     }
       
   685 
       
   686     /**
       
   687      * Check the -src options.
       
   688      */
       
   689     private static void checkSrcOption(String[] args)
       
   690         throws ProblemException {
       
   691         Set<File> dirs = new HashSet<>();
       
   692         for (int i = 0; i<args.length; ++i) {
       
   693             if (args[i].equals("-src")) {
       
   694                 if (i+1 >= args.length) {
       
   695                     throw new ProblemException("You have to specify a directory following -src.");
       
   696                 }
       
   697                 StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);
       
   698                 while (st.hasMoreElements()) {
       
   699                     File dir = new File(st.nextToken());
       
   700                     if (!dir.exists()) {
       
   701                         throw new ProblemException("This directory does not exist: "+dir.getPath());
       
   702                     }
       
   703                     if (!dir.isDirectory()) {
       
   704                         throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");
       
   705                     }
       
   706                     if (dirs.contains(dir)) {
       
   707                         throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");
       
   708                     }
       
   709                     dirs.add(dir);
       
   710                 }
       
   711             }
       
   712         }
       
   713         if (dirs.isEmpty()) {
       
   714             throw new ProblemException("You have to specify -src.");
       
   715         }
       
   716     }
       
   717 
       
   718     /**
       
   719      * Scan the arguments to find an option that specifies a file.
       
   720      */
       
   721     private static File findFileOption(String[] args, String option, String name, boolean needed)
       
   722         throws ProblemException, ProblemException {
       
   723         File file = null;
       
   724         for (int i = 0; i<args.length; ++i) {
       
   725             if (args[i].equals(option)) {
       
   726                 if (file != null) {
       
   727                     throw new ProblemException("You have already specified the "+name+" file!");
       
   728                 }
       
   729                 if (i+1 >= args.length) {
       
   730                     throw new ProblemException("You have to specify a file following "+option+".");
       
   731                 }
       
   732                 file = new File(args[i+1]);
       
   733                 if (file.isDirectory()) {
       
   734                     throw new ProblemException("\""+args[i+1]+"\" is not a file.");
       
   735                 }
       
   736                 if (!file.exists() && needed) {
       
   737                     throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");
       
   738                 }
       
   739 
       
   740             }
       
   741         }
       
   742         if (file == null && needed) {
       
   743             throw new ProblemException("You have to specify "+option);
       
   744         }
       
   745         return file;
       
   746     }
       
   747 
       
   748     /**
       
   749      * Look for a specific switch, return true if found.
       
   750      */
       
   751     public static boolean findBooleanOption(String[] args, String option) {
       
   752         for (String arg : args) {
       
   753             if (arg.equals(option))
       
   754                 return true;
       
   755         }
       
   756         return false;
       
   757     }
       
   758 
       
   759     /**
       
   760      * Scan the arguments to find an option that specifies a number.
       
   761      */
       
   762     public static int findNumberOption(String[] args, String option) {
       
   763         int rc = 0;
       
   764         for (int i = 0; i<args.length; ++i) {
       
   765             if (args[i].equals(option)) {
       
   766                 if (args.length > i+1) {
       
   767                     rc = Integer.parseInt(args[i+1]);
       
   768                 }
       
   769             }
       
   770         }
       
   771         return rc;
       
   772     }
       
   773 
       
   774     /**
       
   775      * Scan the arguments to find the option (-tr) that setup translation rules to java source
       
   776      * from different sources. For example: .properties are translated using CompileProperties
       
   777      * The found translators are stored as suffix rules.
       
   778      */
       
   779     private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
       
   780         throws ProblemException, ProblemException {
       
   781 
       
   782         for (int i = 0; i<args.length; ++i) {
       
   783             if (args[i].equals("-tr")) {
       
   784                 if (i+1 >= args.length) {
       
   785                     throw new ProblemException("You have to specify a translate rule following -tr.");
       
   786                 }
       
   787                 String s = args[i+1];
       
   788                 checkTranslatePattern(s);
       
   789                 int ep = s.indexOf("=");
       
   790                 String suffix = s.substring(0,ep);
       
   791                 String classname = s.substring(ep+1);
       
   792                 if (suffix_rules.get(suffix) != null) {
       
   793                     throw new ProblemException("You have already specified a "+
       
   794                                               "rule for the suffix "+suffix);
       
   795                 }
       
   796                 if (s.equals(".class")) {
       
   797                     throw new ProblemException("You cannot have a translator for .class files!");
       
   798                 }
       
   799                 if (s.equals(".java")) {
       
   800                     throw new ProblemException("You cannot have a translator for .java files!");
       
   801                 }
       
   802                 String extra = null;
       
   803                 int exp = classname.indexOf(",");
       
   804                 if (exp != -1) {
       
   805                     extra = classname.substring(exp+1);
       
   806                     classname = classname.substring(0,exp);
       
   807                 }
       
   808                 try {
       
   809                     Class<?> cl = Class.forName(classname);
       
   810                     Transformer t = (Transformer)cl.newInstance();
       
   811                     t.setExtra(extra);
       
   812                     suffix_rules.put(suffix, t);
       
   813                 }
       
   814                 catch (Exception e) {
       
   815                     throw new ProblemException("Cannot use "+classname+" as a translator!");
       
   816                 }
       
   817             }
       
   818         }
       
   819     }
       
   820 
       
   821     /**
       
   822      * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
       
   823      * For example: -copy .html
       
   824      * The found copiers are stored as suffix rules as well. No translation is done, just copying.
       
   825      */
       
   826     private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
       
   827         throws ProblemException, ProblemException {
       
   828 
       
   829         for (int i = 0; i<args.length; ++i) {
       
   830             if (args[i].equals("-copy")) {
       
   831                 if (i+1 >= args.length) {
       
   832                     throw new ProblemException("You have to specify a translate rule following -tr.");
       
   833                 }
       
   834                 String s = args[i+1];
       
   835                 checkCopyPattern(s);
       
   836                 if (suffix_rules.get(s) != null) {
       
   837                     throw new ProblemException("You have already specified a "+
       
   838                                               "rule for the suffix "+s);
       
   839                 }
       
   840                 if (s.equals(".class")) {
       
   841                     throw new ProblemException("You cannot have a copy rule for .class files!");
       
   842                 }
       
   843                 if (s.equals(".java")) {
       
   844                     throw new ProblemException("You cannot have a copy rule for .java files!");
       
   845                 }
       
   846                 suffix_rules.put(s, javac_state.getCopier());
       
   847             }
       
   848         }
       
   849     }
       
   850 
       
   851     /**
       
   852      * Rewrite a / separated path into \ separated, but only
       
   853      * if we are running on a platform were File.separatorChar=='\', ie winapi.
       
   854      */
       
   855     private String fixupSeparator(String p) {
       
   856         if (File.separatorChar == '/') return p;
       
   857         return p.replaceAll("/", "\\\\");
       
   858     }
       
   859 
       
   860     /**
       
   861      * Scan the arguments for -i -x -xf -if followed by the option
       
   862      * -src, -sourcepath, -modulepath or -classpath and produce a map of all the
       
   863      * files to referenced for that particular option.
       
   864      *
       
   865      * Store the found sources and the found modules in the supplied maps.
       
   866      */
       
   867     private boolean findFiles(String[] args, String option, Set<String> suffixes,
       
   868                               Map<String,Source> found_files, Map<String, Module> found_modules,
       
   869                               Module current_module, boolean inLinksrc)
       
   870         throws ProblemException, ProblemException
       
   871     {
       
   872         // Track which source roots, source path roots and class path roots have been added.
       
   873         Set<File> roots = new HashSet<>();
       
   874         // Track the current set of package includes,excludes as well as excluded source files,
       
   875         // to be used in the next -src/-sourcepath/-classpath
       
   876         List<String> includes = new LinkedList<>();
       
   877         List<String> excludes = new LinkedList<>();
       
   878         List<String> excludefiles = new LinkedList<>();
       
   879         List<String> includefiles = new LinkedList<>();
       
   880         // This include is used to find all modules in the source.
       
   881         List<String> moduleinfo = new LinkedList<>();
       
   882         moduleinfo.add("module-info.java");
       
   883 
       
   884         for (int i = 0; i<args.length; ++i) {
       
   885             if (args[i].equals("-i")) {
       
   886                 if (i+1 >= args.length) {
       
   887                     throw new ProblemException("You have to specify a package pattern following -i");
       
   888                 }
       
   889                 String incl = args[i+1];
       
   890                 checkPattern(incl);
       
   891                 includes.add(incl);
       
   892             }
       
   893             if (args[i].equals("-x")) {
       
   894                 if (i+1 >= args.length) {
       
   895                     throw new ProblemException("You have to specify a package pattern following -x");
       
   896                 }
       
   897                 String excl = args[i+1];
       
   898                 checkPattern(excl);
       
   899                 excludes.add(excl);
       
   900             }
       
   901             if (args[i].equals("-xf")) {
       
   902                 if (i+1 >= args.length) {
       
   903                     throw new ProblemException("You have to specify a file following -xf");
       
   904                 }
       
   905                 String exclf = args[i+1];
       
   906                 checkFilePattern(exclf);
       
   907                 exclf = Util.normalizeDriveLetter(exclf);
       
   908                 excludefiles.add(fixupSeparator(exclf));
       
   909             }
       
   910             if (args[i].equals("-if")) {
       
   911                 if (i+1 >= args.length) {
       
   912                     throw new ProblemException("You have to specify a file following -xf");
       
   913                 }
       
   914                 String inclf = args[i+1];
       
   915                 checkFilePattern(inclf);
       
   916                 inclf = Util.normalizeDriveLetter(inclf);
       
   917                 includefiles.add(fixupSeparator(inclf));
       
   918             }
       
   919             if (args[i].equals(option)) {
       
   920                 if (i+1 >= args.length) {
       
   921                     throw new ProblemException("You have to specify a directory following "+option);
       
   922                 }
       
   923                 String[] root_dirs = args[i+1].split(File.pathSeparator);
       
   924                 for (String r : root_dirs) {
       
   925                     File root = new File(r);
       
   926                     if (!root.isDirectory()) {
       
   927                         throw new ProblemException("\""+r+"\" is not a directory.");
       
   928                     }
       
   929                     try {
       
   930                         root = root.getCanonicalFile();
       
   931                     } catch (IOException e) {
       
   932                         throw new ProblemException(""+e);
       
   933                     }
       
   934                     if (roots.contains(root)) {
       
   935                         throw new ProblemException("\""+r+"\" has already been used for "+option);
       
   936                     }
       
   937                     if (root.equals(bin_dir)) {
       
   938                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
       
   939                     }
       
   940                     if (root.equals(gensrc_dir)) {
       
   941                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
       
   942                     }
       
   943                     if (root.equals(header_dir)) {
       
   944                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
       
   945                     }
       
   946                     roots.add(root);
       
   947                     Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
       
   948                                     found_files, found_modules, current_module,
       
   949                                     findBooleanOption(args, "--permit-sources-without-package"),
       
   950                                     false, inLinksrc);
       
   951                 }
       
   952             }
       
   953             if (args[i].equals("-src") ||
       
   954                 args[i].equals("-sourcepath") ||
       
   955                 args[i].equals("-modulepath") ||
       
   956                 args[i].equals("-classpath") ||
       
   957                 args[i].equals("-cp"))
       
   958             {
       
   959                 // Reset the includes,excludes and excludefiles after they have been used.
       
   960                 includes = new LinkedList<>();
       
   961                 excludes = new LinkedList<>();
       
   962                 excludefiles = new LinkedList<>();
       
   963                 includefiles = new LinkedList<>();
       
   964             }
       
   965         }
       
   966         return true;
       
   967     }
       
   968 
       
   969 }
   430 }
   970