aoqi@0: /* aoqi@0: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.sjavac; aoqi@0: aoqi@0: import java.io.File; aoqi@0: import java.io.IOException; aoqi@0: import java.io.PrintStream; aoqi@0: import java.util.*; aoqi@0: import java.util.regex.Matcher; aoqi@0: import java.util.regex.Pattern; aoqi@0: aoqi@0: import com.sun.tools.sjavac.server.JavacServer; aoqi@0: aoqi@0: /** aoqi@0: * The main class of the smart javac wrapper tool. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own aoqi@0: * risk. This code and its internal interfaces are subject to change aoqi@0: * or deletion without notice.

aoqi@0: */ aoqi@0: public class Main { aoqi@0: aoqi@0: /* This is a smart javac wrapper primarily used when building the OpenJDK, aoqi@0: though other projects are welcome to use it too. But please be aware aoqi@0: that it is not an official api and will change in the future. aoqi@0: (We really mean it!) aoqi@0: aoqi@0: Goals: aoqi@0: aoqi@0: ** Create a state file, containing information about the build, so aoqi@0: that incremental builds only rebuild what is necessary. Also the aoqi@0: state file can be used by make/ant to detect when to trigger aoqi@0: a call to the smart javac wrapper. aoqi@0: aoqi@0: This file is called bin/javac_state (assuming that you specified "-d bin") aoqi@0: Thus the simplest makefile is: aoqi@0: aoqi@0: SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main aoqi@0: SRCS=$(shell find src -name "*.java") aoqi@0: bin/javac_state : $(SRCS) aoqi@0: $(SJAVAC) src -d bin aoqi@0: aoqi@0: This makefile will run very fast and detect properly when Java code needs to aoqi@0: be recompiled. The smart javac wrapper will then use the information in java_state aoqi@0: to do an efficient incremental compile. aoqi@0: aoqi@0: Previously it was near enough impossible to write an efficient makefile for Java aoqi@0: with support for incremental builds and dependency tracking. aoqi@0: aoqi@0: ** Separate java sources to be compiled from java aoqi@0: sources used >only< for linking. The options: aoqi@0: aoqi@0: "dir" points to root dir with sources to be compiled aoqi@0: "-sourcepath dir" points to root dir with sources used only for linking aoqi@0: "-classpath dir" points to dir with classes used only for linking (as before) aoqi@0: aoqi@0: ** Use all cores for compilation by default. aoqi@0: "-j 4" limit the number of cores to 4. aoqi@0: For the moment, the sjavac server additionally limits the number of cores to three. aoqi@0: This will improve in the future when more sharing is performed between concurrent JavaCompilers. aoqi@0: aoqi@0: ** Basic translation support from other sources to java, and then compilation of the generated java. aoqi@0: This functionality might be moved into annotation processors instead. aoqi@0: Again this is driven by the OpenJDK sources where properties and a few other types of files aoqi@0: are converted into Java sources regularily. The javac_state embraces copy and tr, and perform aoqi@0: incremental recompiles and copying for these as well. META-INF will be a special copy rule aoqi@0: that will copy any files found below any META-INF dir in src to the bin/META-INF dir. aoqi@0: "-copy .gif" aoqi@0: "-copy META-INF" aoqi@0: "-tr .prop=com.sun.tools.javac.smart.CompileProperties aoqi@0: "-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle aoqi@0: "-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle aoqi@0: aoqi@0: ** Control which classes in the src,sourcepath and classpath that javac is allowed to see. aoqi@0: Again, this is necessary to deal with the source code structure of the OpenJDK which is aoqi@0: intricate (read messy). aoqi@0: aoqi@0: "-i tools.*" to include the tools package and all its subpackages in the build. aoqi@0: "-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages. aoqi@0: "-x tools.net.drums" to exclude the drums package only, keep its subpackages. aoqi@0: "-xf tools/net/Bar.java" // Do not compile this file... aoqi@0: "-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java! aoqi@0: "-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used. aoqi@0: aoqi@0: ** The smart javac wrapper is driven by the modification time on the source files and compared aoqi@0: to the modification times written into the javac_state file. aoqi@0: aoqi@0: It does not compare the modification time of the source with the modification time of the artifact. aoqi@0: However it will detect if the modification time of an artifact has changed compared to the java_state, aoqi@0: and this will trigger a delete of the artifact and a subsequent recompile of the source. aoqi@0: aoqi@0: The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source aoqi@0: as the final step before the output dir is finalized and immediately jared, or jmodded. The output aoqi@0: dir should be considered opaque. Do not write into the outputdir yourself! aoqi@0: Any artifacts found in the outputdir that javac_state does not know of, will be deleted! aoqi@0: This can however be prevented, using the switch --permit-unidentified-artifacts aoqi@0: This switch is necessary when build the OpenJDK because its makefiles still write directly to aoqi@0: the output classes dirs. aoqi@0: aoqi@0: Any makefile/ant rules that want to put contents into the outputdir should put the content aoqi@0: in one of several source roots. Static content that is under version control, can be put in the same source aoqi@0: code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should aoqi@0: be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments: aoqi@0: "gensrc_stuff src -d bin" aoqi@0: aoqi@0: The command line: aoqi@0: java -cp tools.jar com.sun.tools.sjavac.Main \ aoqi@0: -i "com.bar.*" -x "com.bar.foo.*" \ aoqi@0: first_root \ aoqi@0: -i "com.bar.foo.*" \ aoqi@0: second_root \ aoqi@0: -x "org.net.*" \ aoqi@0: -sourcepath link_root_sources \ aoqi@0: -classpath link_root_classes \ aoqi@0: -d bin aoqi@0: aoqi@0: Will compile all sources for package com.bar and its subpackages, found below first_root, aoqi@0: except the package com.bar.foo (and its subpackages), for which the sources are picked aoqi@0: from second_root instead. It will link against classes in link_root_classes and against aoqi@0: sources in link_root_sources, but will not see (try to link against) sources matching org.net.* aoqi@0: but will link against org.net* classes (if they exist) in link_root_classes. aoqi@0: aoqi@0: (If you want a set of complex filter rules to be applied to several source directories, without aoqi@0: having to repeat the the filter rules for each root. You can use the explicit -src option. For example: aoqi@0: sjavac -x "com.foo.*" -src root1:root2:root3 ) aoqi@0: aoqi@0: The resulting classes are written into bin. aoqi@0: */ aoqi@0: aoqi@0: // This is the final destination for classes and copied files. aoqi@0: private File bin_dir; aoqi@0: // This is where the annotation process will put generated sources. aoqi@0: private File gensrc_dir; aoqi@0: // This is where javac -h puts the generated c-header files. aoqi@0: private File header_dir; aoqi@0: aoqi@0: // This file contains the list of sources genereated by the makefile. aoqi@0: // We double check that our calculated list of sources matches this list, aoqi@0: // if not, then we terminate with an error! aoqi@0: private File makefile_source_list; aoqi@0: // The challenging task to manage an incremental build is done by javac_state. aoqi@0: private JavacState javac_state; aoqi@0: aoqi@0: // The suffix rules tells you for example, that .java files should be compiled, aoqi@0: // and .html files should be copied and .properties files be translated. aoqi@0: Map suffix_rules; aoqi@0: aoqi@0: public static void main(String... args) { aoqi@0: if (args.length > 0 && args[0].startsWith("--startserver:")) { aoqi@0: if (args.length>1) { aoqi@0: Log.error("When spawning a background server, only a single --startserver argument is allowed."); aoqi@0: return; aoqi@0: } aoqi@0: // Spawn a background server. aoqi@0: int rc = JavacServer.startServer(args[0], System.err); aoqi@0: System.exit(rc); aoqi@0: } aoqi@0: Main main = new Main(); aoqi@0: int rc = main.go(args, System.out, System.err); aoqi@0: // Remove the portfile, but only if this background=false was used. aoqi@0: JavacServer.cleanup(args); aoqi@0: System.exit(rc); aoqi@0: } aoqi@0: aoqi@0: private void printHelp() { aoqi@0: System.out.println("Usage: sjavac \n"+ aoqi@0: "where required options are:\n"+ aoqi@0: "dir Compile all sources in dir recursively\n"+ aoqi@0: "-d dir Store generated classes here and the javac_state file\n"+ aoqi@0: "--server:portfile=/tmp/abc Use a background sjavac server\n\n"+ aoqi@0: "All other arguments as javac, except -implicit:none which is forced by default.\n"+ aoqi@0: "No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+ aoqi@0: "Warning!\n"+ aoqi@0: "This tool might disappear at any time, and its command line options might change at any time!"); aoqi@0: } aoqi@0: aoqi@0: public int go(String[] args, PrintStream out, PrintStream err) { aoqi@0: try { aoqi@0: if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) { aoqi@0: printHelp(); aoqi@0: return 0; aoqi@0: } aoqi@0: aoqi@0: Log.setLogLevel(findLogLevel(args), out, err); aoqi@0: String server_settings = Util.findServerSettings(args); aoqi@0: args = verifyImplicitOption(args); aoqi@0: // Find the source root directories, and add the -src option before these, if not there already. aoqi@0: args = addSrcBeforeDirectories(args); aoqi@0: // Check that there is at least one -src supplied. aoqi@0: checkSrcOption(args); aoqi@0: // Check that there is one -d supplied. aoqi@0: bin_dir = findDirectoryOption(args,"-d","output", true, false, true); aoqi@0: gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true); aoqi@0: header_dir = findDirectoryOption(args,"-h","headers", false, false, true); aoqi@0: makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false); aoqi@0: aoqi@0: // Load the prev build state database. aoqi@0: javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir, aoqi@0: findBooleanOption(args, "--permit-unidentified-artifacts"), out, err); aoqi@0: aoqi@0: // Setup the suffix rules from the command line. aoqi@0: suffix_rules = javac_state.getJavaSuffixRule(); aoqi@0: findTranslateOptions(args, suffix_rules); aoqi@0: if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) { aoqi@0: Log.error("You have translators but no gensrc dir (-s) specified!"); aoqi@0: return -1; aoqi@0: } aoqi@0: findCopyOptions(args, suffix_rules); aoqi@0: aoqi@0: // All found modules are put here. aoqi@0: Map modules = new HashMap(); aoqi@0: // We start out in the legacy empty no-name module. aoqi@0: // As soon as we stumble on a module-info.java file we change to that module. aoqi@0: Module current_module = new Module("", ""); aoqi@0: modules.put("", current_module); aoqi@0: aoqi@0: // Find all sources, use the suffix rules to know which files are sources. aoqi@0: Map sources = new HashMap(); aoqi@0: // Find the files, this will automatically populate the found modules aoqi@0: // with found packages where the sources are found! aoqi@0: findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false); aoqi@0: aoqi@0: if (sources.isEmpty()) { aoqi@0: Log.error("Found nothing to compile!"); aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: // Create a map of all source files that are available for linking. Both -src and aoqi@0: // -sourcepath point to such files. It is possible to specify multiple aoqi@0: // -sourcepath options to enable different filtering rules. If the aoqi@0: // filters are the same for multiple sourcepaths, they may be concatenated aoqi@0: // using :(;). Before sending the list of sourcepaths to javac, they are aoqi@0: // all concatenated. The list created here is used by the SmartFileWrapper to aoqi@0: // make sure only the correct sources are actually available. aoqi@0: // We might find more modules here as well. aoqi@0: Map sources_to_link_to = new HashMap(); aoqi@0: findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true); aoqi@0: findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true); aoqi@0: // Rewrite the -src option to make it through to the javac instances. aoqi@0: rewriteOptions(args, "-src", "-sourcepath"); aoqi@0: aoqi@0: // Find all class files allowable for linking. aoqi@0: // And pickup knowledge of all modules found here. aoqi@0: // This cannot currently filter classes inside jar files. aoqi@0: // Map classes_to_link_to = new HashMap(); aoqi@0: // findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true); aoqi@0: aoqi@0: // Find all module sources allowable for linking. aoqi@0: // Map modules_to_link_to = new HashMap(); aoqi@0: // findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true); aoqi@0: aoqi@0: // Add the set of sources to the build database. aoqi@0: javac_state.now().flattenPackagesSourcesAndArtifacts(modules); aoqi@0: javac_state.now().checkInternalState("checking sources", false, sources); aoqi@0: javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to); aoqi@0: javac_state.setVisibleSources(sources_to_link_to); aoqi@0: aoqi@0: // If there is any change in the source files, taint packages aoqi@0: // and mark the database in need of saving. aoqi@0: javac_state.checkSourceStatus(false); aoqi@0: aoqi@0: // Find all existing artifacts. Their timestamp will match the last modified timestamps stored aoqi@0: // in javac_state, simply because loading of the JavacState will clean out all artifacts aoqi@0: // that do not match the javac_state database. aoqi@0: javac_state.findAllArtifacts(); aoqi@0: aoqi@0: // Remove unidentified artifacts from the bin, gensrc and header dirs. aoqi@0: // (Unless we allow them to be there.) aoqi@0: // I.e. artifacts that are not known according to the build database (javac_state). aoqi@0: // For examples, files that have been manually copied into these dirs. aoqi@0: // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp aoqi@0: // in javac_state) have already been removed when the javac_state was loaded. aoqi@0: if (!findBooleanOption(args, "--permit-unidentified-artifacts")) { aoqi@0: javac_state.removeUnidentifiedArtifacts(); aoqi@0: } aoqi@0: // Go through all sources and taint all packages that miss artifacts. aoqi@0: javac_state.taintPackagesThatMissArtifacts(); aoqi@0: aoqi@0: // Now clean out all known artifacts belonging to tainted packages. aoqi@0: javac_state.deleteClassArtifactsInTaintedPackages(); aoqi@0: // Copy files, for example property files, images files, xml files etc etc. aoqi@0: javac_state.performCopying(bin_dir, suffix_rules); aoqi@0: // Translate files, for example compile properties or compile idls. aoqi@0: javac_state.performTranslation(gensrc_dir, suffix_rules); aoqi@0: // Add any potentially generated java sources to the tobe compiled list. aoqi@0: // (Generated sources must always have a package.) aoqi@0: Map generated_sources = new HashMap(); aoqi@0: Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null, aoqi@0: generated_sources, modules, current_module, false, true, false); aoqi@0: javac_state.now().flattenPackagesSourcesAndArtifacts(modules); aoqi@0: // Recheck the the source files and their timestamps again. aoqi@0: javac_state.checkSourceStatus(true); aoqi@0: aoqi@0: // Now do a safety check that the list of source files is identical aoqi@0: // to the list Make believes we are compiling. If we do not get this aoqi@0: // right, then incremental builds will fail with subtility. aoqi@0: // If any difference is detected, then we will fail hard here. aoqi@0: // This is an important safety net. aoqi@0: javac_state.compareWithMakefileList(makefile_source_list); aoqi@0: aoqi@0: // Do the compilations, repeatedly until no tainted packages exist. aoqi@0: boolean again; aoqi@0: // Collect the name of all compiled packages. aoqi@0: Set recently_compiled = new HashSet(); aoqi@0: boolean[] rc = new boolean[1]; aoqi@0: do { aoqi@0: // Clean out artifacts in tainted packages. aoqi@0: javac_state.deleteClassArtifactsInTaintedPackages(); aoqi@0: again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc); aoqi@0: if (!rc[0]) break; aoqi@0: } while (again); aoqi@0: // Only update the state if the compile went well. aoqi@0: if (rc[0]) { aoqi@0: javac_state.save(); aoqi@0: // Reflatten only the artifacts. aoqi@0: javac_state.now().flattenArtifacts(modules); aoqi@0: // Remove artifacts that were generated during the last compile, but not this one. aoqi@0: javac_state.removeSuperfluousArtifacts(recently_compiled); aoqi@0: } aoqi@0: return rc[0] ? 0 : -1; aoqi@0: } catch (ProblemException e) { aoqi@0: Log.error(e.getMessage()); aoqi@0: return -1; aoqi@0: } catch (Exception e) { aoqi@0: e.printStackTrace(err); aoqi@0: return -1; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Are java source files passed on the command line? aoqi@0: */ aoqi@0: private boolean findJavaSourceFiles(String[] args) { aoqi@0: String prev = ""; aoqi@0: for (String s : args) { aoqi@0: if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) { aoqi@0: return true; aoqi@0: } aoqi@0: prev = s; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Is an at file passed on the command line? aoqi@0: */ aoqi@0: private boolean findAtFile(String[] args) { aoqi@0: for (String s : args) { aoqi@0: if (s.startsWith("@")) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Find the log level setting. aoqi@0: */ aoqi@0: private String findLogLevel(String[] args) { aoqi@0: for (String s : args) { aoqi@0: if (s.startsWith("--log=") && s.length()>6) { aoqi@0: return s.substring(6); aoqi@0: } aoqi@0: if (s.equals("-verbose")) { aoqi@0: return "info"; aoqi@0: } aoqi@0: } aoqi@0: return "info"; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Remove smart javac wrapper arguments, before feeding aoqi@0: * the args to the plain javac. aoqi@0: */ aoqi@0: static String[] removeWrapperArgs(String[] args) { aoqi@0: String[] out = new String[args.length]; aoqi@0: // The first source path index is remembered aoqi@0: // here. So that all following can be concatenated to it. aoqi@0: int source_path = -1; aoqi@0: // The same for class path. aoqi@0: int class_path = -1; aoqi@0: // And module path. aoqi@0: int module_path = -1; aoqi@0: int j = 0; aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a directory following "+option+"."); aoqi@0: } aoqi@0: if (args[i+1].indexOf(File.pathSeparatorChar) != -1) { aoqi@0: throw new ProblemException("You must only specify a single directory for "+option+"."); aoqi@0: } aoqi@0: dir = new File(args[i+1]); aoqi@0: if (!dir.exists()) { aoqi@0: if (!create) { aoqi@0: throw new ProblemException("This directory does not exist: "+dir.getPath()); aoqi@0: } else aoqi@0: if (!makeSureExists(dir)) { aoqi@0: throw new ProblemException("Cannot create directory "+dir.getPath()); aoqi@0: } aoqi@0: } aoqi@0: if (!dir.isDirectory()) { aoqi@0: throw new ProblemException("\""+args[i+1]+"\" is not a directory."); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: if (dir == null && needed) { aoqi@0: throw new ProblemException("You have to specify "+option); aoqi@0: } aoqi@0: try { aoqi@0: if (dir != null) aoqi@0: return dir.getCanonicalFile(); aoqi@0: } catch (IOException e) { aoqi@0: throw new ProblemException(""+e); aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Option is followed by path. aoqi@0: */ aoqi@0: private static boolean shouldBeFollowedByPath(String o) { aoqi@0: return o.equals("-s") || aoqi@0: o.equals("-h") || aoqi@0: o.equals("-d") || aoqi@0: o.equals("-sourcepath") || aoqi@0: o.equals("-classpath") || aoqi@0: o.equals("-cp") || aoqi@0: o.equals("-bootclasspath") || aoqi@0: o.equals("-src"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Add -src before source root directories if not already there. aoqi@0: */ aoqi@0: private static String[] addSrcBeforeDirectories(String[] args) { aoqi@0: List newargs = new ArrayList(); aoqi@0: for (int i = 0; i dirs = new HashSet(); aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a directory following -src."); aoqi@0: } aoqi@0: StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator); aoqi@0: while (st.hasMoreElements()) { aoqi@0: File dir = new File(st.nextToken()); aoqi@0: if (!dir.exists()) { aoqi@0: throw new ProblemException("This directory does not exist: "+dir.getPath()); aoqi@0: } aoqi@0: if (!dir.isDirectory()) { aoqi@0: throw new ProblemException("\""+dir.getPath()+"\" is not a directory."); aoqi@0: } aoqi@0: if (dirs.contains(dir)) { aoqi@0: throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!"); aoqi@0: } aoqi@0: dirs.add(dir); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: if (dirs.isEmpty()) { aoqi@0: throw new ProblemException("You have to specify -src."); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Scan the arguments to find an option that specifies a file. aoqi@0: */ aoqi@0: private static File findFileOption(String[] args, String option, String name, boolean needed) aoqi@0: throws ProblemException, ProblemException { aoqi@0: File file = null; aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a file following "+option+"."); aoqi@0: } aoqi@0: file = new File(args[i+1]); aoqi@0: if (file.isDirectory()) { aoqi@0: throw new ProblemException("\""+args[i+1]+"\" is not a file."); aoqi@0: } aoqi@0: if (!file.exists() && needed) { aoqi@0: throw new ProblemException("The file \""+args[i+1]+"\" does not exist."); aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: } aoqi@0: if (file == null && needed) { aoqi@0: throw new ProblemException("You have to specify "+option); aoqi@0: } aoqi@0: return file; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Look for a specific switch, return true if found. aoqi@0: */ aoqi@0: public static boolean findBooleanOption(String[] args, String option) { aoqi@0: for (int i = 0; i i+1) { aoqi@0: rc = Integer.parseInt(args[i+1]); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return rc; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Scan the arguments to find the option (-tr) that setup translation rules to java source aoqi@0: * from different sources. For example: .properties are translated using CompileProperties aoqi@0: * The found translators are stored as suffix rules. aoqi@0: */ aoqi@0: private static void findTranslateOptions(String[] args, Map suffix_rules) aoqi@0: throws ProblemException, ProblemException { aoqi@0: aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a translate rule following -tr."); aoqi@0: } aoqi@0: String s = args[i+1]; aoqi@0: checkTranslatePattern(s); aoqi@0: int ep = s.indexOf("="); aoqi@0: String suffix = s.substring(0,ep); aoqi@0: String classname = s.substring(ep+1); aoqi@0: if (suffix_rules.get(suffix) != null) { aoqi@0: throw new ProblemException("You have already specified a "+ aoqi@0: "rule for the suffix "+suffix); aoqi@0: } aoqi@0: if (s.equals(".class")) { aoqi@0: throw new ProblemException("You cannot have a translator for .class files!"); aoqi@0: } aoqi@0: if (s.equals(".java")) { aoqi@0: throw new ProblemException("You cannot have a translator for .java files!"); aoqi@0: } aoqi@0: String extra = null; aoqi@0: int exp = classname.indexOf(","); aoqi@0: if (exp != -1) { aoqi@0: extra = classname.substring(exp+1); aoqi@0: classname = classname.substring(0,exp); aoqi@0: } aoqi@0: try { aoqi@0: Class cl = Class.forName(classname); aoqi@0: Transformer t = (Transformer)cl.newInstance(); aoqi@0: t.setExtra(extra); aoqi@0: suffix_rules.put(suffix, t); aoqi@0: } aoqi@0: catch (Exception e) { aoqi@0: throw new ProblemException("Cannot use "+classname+" as a translator!"); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir. aoqi@0: * For example: -copy .html aoqi@0: * The found copiers are stored as suffix rules as well. No translation is done, just copying. aoqi@0: */ aoqi@0: private void findCopyOptions(String[] args, Map suffix_rules) aoqi@0: throws ProblemException, ProblemException { aoqi@0: aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a translate rule following -tr."); aoqi@0: } aoqi@0: String s = args[i+1]; aoqi@0: checkCopyPattern(s); aoqi@0: if (suffix_rules.get(s) != null) { aoqi@0: throw new ProblemException("You have already specified a "+ aoqi@0: "rule for the suffix "+s); aoqi@0: } aoqi@0: if (s.equals(".class")) { aoqi@0: throw new ProblemException("You cannot have a copy rule for .class files!"); aoqi@0: } aoqi@0: if (s.equals(".java")) { aoqi@0: throw new ProblemException("You cannot have a copy rule for .java files!"); aoqi@0: } aoqi@0: suffix_rules.put(s, javac_state.getCopier()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Rewrite a / separated path into \ separated, but only aoqi@0: * if we are running on a platform were File.separatorChar=='\', ie winapi. aoqi@0: */ aoqi@0: private String fixupSeparator(String p) { aoqi@0: if (File.separatorChar == '/') return p; aoqi@0: return p.replaceAll("/", "\\\\"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Scan the arguments for -i -x -xf -if followed by the option aoqi@0: * -src, -sourcepath, -modulepath or -classpath and produce a map of all the aoqi@0: * files to referenced for that particular option. aoqi@0: * aoqi@0: * Store the found sources and the found modules in the supplied maps. aoqi@0: */ aoqi@0: private boolean findFiles(String[] args, String option, Set suffixes, aoqi@0: Map found_files, Map found_modules, aoqi@0: Module current_module, boolean inLinksrc) aoqi@0: throws ProblemException, ProblemException aoqi@0: { aoqi@0: // Track which source roots, source path roots and class path roots have been added. aoqi@0: Set roots = new HashSet(); aoqi@0: // Track the current set of package includes,excludes as well as excluded source files, aoqi@0: // to be used in the next -src/-sourcepath/-classpath aoqi@0: List includes = new LinkedList(); aoqi@0: List excludes = new LinkedList(); aoqi@0: List excludefiles = new LinkedList(); aoqi@0: List includefiles = new LinkedList(); aoqi@0: // This include is used to find all modules in the source. aoqi@0: List moduleinfo = new LinkedList(); aoqi@0: moduleinfo.add("module-info.java"); aoqi@0: aoqi@0: for (int i = 0; i= args.length) { aoqi@0: throw new ProblemException("You have to specify a package pattern following -i"); aoqi@0: } aoqi@0: String incl = args[i+1]; aoqi@0: checkPattern(incl); aoqi@0: includes.add(incl); aoqi@0: } aoqi@0: if (args[i].equals("-x")) { aoqi@0: if (i+1 >= args.length) { aoqi@0: throw new ProblemException("You have to specify a package pattern following -x"); aoqi@0: } aoqi@0: String excl = args[i+1]; aoqi@0: checkPattern(excl); aoqi@0: excludes.add(excl); aoqi@0: } aoqi@0: if (args[i].equals("-xf")) { aoqi@0: if (i+1 >= args.length) { aoqi@0: throw new ProblemException("You have to specify a file following -xf"); aoqi@0: } aoqi@0: String exclf = args[i+1]; aoqi@0: checkFilePattern(exclf); aoqi@0: exclf = Util.normalizeDriveLetter(exclf); aoqi@0: excludefiles.add(fixupSeparator(exclf)); aoqi@0: } aoqi@0: if (args[i].equals("-if")) { aoqi@0: if (i+1 >= args.length) { aoqi@0: throw new ProblemException("You have to specify a file following -xf"); aoqi@0: } aoqi@0: String inclf = args[i+1]; aoqi@0: checkFilePattern(inclf); aoqi@0: inclf = Util.normalizeDriveLetter(inclf); aoqi@0: includefiles.add(fixupSeparator(inclf)); aoqi@0: } aoqi@0: if (args[i].equals(option)) { aoqi@0: if (i+1 >= args.length) { aoqi@0: throw new ProblemException("You have to specify a directory following "+option); aoqi@0: } aoqi@0: String[] root_dirs = args[i+1].split(File.pathSeparator); aoqi@0: for (String r : root_dirs) { aoqi@0: File root = new File(r); aoqi@0: if (!root.isDirectory()) { aoqi@0: throw new ProblemException("\""+r+"\" is not a directory."); aoqi@0: } aoqi@0: try { aoqi@0: root = root.getCanonicalFile(); aoqi@0: } catch (IOException e) { aoqi@0: throw new ProblemException(""+e); aoqi@0: } aoqi@0: if (roots.contains(root)) { aoqi@0: throw new ProblemException("\""+r+"\" has already been used for "+option); aoqi@0: } aoqi@0: if (root.equals(bin_dir)) { aoqi@0: throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d"); aoqi@0: } aoqi@0: if (root.equals(gensrc_dir)) { aoqi@0: throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s"); aoqi@0: } aoqi@0: if (root.equals(header_dir)) { aoqi@0: throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h"); aoqi@0: } aoqi@0: roots.add(root); aoqi@0: Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles, aoqi@0: found_files, found_modules, current_module, aoqi@0: findBooleanOption(args, "--permit-sources-without-package"), aoqi@0: false, inLinksrc); aoqi@0: } aoqi@0: } aoqi@0: if (args[i].equals("-src") || aoqi@0: args[i].equals("-sourcepath") || aoqi@0: args[i].equals("-modulepath") || aoqi@0: args[i].equals("-classpath") || aoqi@0: args[i].equals("-cp")) aoqi@0: { aoqi@0: // Reset the includes,excludes and excludefiles after they have been used. aoqi@0: includes = new LinkedList(); aoqi@0: excludes = new LinkedList(); aoqi@0: excludefiles = new LinkedList(); aoqi@0: includefiles = new LinkedList(); aoqi@0: } aoqi@0: } aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: