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

Mon, 03 Jun 2013 17:09:26 -0700

author
jjg
date
Mon, 03 Jun 2013 17:09:26 -0700
changeset 1796
242bcad5be74
parent 1648
a03c4a86ea2b
child 1953
71b0089b146f
permissions
-rw-r--r--

8006615: [doclint] move remaining messages into resource bundle
Reviewed-by: mcimadamore, vromero

     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             // Find all source files allowable for linking.
   253             // We might find more modules here as well.
   254             Map<String,Source> sources_to_link_to = new HashMap<String,Source>();
   255             // Always reuse -src for linking as well! This means that we might
   256             // get two -sourcepath on the commandline after the rewrite, which is
   257             // fine. We can have as many as we like. You need to have separate -src/-sourcepath/-classpath
   258             // if you need different filtering rules for different roots. If you have the same filtering
   259             // rules for all sourcepath roots, you can concatenate them using :(;) as before.
   260               rewriteOptions(args, "-src", "-sourcepath");
   261             findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
   263             // Find all class files allowable for linking.
   264             // And pickup knowledge of all modules found here.
   265             // This cannot currently filter classes inside jar files.
   266 //          Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
   267 //          findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
   269             // Find all module sources allowable for linking.
   270 //          Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
   271 //          findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
   273             // Add the set of sources to the build database.
   274             javac_state.now().collectPackagesSourcesAndArtifacts(modules);
   275             javac_state.now().checkInternalState("checking sources", false, sources);
   276             javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
   277             javac_state.setVisibleSources(sources_to_link_to);
   279             // If there is any change in the source files, taint packages
   280             // and mark the database in need of saving.
   281             javac_state.checkSourceStatus(false);
   283             // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
   284             // in javac_state, simply because loading of the JavacState will clean out all artifacts
   285             // that do not match the javac_state database.
   286             javac_state.findAllArtifacts();
   288             // Remove unidentified artifacts from the bin, gensrc and header dirs.
   289             // (Unless we allow them to be there.)
   290             // I.e. artifacts that are not known according to the build database (javac_state).
   291             // For examples, files that have been manually copied into these dirs.
   292             // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
   293             // in javac_state) have already been removed when the javac_state was loaded.
   294             if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
   295                 javac_state.removeUnidentifiedArtifacts();
   296             }
   297             // Go through all sources and taint all packages that miss artifacts.
   298             javac_state.taintPackagesThatMissArtifacts();
   300             // Now clean out all known artifacts belonging to tainted packages.
   301             javac_state.deleteClassArtifactsInTaintedPackages();
   302             // Copy files, for example property files, images files, xml files etc etc.
   303             javac_state.performCopying(bin_dir, suffix_rules);
   304             // Translate files, for example compile properties or compile idls.
   305             javac_state.performTranslation(gensrc_dir, suffix_rules);
   306             // Add any potentially generated java sources to the tobe compiled list.
   307             // (Generated sources must always have a package.)
   308             Map<String,Source> generated_sources = new HashMap<String,Source>();
   309             Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
   310                    generated_sources, modules, current_module, false, true, false);
   311             javac_state.now().collectPackagesSourcesAndArtifacts(modules);
   312             // Recheck the the source files and their timestamps again.
   313             javac_state.checkSourceStatus(true);
   315             // Now do a safety check that the list of source files is identical
   316             // to the list Make believes we are compiling. If we do not get this
   317             // right, then incremental builds will fail with subtility.
   318             // If any difference is detected, then we will fail hard here.
   319             // This is an important safety net.
   320             javac_state.compareWithMakefileList(makefile_source_list);
   322             // Do the compilations, repeatedly until no tainted packages exist.
   323             boolean again;
   324             // Collect the name of all compiled packages.
   325             Set<String> recently_compiled = new HashSet<String>();
   326             boolean[] rc = new boolean[1];
   327             do {
   328                 // Clean out artifacts in tainted packages.
   329                 javac_state.deleteClassArtifactsInTaintedPackages();
   330                 again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
   331                 if (!rc[0]) break;
   332             } while (again);
   333             // Only update the state if the compile went well.
   334             if (rc[0]) {
   335                 javac_state.save();
   336                 // Collect all the artifacts.
   337                 javac_state.now().collectArtifacts(modules);
   338                 // Remove artifacts that were generated during the last compile, but not this one.
   339                 javac_state.removeSuperfluousArtifacts(recently_compiled);
   340             }
   341             return rc[0] ? 0 : -1;
   342         } catch (ProblemException e) {
   343             Log.error(e.getMessage());
   344             return -1;
   345         } catch (Exception e) {
   346             e.printStackTrace(err);
   347             return -1;
   348         }
   349     }
   351     /**
   352      * Are java source files passed on the command line?
   353      */
   354     private boolean findJavaSourceFiles(String[] args) {
   355         String prev = "";
   356         for (String s : args) {
   357             if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
   358                 return true;
   359             }
   360             prev = s;
   361         }
   362         return false;
   363     }
   365     /**
   366      * Is an at file passed on the command line?
   367      */
   368     private boolean findAtFile(String[] args) {
   369         for (String s : args) {
   370             if (s.startsWith("@")) {
   371                 return true;
   372             }
   373         }
   374         return false;
   375     }
   377     /**
   378      * Find the log level setting.
   379      */
   380     private String findLogLevel(String[] args) {
   381         for (String s : args) {
   382             if (s.startsWith("--log=") && s.length()>6) {
   383                 return s.substring(6);
   384             }
   385             if (s.equals("-verbose")) {
   386                 return "info";
   387             }
   388         }
   389         return "info";
   390     }
   392     /**
   393      * Remove smart javac wrapper arguments, before feeding
   394      * the args to the plain javac.
   395      */
   396     static String[] removeWrapperArgs(String[] args) {
   397         String[] out = new String[args.length];
   398         // The first source path index is remembered
   399         // here. So that all following can be concatenated to it.
   400         int source_path = -1;
   401         // The same for class path.
   402         int class_path = -1;
   403         // And module path.
   404         int module_path = -1;
   405         int j = 0;
   406         for (int i = 0; i<args.length; ++i) {
   407             if (args[i].equals("-src") ||
   408                 args[i].equals("-x") ||
   409                 args[i].equals("-i") ||
   410                 args[i].equals("-xf") ||
   411                 args[i].equals("-if") ||
   412                 args[i].equals("-copy") ||
   413                 args[i].equals("-tr") ||
   414                 args[i].equals("-j")) {
   415                 // Just skip it and skip following value
   416                 i++;
   417             } else if (args[i].startsWith("--server:")) {
   418                 // Just skip it.
   419             } else if (args[i].startsWith("--log=")) {
   420                 // Just skip it.
   421             } else if (args[i].equals("--permit-unidentified-artifacts")) {
   422                 // Just skip it.
   423             } else if (args[i].equals("--permit-sources-without-package")) {
   424                 // Just skip it.
   425             } else if (args[i].equals("--compare-found-sources")) {
   426                 // Just skip it and skip verify file name
   427                 i++;
   428             } else if (args[i].equals("-sourcepath")) {
   429                 if (source_path == -1) {
   430                     source_path = j;
   431                     out[j] = args[i];
   432                     out[j+1] = args[i+1];
   433                     j+=2;
   434                     i++;
   435                 } else {
   436                     // Skip this and its argument, but
   437                     // append argument to found sourcepath.
   438                     out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];
   439                     i++;
   440                 }
   441             } else if (args[i].equals("-classpath") || args[i].equals("-cp")) {
   442                 if (class_path == -1) {
   443                     class_path = j;
   444                     out[j] = args[i];
   445                     out[j+1] = args[i+1];
   446                     j+=2;
   447                     i++;
   448                 } else {
   449                     // Skip this and its argument, but
   450                     // append argument to found sourcepath.
   451                     out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];
   452                     i++;
   453                 }
   454             } else if (args[i].equals("-modulepath")) {
   455                 if (module_path == -1) {
   456                     module_path = j;
   457                     out[j] = args[i];
   458                     out[j+1] = args[i+1];
   459                     j+=2;
   460                     i++;
   461                 } else {
   462                     // Skip this and its argument, but
   463                     // append argument to found sourcepath.
   464                     out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];
   465                     i++;
   466                 }
   467              } else {
   468                 // Copy argument.
   469                 out[j] = args[i];
   470                 j++;
   471             }
   472         }
   473         String[] ret = new String[j];
   474         System.arraycopy(out, 0, ret, 0, j);
   475         return ret;
   476     }
   478     /**
   479      * Make sure directory exist, create it if not.
   480      */
   481     private static boolean makeSureExists(File dir) {
   482         // Make sure the dest directories exist.
   483         if (!dir.exists()) {
   484             if (!dir.mkdirs()) {
   485                 Log.error("Could not create the directory "+dir.getPath());
   486                 return false;
   487             }
   488         }
   489         return true;
   490     }
   492     /**
   493      * Verify that a package pattern is valid.
   494      */
   495     private static void checkPattern(String s) throws ProblemException {
   496         // Package names like foo.bar.gamma are allowed, and
   497         // package names suffixed with .* like foo.bar.* are
   498         // also allowed.
   499         Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
   500         Matcher m = p.matcher(s);
   501         if (!m.matches()) {
   502             throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
   503         }
   504     }
   506     /**
   507      * Verify that a translate pattern is valid.
   508      */
   509     private static void checkTranslatePattern(String s) throws ProblemException {
   510         // .prop=com.sun.tools.javac.smart.CompileProperties
   511         // .idl=com.sun.corba.CompileIdl
   512         // .g3=antlr.CompileGrammar,debug=true
   513         Pattern p = Pattern.compile(
   514             "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+
   515             "(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");
   516         Matcher m = p.matcher(s);
   517         if (!m.matches()) {
   518             throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");
   519         }
   520     }
   522     /**
   523      * Verify that a copy pattern is valid.
   524      */
   525     private static void checkCopyPattern(String s) throws ProblemException {
   526         // .gif
   527         // .html
   528         Pattern p = Pattern.compile(
   529             "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");
   530         Matcher m = p.matcher(s);
   531         if (!m.matches()) {
   532             throw new ProblemException("The string \""+s+"\" is not a proper suffix.");
   533         }
   534     }
   536     /**
   537      * Verify that a source file name is valid.
   538      */
   539     private static void checkFilePattern(String s) throws ProblemException {
   540         // File names like foo/bar/gamma/Bar.java are allowed,
   541         // as well as /bar/jndi.properties as well as,
   542         // */bar/Foo.java
   543         Pattern p = null;
   544         if (File.separatorChar == '\\') {
   545             p = Pattern.compile("\\*?(.+\\\\)*.+");
   546         }
   547         else if (File.separatorChar == '/') {
   548             p = Pattern.compile("\\*?(.+/)*.+");
   549         } else {
   550             throw new ProblemException("This platform uses the unsupported "+File.separatorChar+
   551                                       " as file separator character. Please add support for it!");
   552         }
   553         Matcher m = p.matcher(s);
   554         if (!m.matches()) {
   555             throw new ProblemException("The string \""+s+"\" is not a proper file name.");
   556         }
   557     }
   559     /**
   560      * Scan the arguments to find an option is used.
   561      */
   562     private static boolean hasOption(String[] args, String option) {
   563         for (String a : args) {
   564             if (a.equals(option)) return true;
   565         }
   566         return false;
   567     }
   569     /**
   570      * Check if -implicit is supplied, if so check that it is none.
   571      * If -implicit is not supplied, supply -implicit:none
   572      * Only implicit:none is allowed because otherwise the multicore compilations
   573      * and dependency tracking will be tangled up.
   574      */
   575     private static String[] verifyImplicitOption(String[] args)
   576         throws ProblemException {
   578         boolean foundImplicit = false;
   579         for (String a : args) {
   580             if (a.startsWith("-implicit:")) {
   581                 foundImplicit = true;
   582                 if (!a.equals("-implicit:none")) {
   583                     throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");
   584                 }
   585             }
   586         }
   587         if (foundImplicit) {
   588             return args;
   589         }
   590         // -implicit:none not found lets add it.
   591         String[] newargs = new String[args.length+1];
   592         System.arraycopy(args,0, newargs, 0, args.length);
   593         newargs[args.length] = "-implicit:none";
   594         return newargs;
   595     }
   597     /**
   598      * Rewrite a single option into something else.
   599      */
   600     private static void rewriteOptions(String[] args, String option, String new_option) {
   601         for (int i=0; i<args.length; ++i) {
   602             if (args[i].equals(option)) {
   603                 args[i] = new_option;
   604             }
   605         }
   606     }
   608     /**
   609      * Scan the arguments to find an option that specifies a directory.
   610      * Create the directory if necessary.
   611      */
   612     private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)
   613         throws ProblemException, ProblemException {
   614         File dir = null;
   615         for (int i = 0; i<args.length; ++i) {
   616             if (args[i].equals(option)) {
   617                 if (dir != null) {
   618                     throw new ProblemException("You have already specified the "+name+" dir!");
   619                 }
   620                 if (i+1 >= args.length) {
   621                     throw new ProblemException("You have to specify a directory following "+option+".");
   622                 }
   623                 if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {
   624                     throw new ProblemException("You must only specify a single directory for "+option+".");
   625                 }
   626                 dir = new File(args[i+1]);
   627                 if (!dir.exists()) {
   628                     if (!create) {
   629                          throw new ProblemException("This directory does not exist: "+dir.getPath());
   630                     } else
   631                     if (!makeSureExists(dir)) {
   632                         throw new ProblemException("Cannot create directory "+dir.getPath());
   633                     }
   634                 }
   635                 if (!dir.isDirectory()) {
   636                     throw new ProblemException("\""+args[i+1]+"\" is not a directory.");
   637                 }
   638             }
   639         }
   640         if (dir == null && needed) {
   641             throw new ProblemException("You have to specify "+option);
   642         }
   643         try {
   644             if (dir != null)
   645                 return dir.getCanonicalFile();
   646         } catch (IOException e) {
   647             throw new ProblemException(""+e);
   648         }
   649         return null;
   650     }
   652     /**
   653      * Option is followed by path.
   654      */
   655     private static boolean shouldBeFollowedByPath(String o) {
   656         return o.equals("-s") ||
   657                o.equals("-h") ||
   658                o.equals("-d") ||
   659                o.equals("-sourcepath") ||
   660                o.equals("-classpath") ||
   661                o.equals("-cp") ||
   662                o.equals("-bootclasspath") ||
   663                o.equals("-src");
   664     }
   666     /**
   667      * Add -src before source root directories if not already there.
   668      */
   669     private static String[] addSrcBeforeDirectories(String[] args) {
   670         List<String> newargs = new ArrayList<String>();
   671         for (int i = 0; i<args.length; ++i) {
   672             File dir = new File(args[i]);
   673             if (dir.exists() && dir.isDirectory()) {
   674                 if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {
   675                     newargs.add("-src");
   676                 }
   677             }
   678             newargs.add(args[i]);
   679         }
   680         return newargs.toArray(new String[0]);
   681     }
   683     /**
   684      * Check the -src options.
   685      */
   686     private static void checkSrcOption(String[] args)
   687         throws ProblemException {
   688         Set<File> dirs = new HashSet<File>();
   689         for (int i = 0; i<args.length; ++i) {
   690             if (args[i].equals("-src")) {
   691                 if (i+1 >= args.length) {
   692                     throw new ProblemException("You have to specify a directory following -src.");
   693                 }
   694                 StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);
   695                 while (st.hasMoreElements()) {
   696                     File dir = new File(st.nextToken());
   697                     if (!dir.exists()) {
   698                         throw new ProblemException("This directory does not exist: "+dir.getPath());
   699                     }
   700                     if (!dir.isDirectory()) {
   701                         throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");
   702                     }
   703                     if (dirs.contains(dir)) {
   704                         throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");
   705                     }
   706                     dirs.add(dir);
   707                 }
   708             }
   709         }
   710         if (dirs.isEmpty()) {
   711             throw new ProblemException("You have to specify -src.");
   712         }
   713     }
   715     /**
   716      * Scan the arguments to find an option that specifies a file.
   717      */
   718     private static File findFileOption(String[] args, String option, String name, boolean needed)
   719         throws ProblemException, ProblemException {
   720         File file = null;
   721         for (int i = 0; i<args.length; ++i) {
   722             if (args[i].equals(option)) {
   723                 if (file != null) {
   724                     throw new ProblemException("You have already specified the "+name+" file!");
   725                 }
   726                 if (i+1 >= args.length) {
   727                     throw new ProblemException("You have to specify a file following "+option+".");
   728                 }
   729                 file = new File(args[i+1]);
   730                 if (file.isDirectory()) {
   731                     throw new ProblemException("\""+args[i+1]+"\" is not a file.");
   732                 }
   733                 if (!file.exists() && needed) {
   734                     throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");
   735                 }
   737             }
   738         }
   739         if (file == null && needed) {
   740             throw new ProblemException("You have to specify "+option);
   741         }
   742         return file;
   743     }
   745     /**
   746      * Look for a specific switch, return true if found.
   747      */
   748     public static boolean findBooleanOption(String[] args, String option) {
   749         for (int i = 0; i<args.length; ++i) {
   750             if (args[i].equals(option)) return true;
   751         }
   752         return false;
   753     }
   755     /**
   756      * Scan the arguments to find an option that specifies a number.
   757      */
   758     public static int findNumberOption(String[] args, String option) {
   759         int rc = 0;
   760         for (int i = 0; i<args.length; ++i) {
   761             if (args[i].equals(option)) {
   762                 if (args.length > i+1) {
   763                     rc = Integer.parseInt(args[i+1]);
   764                 }
   765             }
   766         }
   767         return rc;
   768     }
   770     /**
   771      * Scan the arguments to find the option (-tr) that setup translation rules to java source
   772      * from different sources. For example: .properties are translated using CompileProperties
   773      * The found translators are stored as suffix rules.
   774      */
   775     private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
   776         throws ProblemException, ProblemException {
   778         for (int i = 0; i<args.length; ++i) {
   779             if (args[i].equals("-tr")) {
   780                 if (i+1 >= args.length) {
   781                     throw new ProblemException("You have to specify a translate rule following -tr.");
   782                 }
   783                 String s = args[i+1];
   784                 checkTranslatePattern(s);
   785                 int ep = s.indexOf("=");
   786                 String suffix = s.substring(0,ep);
   787                 String classname = s.substring(ep+1);
   788                 if (suffix_rules.get(suffix) != null) {
   789                     throw new ProblemException("You have already specified a "+
   790                                               "rule for the suffix "+suffix);
   791                 }
   792                 if (s.equals(".class")) {
   793                     throw new ProblemException("You cannot have a translator for .class files!");
   794                 }
   795                 if (s.equals(".java")) {
   796                     throw new ProblemException("You cannot have a translator for .java files!");
   797                 }
   798                 String extra = null;
   799                 int exp = classname.indexOf(",");
   800                 if (exp != -1) {
   801                     extra = classname.substring(exp+1);
   802                     classname = classname.substring(0,exp);
   803                 }
   804                 try {
   805                     Class<?> cl = Class.forName(classname);
   806                     Transformer t = (Transformer)cl.newInstance();
   807                     t.setExtra(extra);
   808                     suffix_rules.put(suffix, t);
   809                 }
   810                 catch (Exception e) {
   811                     throw new ProblemException("Cannot use "+classname+" as a translator!");
   812                 }
   813             }
   814         }
   815     }
   817     /**
   818      * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
   819      * For example: -copy .html
   820      * The found copiers are stored as suffix rules as well. No translation is done, just copying.
   821      */
   822     private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
   823         throws ProblemException, ProblemException {
   825         for (int i = 0; i<args.length; ++i) {
   826             if (args[i].equals("-copy")) {
   827                 if (i+1 >= args.length) {
   828                     throw new ProblemException("You have to specify a translate rule following -tr.");
   829                 }
   830                 String s = args[i+1];
   831                 checkCopyPattern(s);
   832                 if (suffix_rules.get(s) != null) {
   833                     throw new ProblemException("You have already specified a "+
   834                                               "rule for the suffix "+s);
   835                 }
   836                 if (s.equals(".class")) {
   837                     throw new ProblemException("You cannot have a copy rule for .class files!");
   838                 }
   839                 if (s.equals(".java")) {
   840                     throw new ProblemException("You cannot have a copy rule for .java files!");
   841                 }
   842                 suffix_rules.put(s, javac_state.getCopier());
   843             }
   844         }
   845     }
   847     /**
   848      * Rewrite a / separated path into \ separated, but only
   849      * if we are running on a platform were File.separatorChar=='\', ie winapi.
   850      */
   851     private String fixupSeparator(String p) {
   852         if (File.separatorChar == '/') return p;
   853         return p.replaceAll("/", "\\\\");
   854     }
   856     /**
   857      * Scan the arguments for -i -x -xf -if followed by the option
   858      * -src, -sourcepath, -modulepath or -classpath and produce a map of all the
   859      * files to referenced for that particular option.
   860      *
   861      * Store the found sources and the found modules in the supplied maps.
   862      */
   863     private boolean findFiles(String[] args, String option, Set<String> suffixes,
   864                               Map<String,Source> found_files, Map<String, Module> found_modules,
   865                               Module current_module, boolean inLinksrc)
   866         throws ProblemException, ProblemException
   867     {
   868         // Track which source roots, source path roots and class path roots have been added.
   869         Set<File> roots = new HashSet<File>();
   870         // Track the current set of package includes,excludes as well as excluded source files,
   871         // to be used in the next -src/-sourcepath/-classpath
   872         List<String> includes = new LinkedList<String>();
   873         List<String> excludes = new LinkedList<String>();
   874         List<String> excludefiles = new LinkedList<String>();
   875         List<String> includefiles = new LinkedList<String>();
   876         // This include is used to find all modules in the source.
   877         List<String> moduleinfo = new LinkedList<String>();
   878         moduleinfo.add("module-info.java");
   880         for (int i = 0; i<args.length; ++i) {
   881             if (args[i].equals("-i")) {
   882                 if (i+1 >= args.length) {
   883                     throw new ProblemException("You have to specify a package pattern following -i");
   884                 }
   885                 String incl = args[i+1];
   886                 checkPattern(incl);
   887                 includes.add(incl);
   888             }
   889             if (args[i].equals("-x")) {
   890                 if (i+1 >= args.length) {
   891                     throw new ProblemException("You have to specify a package pattern following -x");
   892                 }
   893                 String excl = args[i+1];
   894                 checkPattern(excl);
   895                 excludes.add(excl);
   896             }
   897             if (args[i].equals("-xf")) {
   898                 if (i+1 >= args.length) {
   899                     throw new ProblemException("You have to specify a file following -xf");
   900                 }
   901                 String exclf = args[i+1];
   902                 checkFilePattern(exclf);
   903                 exclf = Util.normalizeDriveLetter(exclf);
   904                 excludefiles.add(fixupSeparator(exclf));
   905             }
   906             if (args[i].equals("-if")) {
   907                 if (i+1 >= args.length) {
   908                     throw new ProblemException("You have to specify a file following -xf");
   909                 }
   910                 String inclf = args[i+1];
   911                 checkFilePattern(inclf);
   912                 inclf = Util.normalizeDriveLetter(inclf);
   913                 includefiles.add(fixupSeparator(inclf));
   914             }
   915             if (args[i].equals(option)) {
   916                 if (i+1 >= args.length) {
   917                     throw new ProblemException("You have to specify a directory following "+option);
   918                 }
   919                 String[] root_dirs = args[i+1].split(File.pathSeparator);
   920                 for (String r : root_dirs) {
   921                     File root = new File(r);
   922                     if (!root.isDirectory()) {
   923                         throw new ProblemException("\""+r+"\" is not a directory.");
   924                     }
   925                     try {
   926                         root = root.getCanonicalFile();
   927                     } catch (IOException e) {
   928                         throw new ProblemException(""+e);
   929                     }
   930                     if (roots.contains(root)) {
   931                         throw new ProblemException("\""+r+"\" has already been used for "+option);
   932                     }
   933                     if (root.equals(bin_dir)) {
   934                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
   935                     }
   936                     if (root.equals(gensrc_dir)) {
   937                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
   938                     }
   939                     if (root.equals(header_dir)) {
   940                         throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
   941                     }
   942                     roots.add(root);
   943                     Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
   944                                     found_files, found_modules, current_module,
   945                                     findBooleanOption(args, "--permit-sources-without-package"),
   946                                     false, inLinksrc);
   947                 }
   948             }
   949             if (args[i].equals("-src") ||
   950                 args[i].equals("-sourcepath") ||
   951                 args[i].equals("-modulepath") ||
   952                 args[i].equals("-classpath") ||
   953                 args[i].equals("-cp"))
   954             {
   955                 // Reset the includes,excludes and excludefiles after they have been used.
   956                 includes = new LinkedList<String>();
   957                 excludes = new LinkedList<String>();
   958                 excludefiles = new LinkedList<String>();
   959                 includefiles = new LinkedList<String>();
   960             }
   961         }
   962         return true;
   963     }
   965 }

mercurial