src/share/classes/com/sun/tools/sjavac/Main.java

Wed, 27 Apr 2016 01:34:52 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:34:52 +0800
changeset 0
959103a6100f
child 2525
2eb010b6cb22
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/langtools/
changeset: 2573:53ca196be1ae
tag: jdk8u25-b17

     1 /*
     2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.sjavac;
    28 import java.io.File;
    29 import java.io.IOException;
    30 import java.io.PrintStream;
    31 import java.util.*;
    32 import java.util.regex.Matcher;
    33 import java.util.regex.Pattern;
    35 import com.sun.tools.sjavac.server.JavacServer;
    37 /**
    38  * The main class of the smart javac wrapper tool.
    39  *
    40  * <p><b>This is NOT part of any supported API.
    41  * If you write code that depends on this, you do so at your own
    42  * risk.  This code and its internal interfaces are subject to change
    43  * or deletion without notice.</b></p>
    44  */
    45 public class Main {
    47     /*  This is a smart javac wrapper primarily used when building the OpenJDK,
    48         though other projects are welcome to use it too. But please be aware
    49         that it is not an official api and will change in the future.
    50         (We really mean it!)
    52         Goals:
    54         ** Create a state file, containing information about the build, so
    55            that incremental builds only rebuild what is necessary. Also the
    56            state file can be used by make/ant to detect when to trigger
    57            a call to the smart javac wrapper.
    59            This file is called bin/javac_state (assuming that you specified "-d bin")
    60            Thus the simplest makefile is:
    62            SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main
    63            SRCS=$(shell find src -name "*.java")
    64            bin/javac_state : $(SRCS)
    65                   $(SJAVAC) src -d bin
    67            This makefile will run very fast and detect properly when Java code needs to
    68            be recompiled. The smart javac wrapper will then use the information in java_state
    69            to do an efficient incremental compile.
    71            Previously it was near enough impossible to write an efficient makefile for Java
    72            with support for incremental builds and dependency tracking.
    74         ** Separate java sources to be compiled from java
    75            sources used >only< for linking. The options:
    77            "dir" points to root dir with sources to be compiled
    78            "-sourcepath dir" points to root dir with sources used only for linking
    79            "-classpath dir" points to dir with classes used only for linking (as before)
    81         ** Use all cores for compilation by default.
    82            "-j 4" limit the number of cores to 4.
    83            For the moment, the sjavac server additionally limits the number of cores to three.
    84            This will improve in the future when more sharing is performed between concurrent JavaCompilers.
    86         ** Basic translation support from other sources to java, and then compilation of the generated java.
    87            This functionality might be moved into annotation processors instead.
    88            Again this is driven by the OpenJDK sources where properties and a few other types of files
    89            are converted into Java sources regularily. The javac_state embraces copy and tr, and perform
    90            incremental recompiles and copying for these as well. META-INF will be a special copy rule
    91            that will copy any files found below any META-INF dir in src to the bin/META-INF dir.
    92            "-copy .gif"
    93            "-copy META-INF"
    94            "-tr .prop=com.sun.tools.javac.smart.CompileProperties
    95            "-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle
    96            "-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
    98         ** Control which classes in the src,sourcepath and classpath that javac is allowed to see.
    99            Again, this is necessary to deal with the source code structure of the OpenJDK which is
   100            intricate (read messy).
   102            "-i tools.*" to include the tools package and all its subpackages in the build.
   103            "-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages.
   104            "-x tools.net.drums" to exclude the drums package only, keep its subpackages.
   105            "-xf tools/net/Bar.java" // Do not compile this file...
   106            "-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java!
   107            "-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used.
   109         ** The smart javac wrapper is driven by the modification time on the source files and compared
   110            to the modification times written into the javac_state file.
   112            It does not compare the modification time of the source with the modification time of the artifact.
   113            However it will detect if the modification time of an artifact has changed compared to the java_state,
   114            and this will trigger a delete of the artifact and a subsequent recompile of the source.
   116            The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source
   117            as the final step before the output dir is finalized and immediately jared, or jmodded. The output
   118            dir should be considered opaque. Do not write into the outputdir yourself!
   119            Any artifacts found in the outputdir that javac_state does not know of, will be deleted!
   120            This can however be prevented, using the switch --permit-unidentified-artifacts
   121            This switch is necessary when build the OpenJDK because its makefiles still write directly to
   122            the output classes dirs.
   124            Any makefile/ant rules that want to put contents into the outputdir should put the content
   125            in one of several source roots. Static content that is under version control, can be put in the same source
   126            code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should
   127            be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments:
   128            "gensrc_stuff src -d bin"
   130         The command line:
   131         java -cp tools.jar com.sun.tools.sjavac.Main \
   132              -i "com.bar.*" -x "com.bar.foo.*" \
   133              first_root \
   134              -i "com.bar.foo.*" \
   135              second_root \
   136              -x "org.net.*" \
   137              -sourcepath link_root_sources \
   138              -classpath link_root_classes \
   139              -d bin
   141         Will compile all sources for package com.bar and its subpackages, found below first_root,
   142         except the package com.bar.foo (and its subpackages), for which the sources are picked
   143         from second_root instead. It will link against classes in link_root_classes and against
   144         sources in link_root_sources, but will not see (try to link against) sources matching org.net.*
   145         but will link against org.net* classes (if they exist) in link_root_classes.
   147         (If you want a set of complex filter rules to be applied to several source directories, without
   148          having to repeat the the filter rules for each root. You can use the explicit -src option. For example:
   149          sjavac -x "com.foo.*" -src root1:root2:root3  )
   151         The resulting classes are written into bin.
   152     */
   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;
   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;
   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;
   172     public static void main(String... args)  {
   173         if (args.length > 0 && args[0].startsWith("--startserver:")) {
   174             if (args.length>1) {
   175                 Log.error("When spawning a background server, only a single --startserver argument is allowed.");
   176                 return;
   177             }
   178             // Spawn a background server.
   179             int rc = JavacServer.startServer(args[0], System.err);
   180             System.exit(rc);
   181         }
   182         Main main = new Main();
   183         int rc = main.go(args, System.out, System.err);
   184         // Remove the portfile, but only if this background=false was used.
   185         JavacServer.cleanup(args);
   186         System.exit(rc);
   187     }
   189     private void printHelp() {
   190         System.out.println("Usage: sjavac <options>\n"+
   191                            "where required options are:\n"+
   192                            "dir                        Compile all sources in dir recursively\n"+
   193                            "-d dir                     Store generated classes here and the javac_state file\n"+
   194                            "--server:portfile=/tmp/abc Use a background sjavac server\n\n"+
   195                            "All other arguments as javac, except -implicit:none which is forced by default.\n"+
   196                            "No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+
   197                            "Warning!\n"+
   198                            "This tool might disappear at any time, and its command line options might change at any time!");
   199     }
   201     public int go(String[] args, PrintStream out, PrintStream err) {
   202         try {
   203             if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
   204                 printHelp();
   205                 return 0;
   206             }
   208             Log.setLogLevel(findLogLevel(args), out, err);
   209             String server_settings = Util.findServerSettings(args);
   210             args = verifyImplicitOption(args);
   211             // Find the source root directories, and add the -src option before these, if not there already.
   212             args = addSrcBeforeDirectories(args);
   213             // Check that there is at least one -src supplied.
   214             checkSrcOption(args);
   215             // Check that there is one -d supplied.
   216             bin_dir = findDirectoryOption(args,"-d","output", true, false, true);
   217             gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);
   218             header_dir = findDirectoryOption(args,"-h","headers", false, false, true);
   219             makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);
   221             // Load the prev build state database.
   222             javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
   223                     findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
   225             // Setup the suffix rules from the command line.
   226             suffix_rules = javac_state.getJavaSuffixRule();
   227             findTranslateOptions(args, suffix_rules);
   228             if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {
   229                 Log.error("You have translators but no gensrc dir (-s) specified!");
   230                 return -1;
   231             }
   232             findCopyOptions(args, suffix_rules);
   234             // All found modules are put here.
   235             Map<String,Module> modules = new HashMap<String,Module>();
   236             // We start out in the legacy empty no-name module.
   237             // As soon as we stumble on a module-info.java file we change to that module.
   238             Module current_module = new Module("", "");
   239             modules.put("", current_module);
   241             // Find all sources, use the suffix rules to know which files are sources.
   242             Map<String,Source> sources = new HashMap<String,Source>();
   243             // Find the files, this will automatically populate the found modules
   244             // with found packages where the sources are found!
   245             findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);
   247             if (sources.isEmpty()) {
   248                 Log.error("Found nothing to compile!");
   249                 return -1;
   250             }
   252             // Create a map of all source files that are available for linking. Both -src and
   253             // -sourcepath point to such files. It is possible to specify multiple
   254             // -sourcepath options to enable different filtering rules. If the
   255             // filters are the same for multiple sourcepaths, they may be concatenated
   256             // using :(;). Before sending the list of sourcepaths to javac, they are
   257             // all concatenated. The list created here is used by the SmartFileWrapper to
   258             // make sure only the correct sources are actually available.
   259             // We might find more modules here as well.
   260             Map<String,Source> sources_to_link_to = new HashMap<String,Source>();
   261             findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true);
   262             findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
   263             // Rewrite the -src option to make it through to the javac instances.
   264             rewriteOptions(args, "-src", "-sourcepath");
   266             // Find all class files allowable for linking.
   267             // And pickup knowledge of all modules found here.
   268             // This cannot currently filter classes inside jar files.
   269 //          Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
   270 //          findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
   272             // Find all module sources allowable for linking.
   273 //          Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
   274 //          findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
   276             // Add the set of sources to the build database.
   277             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   278             javac_state.now().checkInternalState("checking sources", false, sources);
   279             javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
   280             javac_state.setVisibleSources(sources_to_link_to);
   282             // If there is any change in the source files, taint packages
   283             // and mark the database in need of saving.
   284             javac_state.checkSourceStatus(false);
   286             // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
   287             // in javac_state, simply because loading of the JavacState will clean out all artifacts
   288             // that do not match the javac_state database.
   289             javac_state.findAllArtifacts();
   291             // Remove unidentified artifacts from the bin, gensrc and header dirs.
   292             // (Unless we allow them to be there.)
   293             // I.e. artifacts that are not known according to the build database (javac_state).
   294             // For examples, files that have been manually copied into these dirs.
   295             // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
   296             // in javac_state) have already been removed when the javac_state was loaded.
   297             if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
   298                 javac_state.removeUnidentifiedArtifacts();
   299             }
   300             // Go through all sources and taint all packages that miss artifacts.
   301             javac_state.taintPackagesThatMissArtifacts();
   303             // Now clean out all known artifacts belonging to tainted packages.
   304             javac_state.deleteClassArtifactsInTaintedPackages();
   305             // Copy files, for example property files, images files, xml files etc etc.
   306             javac_state.performCopying(bin_dir, suffix_rules);
   307             // Translate files, for example compile properties or compile idls.
   308             javac_state.performTranslation(gensrc_dir, suffix_rules);
   309             // Add any potentially generated java sources to the tobe compiled list.
   310             // (Generated sources must always have a package.)
   311             Map<String,Source> generated_sources = new HashMap<String,Source>();
   312             Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
   313                    generated_sources, modules, current_module, false, true, false);
   314             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
   315             // Recheck the the source files and their timestamps again.
   316             javac_state.checkSourceStatus(true);
   318             // 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
   320             // right, then incremental builds will fail with subtility.
   321             // If any difference is detected, then we will fail hard here.
   322             // This is an important safety net.
   323             javac_state.compareWithMakefileList(makefile_source_list);
   325             // Do the compilations, repeatedly until no tainted packages exist.
   326             boolean again;
   327             // Collect the name of all compiled packages.
   328             Set<String> recently_compiled = new HashSet<String>();
   329             boolean[] rc = new boolean[1];
   330             do {
   331                 // Clean out artifacts in tainted packages.
   332                 javac_state.deleteClassArtifactsInTaintedPackages();
   333                 again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
   334                 if (!rc[0]) break;
   335             } while (again);
   336             // Only update the state if the compile went well.
   337             if (rc[0]) {
   338                 javac_state.save();
   339                 // Reflatten only the artifacts.
   340                 javac_state.now().flattenArtifacts(modules);
   341                 // Remove artifacts that were generated during the last compile, but not this one.
   342                 javac_state.removeSuperfluousArtifacts(recently_compiled);
   343             }
   344             return rc[0] ? 0 : -1;
   345         } catch (ProblemException e) {
   346             Log.error(e.getMessage());
   347             return -1;
   348         } catch (Exception e) {
   349             e.printStackTrace(err);
   350             return -1;
   351         }
   352     }
   354     /**
   355      * Are java source files passed on the command line?
   356      */
   357     private boolean findJavaSourceFiles(String[] args) {
   358         String prev = "";
   359         for (String s : args) {
   360             if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
   361                 return true;
   362             }
   363             prev = s;
   364         }
   365         return false;
   366     }
   368     /**
   369      * Is an at file passed on the command line?
   370      */
   371     private boolean findAtFile(String[] args) {
   372         for (String s : args) {
   373             if (s.startsWith("@")) {
   374                 return true;
   375             }
   376         }
   377         return false;
   378     }
   380     /**
   381      * Find the log level setting.
   382      */
   383     private String findLogLevel(String[] args) {
   384         for (String s : args) {
   385             if (s.startsWith("--log=") && s.length()>6) {
   386                 return s.substring(6);
   387             }
   388             if (s.equals("-verbose")) {
   389                 return "info";
   390             }
   391         }
   392         return "info";
   393     }
   395     /**
   396      * Remove smart javac wrapper arguments, before feeding
   397      * the args to the plain javac.
   398      */
   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     }
   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;
   493     }
   495     /**
   496      * Verify that a package pattern is valid.
   497      */
   498     private static void checkPattern(String s) throws ProblemException {
   499         // Package names like foo.bar.gamma are allowed, and
   500         // package names suffixed with .* like foo.bar.* are
   501         // also allowed.
   502         Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
   503         Matcher m = p.matcher(s);
   504         if (!m.matches()) {
   505             throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
   506         }
   507     }
   509     /**
   510      * Verify that a translate pattern is valid.
   511      */
   512     private static void checkTranslatePattern(String s) throws ProblemException {
   513         // .prop=com.sun.tools.javac.smart.CompileProperties
   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     }
   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     }
   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     }
   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     }
   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 {
   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     }
   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     }
   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     }
   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     }
   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<String>();
   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     }
   686     /**
   687      * Check the -src options.
   688      */
   689     private static void checkSrcOption(String[] args)
   690         throws ProblemException {
   691         Set<File> dirs = new HashSet<File>();
   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     }
   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                 }
   740             }
   741         }
   742         if (file == null && needed) {
   743             throw new ProblemException("You have to specify "+option);
   744         }
   745         return file;
   746     }
   748     /**
   749      * Look for a specific switch, return true if found.
   750      */
   751     public static boolean findBooleanOption(String[] args, String option) {
   752         for (int i = 0; i<args.length; ++i) {
   753             if (args[i].equals(option)) return true;
   754         }
   755         return false;
   756     }
   758     /**
   759      * Scan the arguments to find an option that specifies a number.
   760      */
   761     public static int findNumberOption(String[] args, String option) {
   762         int rc = 0;
   763         for (int i = 0; i<args.length; ++i) {
   764             if (args[i].equals(option)) {
   765                 if (args.length > i+1) {
   766                     rc = Integer.parseInt(args[i+1]);
   767                 }
   768             }
   769         }
   770         return rc;
   771     }
   773     /**
   774      * Scan the arguments to find the option (-tr) that setup translation rules to java source
   775      * from different sources. For example: .properties are translated using CompileProperties
   776      * The found translators are stored as suffix rules.
   777      */
   778     private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
   779         throws ProblemException, ProblemException {
   781         for (int i = 0; i<args.length; ++i) {
   782             if (args[i].equals("-tr")) {
   783                 if (i+1 >= args.length) {
   784                     throw new ProblemException("You have to specify a translate rule following -tr.");
   785                 }
   786                 String s = args[i+1];
   787                 checkTranslatePattern(s);
   788                 int ep = s.indexOf("=");
   789                 String suffix = s.substring(0,ep);
   790                 String classname = s.substring(ep+1);
   791                 if (suffix_rules.get(suffix) != null) {
   792                     throw new ProblemException("You have already specified a "+
   793                                               "rule for the suffix "+suffix);
   794                 }
   795                 if (s.equals(".class")) {
   796                     throw new ProblemException("You cannot have a translator for .class files!");
   797                 }
   798                 if (s.equals(".java")) {
   799                     throw new ProblemException("You cannot have a translator for .java files!");
   800                 }
   801                 String extra = null;
   802                 int exp = classname.indexOf(",");
   803                 if (exp != -1) {
   804                     extra = classname.substring(exp+1);
   805                     classname = classname.substring(0,exp);
   806                 }
   807                 try {
   808                     Class<?> cl = Class.forName(classname);
   809                     Transformer t = (Transformer)cl.newInstance();
   810                     t.setExtra(extra);
   811                     suffix_rules.put(suffix, t);
   812                 }
   813                 catch (Exception e) {
   814                     throw new ProblemException("Cannot use "+classname+" as a translator!");
   815                 }
   816             }
   817         }
   818     }
   820     /**
   821      * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
   822      * For example: -copy .html
   823      * The found copiers are stored as suffix rules as well. No translation is done, just copying.
   824      */
   825     private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
   826         throws ProblemException, ProblemException {
   828         for (int i = 0; i<args.length; ++i) {
   829             if (args[i].equals("-copy")) {
   830                 if (i+1 >= args.length) {
   831                     throw new ProblemException("You have to specify a translate rule following -tr.");
   832                 }
   833                 String s = args[i+1];
   834                 checkCopyPattern(s);
   835                 if (suffix_rules.get(s) != null) {
   836                     throw new ProblemException("You have already specified a "+
   837                                               "rule for the suffix "+s);
   838                 }
   839                 if (s.equals(".class")) {
   840                     throw new ProblemException("You cannot have a copy rule for .class files!");
   841                 }
   842                 if (s.equals(".java")) {
   843                     throw new ProblemException("You cannot have a copy rule for .java files!");
   844                 }
   845                 suffix_rules.put(s, javac_state.getCopier());
   846             }
   847         }
   848     }
   850     /**
   851      * Rewrite a / separated path into \ separated, but only
   852      * if we are running on a platform were File.separatorChar=='\', ie winapi.
   853      */
   854     private String fixupSeparator(String p) {
   855         if (File.separatorChar == '/') return p;
   856         return p.replaceAll("/", "\\\\");
   857     }
   859     /**
   860      * Scan the arguments for -i -x -xf -if followed by the option
   861      * -src, -sourcepath, -modulepath or -classpath and produce a map of all the
   862      * files to referenced for that particular option.
   863      *
   864      * Store the found sources and the found modules in the supplied maps.
   865      */
   866     private boolean findFiles(String[] args, String option, Set<String> suffixes,
   867                               Map<String,Source> found_files, Map<String, Module> found_modules,
   868                               Module current_module, boolean inLinksrc)
   869         throws ProblemException, ProblemException
   870     {
   871         // Track which source roots, source path roots and class path roots have been added.
   872         Set<File> roots = new HashSet<File>();
   873         // Track the current set of package includes,excludes as well as excluded source files,
   874         // to be used in the next -src/-sourcepath/-classpath
   875         List<String> includes = new LinkedList<String>();
   876         List<String> excludes = new LinkedList<String>();
   877         List<String> excludefiles = new LinkedList<String>();
   878         List<String> includefiles = new LinkedList<String>();
   879         // This include is used to find all modules in the source.
   880         List<String> moduleinfo = new LinkedList<String>();
   881         moduleinfo.add("module-info.java");
   883         for (int i = 0; i<args.length; ++i) {
   884             if (args[i].equals("-i")) {
   885                 if (i+1 >= args.length) {
   886                     throw new ProblemException("You have to specify a package pattern following -i");
   887                 }
   888                 String incl = args[i+1];
   889                 checkPattern(incl);
   890                 includes.add(incl);
   891             }
   892             if (args[i].equals("-x")) {
   893                 if (i+1 >= args.length) {
   894                     throw new ProblemException("You have to specify a package pattern following -x");
   895                 }
   896                 String excl = args[i+1];
   897                 checkPattern(excl);
   898                 excludes.add(excl);
   899             }
   900             if (args[i].equals("-xf")) {
   901                 if (i+1 >= args.length) {
   902                     throw new ProblemException("You have to specify a file following -xf");
   903                 }
   904                 String exclf = args[i+1];
   905                 checkFilePattern(exclf);
   906                 exclf = Util.normalizeDriveLetter(exclf);
   907                 excludefiles.add(fixupSeparator(exclf));
   908             }
   909             if (args[i].equals("-if")) {
   910                 if (i+1 >= args.length) {
   911                     throw new ProblemException("You have to specify a file following -xf");
   912                 }
   913                 String inclf = args[i+1];
   914                 checkFilePattern(inclf);
   915                 inclf = Util.normalizeDriveLetter(inclf);
   916                 includefiles.add(fixupSeparator(inclf));
   917             }
   918             if (args[i].equals(option)) {
   919                 if (i+1 >= args.length) {
   920                     throw new ProblemException("You have to specify a directory following "+option);
   921                 }
   922                 String[] root_dirs = args[i+1].split(File.pathSeparator);
   923                 for (String r : root_dirs) {
   924                     File root = new File(r);
   925                     if (!root.isDirectory()) {
   926                         throw new ProblemException("\""+r+"\" is not a directory.");
   927                     }
   928                     try {
   929                         root = root.getCanonicalFile();
   930                     } catch (IOException e) {
   931                         throw new ProblemException(""+e);
   932                     }
   933                     if (roots.contains(root)) {
   934                         throw new ProblemException("\""+r+"\" has already been used for "+option);
   935                     }
   936                     if (root.equals(bin_dir)) {
   937                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
   938                     }
   939                     if (root.equals(gensrc_dir)) {
   940                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
   941                     }
   942                     if (root.equals(header_dir)) {
   943                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
   944                     }
   945                     roots.add(root);
   946                     Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
   947                                     found_files, found_modules, current_module,
   948                                     findBooleanOption(args, "--permit-sources-without-package"),
   949                                     false, inLinksrc);
   950                 }
   951             }
   952             if (args[i].equals("-src") ||
   953                 args[i].equals("-sourcepath") ||
   954                 args[i].equals("-modulepath") ||
   955                 args[i].equals("-classpath") ||
   956                 args[i].equals("-cp"))
   957             {
   958                 // Reset the includes,excludes and excludefiles after they have been used.
   959                 includes = new LinkedList<String>();
   960                 excludes = new LinkedList<String>();
   961                 excludefiles = new LinkedList<String>();
   962                 includefiles = new LinkedList<String>();
   963             }
   964         }
   965         return true;
   966     }
   968 }

mercurial