Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/sjavac/Main.java
38899 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.sjavac;2627import java.io.File;28import java.io.IOException;29import java.io.PrintStream;30import java.util.*;31import java.util.regex.Matcher;32import java.util.regex.Pattern;3334import com.sun.tools.sjavac.server.JavacServer;3536/**37* The main class of the smart javac wrapper tool.38*39* <p><b>This is NOT part of any supported API.40* If you write code that depends on this, you do so at your own41* risk. This code and its internal interfaces are subject to change42* or deletion without notice.</b></p>43*/44public class Main {4546/* This is a smart javac wrapper primarily used when building the OpenJDK,47though other projects are welcome to use it too. But please be aware48that it is not an official api and will change in the future.49(We really mean it!)5051Goals:5253** Create a state file, containing information about the build, so54that incremental builds only rebuild what is necessary. Also the55state file can be used by make/ant to detect when to trigger56a call to the smart javac wrapper.5758This file is called bin/javac_state (assuming that you specified "-d bin")59Thus the simplest makefile is:6061SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main62SRCS=$(shell find src -name "*.java")63bin/javac_state : $(SRCS)64$(SJAVAC) src -d bin6566This makefile will run very fast and detect properly when Java code needs to67be recompiled. The smart javac wrapper will then use the information in java_state68to do an efficient incremental compile.6970Previously it was near enough impossible to write an efficient makefile for Java71with support for incremental builds and dependency tracking.7273** Separate java sources to be compiled from java74sources used >only< for linking. The options:7576"dir" points to root dir with sources to be compiled77"-sourcepath dir" points to root dir with sources used only for linking78"-classpath dir" points to dir with classes used only for linking (as before)7980** Use all cores for compilation by default.81"-j 4" limit the number of cores to 4.82For the moment, the sjavac server additionally limits the number of cores to three.83This will improve in the future when more sharing is performed between concurrent JavaCompilers.8485** Basic translation support from other sources to java, and then compilation of the generated java.86This functionality might be moved into annotation processors instead.87Again this is driven by the OpenJDK sources where properties and a few other types of files88are converted into Java sources regularily. The javac_state embraces copy and tr, and perform89incremental recompiles and copying for these as well. META-INF will be a special copy rule90that will copy any files found below any META-INF dir in src to the bin/META-INF dir.91"-copy .gif"92"-copy META-INF"93"-tr .prop=com.sun.tools.javac.smart.CompileProperties94"-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle95"-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle9697** Control which classes in the src,sourcepath and classpath that javac is allowed to see.98Again, this is necessary to deal with the source code structure of the OpenJDK which is99intricate (read messy).100101"-i tools.*" to include the tools package and all its subpackages in the build.102"-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages.103"-x tools.net.drums" to exclude the drums package only, keep its subpackages.104"-xf tools/net/Bar.java" // Do not compile this file...105"-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java!106"-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used.107108** The smart javac wrapper is driven by the modification time on the source files and compared109to the modification times written into the javac_state file.110111It does not compare the modification time of the source with the modification time of the artifact.112However it will detect if the modification time of an artifact has changed compared to the java_state,113and this will trigger a delete of the artifact and a subsequent recompile of the source.114115The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source116as the final step before the output dir is finalized and immediately jared, or jmodded. The output117dir should be considered opaque. Do not write into the outputdir yourself!118Any artifacts found in the outputdir that javac_state does not know of, will be deleted!119This can however be prevented, using the switch --permit-unidentified-artifacts120This switch is necessary when build the OpenJDK because its makefiles still write directly to121the output classes dirs.122123Any makefile/ant rules that want to put contents into the outputdir should put the content124in one of several source roots. Static content that is under version control, can be put in the same source125code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should126be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments:127"gensrc_stuff src -d bin"128129The command line:130java -cp tools.jar com.sun.tools.sjavac.Main \131-i "com.bar.*" -x "com.bar.foo.*" \132first_root \133-i "com.bar.foo.*" \134second_root \135-x "org.net.*" \136-sourcepath link_root_sources \137-classpath link_root_classes \138-d bin139140Will compile all sources for package com.bar and its subpackages, found below first_root,141except the package com.bar.foo (and its subpackages), for which the sources are picked142from second_root instead. It will link against classes in link_root_classes and against143sources in link_root_sources, but will not see (try to link against) sources matching org.net.*144but will link against org.net* classes (if they exist) in link_root_classes.145146(If you want a set of complex filter rules to be applied to several source directories, without147having to repeat the the filter rules for each root. You can use the explicit -src option. For example:148sjavac -x "com.foo.*" -src root1:root2:root3 )149150The resulting classes are written into bin.151*/152153// This is the final destination for classes and copied files.154private File bin_dir;155// This is where the annotation process will put generated sources.156private File gensrc_dir;157// This is where javac -h puts the generated c-header files.158private File header_dir;159160// This file contains the list of sources genereated by the makefile.161// We double check that our calculated list of sources matches this list,162// if not, then we terminate with an error!163private File makefile_source_list;164// The challenging task to manage an incremental build is done by javac_state.165private JavacState javac_state;166167// The suffix rules tells you for example, that .java files should be compiled,168// and .html files should be copied and .properties files be translated.169Map<String,Transformer> suffix_rules;170171public static void main(String... args) {172if (args.length > 0 && args[0].startsWith("--startserver:")) {173if (args.length>1) {174Log.error("When spawning a background server, only a single --startserver argument is allowed.");175return;176}177// Spawn a background server.178int rc = JavacServer.startServer(args[0], System.err);179System.exit(rc);180}181Main main = new Main();182int rc = main.go(args, System.out, System.err);183// Remove the portfile, but only if this background=false was used.184JavacServer.cleanup(args);185System.exit(rc);186}187188private void printHelp() {189System.out.println("Usage: sjavac <options>\n"+190"where required options are:\n"+191"dir Compile all sources in dir recursively\n"+192"-d dir Store generated classes here and the javac_state file\n"+193"--server:portfile=/tmp/abc Use a background sjavac server\n\n"+194"All other arguments as javac, except -implicit:none which is forced by default.\n"+195"No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+196"Warning!\n"+197"This tool might disappear at any time, and its command line options might change at any time!");198}199200public int go(String[] args, PrintStream out, PrintStream err) {201try {202if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {203printHelp();204return 0;205}206207Log.setLogLevel(findLogLevel(args), out, err);208String server_settings = Util.findServerSettings(args);209args = verifyImplicitOption(args);210// Find the source root directories, and add the -src option before these, if not there already.211args = addSrcBeforeDirectories(args);212// Check that there is at least one -src supplied.213checkSrcOption(args);214// Check that there is one -d supplied.215bin_dir = findDirectoryOption(args,"-d","output", true, false, true);216gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);217header_dir = findDirectoryOption(args,"-h","headers", false, false, true);218makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);219220// Load the prev build state database.221javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,222findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);223224// Setup the suffix rules from the command line.225suffix_rules = javac_state.getJavaSuffixRule();226findTranslateOptions(args, suffix_rules);227if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {228Log.error("You have translators but no gensrc dir (-s) specified!");229return -1;230}231findCopyOptions(args, suffix_rules);232233// All found modules are put here.234Map<String,Module> modules = new HashMap<String,Module>();235// We start out in the legacy empty no-name module.236// As soon as we stumble on a module-info.java file we change to that module.237Module current_module = new Module("", "");238modules.put("", current_module);239240// Find all sources, use the suffix rules to know which files are sources.241Map<String,Source> sources = new HashMap<String,Source>();242// Find the files, this will automatically populate the found modules243// with found packages where the sources are found!244findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);245246if (sources.isEmpty()) {247Log.error("Found nothing to compile!");248return -1;249}250251// Create a map of all source files that are available for linking. Both -src and252// -sourcepath point to such files. It is possible to specify multiple253// -sourcepath options to enable different filtering rules. If the254// filters are the same for multiple sourcepaths, they may be concatenated255// using :(;). Before sending the list of sourcepaths to javac, they are256// all concatenated. The list created here is used by the SmartFileWrapper to257// make sure only the correct sources are actually available.258// We might find more modules here as well.259Map<String,Source> sources_to_link_to = new HashMap<String,Source>();260findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true);261findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);262// Rewrite the -src option to make it through to the javac instances.263rewriteOptions(args, "-src", "-sourcepath");264265// Find all class files allowable for linking.266// And pickup knowledge of all modules found here.267// This cannot currently filter classes inside jar files.268// Map<String,Source> classes_to_link_to = new HashMap<String,Source>();269// findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);270271// Find all module sources allowable for linking.272// Map<String,Source> modules_to_link_to = new HashMap<String,Source>();273// findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);274275// Add the set of sources to the build database.276javac_state.now().flattenPackagesSourcesAndArtifacts(modules);277javac_state.now().checkInternalState("checking sources", false, sources);278javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);279javac_state.setVisibleSources(sources_to_link_to);280281// If there is any change in the source files, taint packages282// and mark the database in need of saving.283javac_state.checkSourceStatus(false);284285// Find all existing artifacts. Their timestamp will match the last modified timestamps stored286// in javac_state, simply because loading of the JavacState will clean out all artifacts287// that do not match the javac_state database.288javac_state.findAllArtifacts();289290// Remove unidentified artifacts from the bin, gensrc and header dirs.291// (Unless we allow them to be there.)292// I.e. artifacts that are not known according to the build database (javac_state).293// For examples, files that have been manually copied into these dirs.294// Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp295// in javac_state) have already been removed when the javac_state was loaded.296if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {297javac_state.removeUnidentifiedArtifacts();298}299// Go through all sources and taint all packages that miss artifacts.300javac_state.taintPackagesThatMissArtifacts();301302// Now clean out all known artifacts belonging to tainted packages.303javac_state.deleteClassArtifactsInTaintedPackages();304// Copy files, for example property files, images files, xml files etc etc.305javac_state.performCopying(bin_dir, suffix_rules);306// Translate files, for example compile properties or compile idls.307javac_state.performTranslation(gensrc_dir, suffix_rules);308// Add any potentially generated java sources to the tobe compiled list.309// (Generated sources must always have a package.)310Map<String,Source> generated_sources = new HashMap<String,Source>();311Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,312generated_sources, modules, current_module, false, true, false);313javac_state.now().flattenPackagesSourcesAndArtifacts(modules);314// Recheck the the source files and their timestamps again.315javac_state.checkSourceStatus(true);316317// Now do a safety check that the list of source files is identical318// to the list Make believes we are compiling. If we do not get this319// right, then incremental builds will fail with subtility.320// If any difference is detected, then we will fail hard here.321// This is an important safety net.322javac_state.compareWithMakefileList(makefile_source_list);323324// Do the compilations, repeatedly until no tainted packages exist.325boolean again;326// Collect the name of all compiled packages.327Set<String> recently_compiled = new HashSet<String>();328boolean[] rc = new boolean[1];329do {330// Clean out artifacts in tainted packages.331javac_state.deleteClassArtifactsInTaintedPackages();332again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);333if (!rc[0]) break;334} while (again);335// Only update the state if the compile went well.336if (rc[0]) {337javac_state.save();338// Reflatten only the artifacts.339javac_state.now().flattenArtifacts(modules);340// Remove artifacts that were generated during the last compile, but not this one.341javac_state.removeSuperfluousArtifacts(recently_compiled);342}343return rc[0] ? 0 : -1;344} catch (ProblemException e) {345Log.error(e.getMessage());346return -1;347} catch (Exception e) {348e.printStackTrace(err);349return -1;350}351}352353/**354* Are java source files passed on the command line?355*/356private boolean findJavaSourceFiles(String[] args) {357String prev = "";358for (String s : args) {359if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {360return true;361}362prev = s;363}364return false;365}366367/**368* Is an at file passed on the command line?369*/370private boolean findAtFile(String[] args) {371for (String s : args) {372if (s.startsWith("@")) {373return true;374}375}376return false;377}378379/**380* Find the log level setting.381*/382private String findLogLevel(String[] args) {383for (String s : args) {384if (s.startsWith("--log=") && s.length()>6) {385return s.substring(6);386}387if (s.equals("-verbose")) {388return "info";389}390}391return "info";392}393394/**395* Remove smart javac wrapper arguments, before feeding396* the args to the plain javac.397*/398static String[] removeWrapperArgs(String[] args) {399String[] out = new String[args.length];400// The first source path index is remembered401// here. So that all following can be concatenated to it.402int source_path = -1;403// The same for class path.404int class_path = -1;405// And module path.406int module_path = -1;407int j = 0;408for (int i = 0; i<args.length; ++i) {409if (args[i].equals("-src") ||410args[i].equals("-x") ||411args[i].equals("-i") ||412args[i].equals("-xf") ||413args[i].equals("-if") ||414args[i].equals("-copy") ||415args[i].equals("-tr") ||416args[i].equals("-j")) {417// Just skip it and skip following value418i++;419} else if (args[i].startsWith("--server:")) {420// Just skip it.421} else if (args[i].startsWith("--log=")) {422// Just skip it.423} else if (args[i].equals("--permit-unidentified-artifacts")) {424// Just skip it.425} else if (args[i].equals("--permit-sources-without-package")) {426// Just skip it.427} else if (args[i].equals("--compare-found-sources")) {428// Just skip it and skip verify file name429i++;430} else if (args[i].equals("-sourcepath")) {431if (source_path == -1) {432source_path = j;433out[j] = args[i];434out[j+1] = args[i+1];435j+=2;436i++;437} else {438// Skip this and its argument, but439// append argument to found sourcepath.440out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];441i++;442}443} else if (args[i].equals("-classpath") || args[i].equals("-cp")) {444if (class_path == -1) {445class_path = j;446out[j] = args[i];447out[j+1] = args[i+1];448j+=2;449i++;450} else {451// Skip this and its argument, but452// append argument to found sourcepath.453out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];454i++;455}456} else if (args[i].equals("-modulepath")) {457if (module_path == -1) {458module_path = j;459out[j] = args[i];460out[j+1] = args[i+1];461j+=2;462i++;463} else {464// Skip this and its argument, but465// append argument to found sourcepath.466out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];467i++;468}469} else {470// Copy argument.471out[j] = args[i];472j++;473}474}475String[] ret = new String[j];476System.arraycopy(out, 0, ret, 0, j);477return ret;478}479480/**481* Make sure directory exist, create it if not.482*/483private static boolean makeSureExists(File dir) {484// Make sure the dest directories exist.485if (!dir.exists()) {486if (!dir.mkdirs()) {487Log.error("Could not create the directory "+dir.getPath());488return false;489}490}491return true;492}493494/**495* Verify that a package pattern is valid.496*/497private static void checkPattern(String s) throws ProblemException {498// Package names like foo.bar.gamma are allowed, and499// package names suffixed with .* like foo.bar.* are500// also allowed.501Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");502Matcher m = p.matcher(s);503if (!m.matches()) {504throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");505}506}507508/**509* Verify that a translate pattern is valid.510*/511private static void checkTranslatePattern(String s) throws ProblemException {512// .prop=com.sun.tools.javac.smart.CompileProperties513// .idl=com.sun.corba.CompileIdl514// .g3=antlr.CompileGrammar,debug=true515Pattern p = Pattern.compile(516"\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+517"(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");518Matcher m = p.matcher(s);519if (!m.matches()) {520throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");521}522}523524/**525* Verify that a copy pattern is valid.526*/527private static void checkCopyPattern(String s) throws ProblemException {528// .gif529// .html530Pattern p = Pattern.compile(531"\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");532Matcher m = p.matcher(s);533if (!m.matches()) {534throw new ProblemException("The string \""+s+"\" is not a proper suffix.");535}536}537538/**539* Verify that a source file name is valid.540*/541private static void checkFilePattern(String s) throws ProblemException {542// File names like foo/bar/gamma/Bar.java are allowed,543// as well as /bar/jndi.properties as well as,544// */bar/Foo.java545Pattern p = null;546if (File.separatorChar == '\\') {547p = Pattern.compile("\\*?(.+\\\\)*.+");548}549else if (File.separatorChar == '/') {550p = Pattern.compile("\\*?(.+/)*.+");551} else {552throw new ProblemException("This platform uses the unsupported "+File.separatorChar+553" as file separator character. Please add support for it!");554}555Matcher m = p.matcher(s);556if (!m.matches()) {557throw new ProblemException("The string \""+s+"\" is not a proper file name.");558}559}560561/**562* Scan the arguments to find an option is used.563*/564private static boolean hasOption(String[] args, String option) {565for (String a : args) {566if (a.equals(option)) return true;567}568return false;569}570571/**572* Check if -implicit is supplied, if so check that it is none.573* If -implicit is not supplied, supply -implicit:none574* Only implicit:none is allowed because otherwise the multicore compilations575* and dependency tracking will be tangled up.576*/577private static String[] verifyImplicitOption(String[] args)578throws ProblemException {579580boolean foundImplicit = false;581for (String a : args) {582if (a.startsWith("-implicit:")) {583foundImplicit = true;584if (!a.equals("-implicit:none")) {585throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");586}587}588}589if (foundImplicit) {590return args;591}592// -implicit:none not found lets add it.593String[] newargs = new String[args.length+1];594System.arraycopy(args,0, newargs, 0, args.length);595newargs[args.length] = "-implicit:none";596return newargs;597}598599/**600* Rewrite a single option into something else.601*/602private static void rewriteOptions(String[] args, String option, String new_option) {603for (int i=0; i<args.length; ++i) {604if (args[i].equals(option)) {605args[i] = new_option;606}607}608}609610/**611* Scan the arguments to find an option that specifies a directory.612* Create the directory if necessary.613*/614private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)615throws ProblemException, ProblemException {616File dir = null;617for (int i = 0; i<args.length; ++i) {618if (args[i].equals(option)) {619if (dir != null) {620throw new ProblemException("You have already specified the "+name+" dir!");621}622if (i+1 >= args.length) {623throw new ProblemException("You have to specify a directory following "+option+".");624}625if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {626throw new ProblemException("You must only specify a single directory for "+option+".");627}628dir = new File(args[i+1]);629if (!dir.exists()) {630if (!create) {631throw new ProblemException("This directory does not exist: "+dir.getPath());632} else633if (!makeSureExists(dir)) {634throw new ProblemException("Cannot create directory "+dir.getPath());635}636}637if (!dir.isDirectory()) {638throw new ProblemException("\""+args[i+1]+"\" is not a directory.");639}640}641}642if (dir == null && needed) {643throw new ProblemException("You have to specify "+option);644}645try {646if (dir != null)647return dir.getCanonicalFile();648} catch (IOException e) {649throw new ProblemException(""+e);650}651return null;652}653654/**655* Option is followed by path.656*/657private static boolean shouldBeFollowedByPath(String o) {658return o.equals("-s") ||659o.equals("-h") ||660o.equals("-d") ||661o.equals("-sourcepath") ||662o.equals("-classpath") ||663o.equals("-cp") ||664o.equals("-bootclasspath") ||665o.equals("-src");666}667668/**669* Add -src before source root directories if not already there.670*/671private static String[] addSrcBeforeDirectories(String[] args) {672List<String> newargs = new ArrayList<String>();673for (int i = 0; i<args.length; ++i) {674File dir = new File(args[i]);675if (dir.exists() && dir.isDirectory()) {676if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {677newargs.add("-src");678}679}680newargs.add(args[i]);681}682return newargs.toArray(new String[0]);683}684685/**686* Check the -src options.687*/688private static void checkSrcOption(String[] args)689throws ProblemException {690Set<File> dirs = new HashSet<File>();691for (int i = 0; i<args.length; ++i) {692if (args[i].equals("-src")) {693if (i+1 >= args.length) {694throw new ProblemException("You have to specify a directory following -src.");695}696StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);697while (st.hasMoreElements()) {698File dir = new File(st.nextToken());699if (!dir.exists()) {700throw new ProblemException("This directory does not exist: "+dir.getPath());701}702if (!dir.isDirectory()) {703throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");704}705if (dirs.contains(dir)) {706throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");707}708dirs.add(dir);709}710}711}712if (dirs.isEmpty()) {713throw new ProblemException("You have to specify -src.");714}715}716717/**718* Scan the arguments to find an option that specifies a file.719*/720private static File findFileOption(String[] args, String option, String name, boolean needed)721throws ProblemException, ProblemException {722File file = null;723for (int i = 0; i<args.length; ++i) {724if (args[i].equals(option)) {725if (file != null) {726throw new ProblemException("You have already specified the "+name+" file!");727}728if (i+1 >= args.length) {729throw new ProblemException("You have to specify a file following "+option+".");730}731file = new File(args[i+1]);732if (file.isDirectory()) {733throw new ProblemException("\""+args[i+1]+"\" is not a file.");734}735if (!file.exists() && needed) {736throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");737}738739}740}741if (file == null && needed) {742throw new ProblemException("You have to specify "+option);743}744return file;745}746747/**748* Look for a specific switch, return true if found.749*/750public static boolean findBooleanOption(String[] args, String option) {751for (int i = 0; i<args.length; ++i) {752if (args[i].equals(option)) return true;753}754return false;755}756757/**758* Scan the arguments to find an option that specifies a number.759*/760public static int findNumberOption(String[] args, String option) {761int rc = 0;762for (int i = 0; i<args.length; ++i) {763if (args[i].equals(option)) {764if (args.length > i+1) {765rc = Integer.parseInt(args[i+1]);766}767}768}769return rc;770}771772/**773* Scan the arguments to find the option (-tr) that setup translation rules to java source774* from different sources. For example: .properties are translated using CompileProperties775* The found translators are stored as suffix rules.776*/777private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)778throws ProblemException, ProblemException {779780for (int i = 0; i<args.length; ++i) {781if (args[i].equals("-tr")) {782if (i+1 >= args.length) {783throw new ProblemException("You have to specify a translate rule following -tr.");784}785String s = args[i+1];786checkTranslatePattern(s);787int ep = s.indexOf("=");788String suffix = s.substring(0,ep);789String classname = s.substring(ep+1);790if (suffix_rules.get(suffix) != null) {791throw new ProblemException("You have already specified a "+792"rule for the suffix "+suffix);793}794if (s.equals(".class")) {795throw new ProblemException("You cannot have a translator for .class files!");796}797if (s.equals(".java")) {798throw new ProblemException("You cannot have a translator for .java files!");799}800String extra = null;801int exp = classname.indexOf(",");802if (exp != -1) {803extra = classname.substring(exp+1);804classname = classname.substring(0,exp);805}806try {807Class<?> cl = Class.forName(classname);808Transformer t = (Transformer)cl.newInstance();809t.setExtra(extra);810suffix_rules.put(suffix, t);811}812catch (Exception e) {813throw new ProblemException("Cannot use "+classname+" as a translator!");814}815}816}817}818819/**820* Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.821* For example: -copy .html822* The found copiers are stored as suffix rules as well. No translation is done, just copying.823*/824private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)825throws ProblemException, ProblemException {826827for (int i = 0; i<args.length; ++i) {828if (args[i].equals("-copy")) {829if (i+1 >= args.length) {830throw new ProblemException("You have to specify a translate rule following -tr.");831}832String s = args[i+1];833checkCopyPattern(s);834if (suffix_rules.get(s) != null) {835throw new ProblemException("You have already specified a "+836"rule for the suffix "+s);837}838if (s.equals(".class")) {839throw new ProblemException("You cannot have a copy rule for .class files!");840}841if (s.equals(".java")) {842throw new ProblemException("You cannot have a copy rule for .java files!");843}844suffix_rules.put(s, javac_state.getCopier());845}846}847}848849/**850* Rewrite a / separated path into \ separated, but only851* if we are running on a platform were File.separatorChar=='\', ie winapi.852*/853private String fixupSeparator(String p) {854if (File.separatorChar == '/') return p;855return p.replaceAll("/", "\\\\");856}857858/**859* Scan the arguments for -i -x -xf -if followed by the option860* -src, -sourcepath, -modulepath or -classpath and produce a map of all the861* files to referenced for that particular option.862*863* Store the found sources and the found modules in the supplied maps.864*/865private boolean findFiles(String[] args, String option, Set<String> suffixes,866Map<String,Source> found_files, Map<String, Module> found_modules,867Module current_module, boolean inLinksrc)868throws ProblemException, ProblemException869{870// Track which source roots, source path roots and class path roots have been added.871Set<File> roots = new HashSet<File>();872// Track the current set of package includes,excludes as well as excluded source files,873// to be used in the next -src/-sourcepath/-classpath874List<String> includes = new LinkedList<String>();875List<String> excludes = new LinkedList<String>();876List<String> excludefiles = new LinkedList<String>();877List<String> includefiles = new LinkedList<String>();878// This include is used to find all modules in the source.879List<String> moduleinfo = new LinkedList<String>();880moduleinfo.add("module-info.java");881882for (int i = 0; i<args.length; ++i) {883if (args[i].equals("-i")) {884if (i+1 >= args.length) {885throw new ProblemException("You have to specify a package pattern following -i");886}887String incl = args[i+1];888checkPattern(incl);889includes.add(incl);890}891if (args[i].equals("-x")) {892if (i+1 >= args.length) {893throw new ProblemException("You have to specify a package pattern following -x");894}895String excl = args[i+1];896checkPattern(excl);897excludes.add(excl);898}899if (args[i].equals("-xf")) {900if (i+1 >= args.length) {901throw new ProblemException("You have to specify a file following -xf");902}903String exclf = args[i+1];904checkFilePattern(exclf);905exclf = Util.normalizeDriveLetter(exclf);906excludefiles.add(fixupSeparator(exclf));907}908if (args[i].equals("-if")) {909if (i+1 >= args.length) {910throw new ProblemException("You have to specify a file following -xf");911}912String inclf = args[i+1];913checkFilePattern(inclf);914inclf = Util.normalizeDriveLetter(inclf);915includefiles.add(fixupSeparator(inclf));916}917if (args[i].equals(option)) {918if (i+1 >= args.length) {919throw new ProblemException("You have to specify a directory following "+option);920}921String[] root_dirs = args[i+1].split(File.pathSeparator);922for (String r : root_dirs) {923File root = new File(r);924if (!root.isDirectory()) {925throw new ProblemException("\""+r+"\" is not a directory.");926}927try {928root = root.getCanonicalFile();929} catch (IOException e) {930throw new ProblemException(""+e);931}932if (roots.contains(root)) {933throw new ProblemException("\""+r+"\" has already been used for "+option);934}935if (root.equals(bin_dir)) {936throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");937}938if (root.equals(gensrc_dir)) {939throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");940}941if (root.equals(header_dir)) {942throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");943}944roots.add(root);945Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,946found_files, found_modules, current_module,947findBooleanOption(args, "--permit-sources-without-package"),948false, inLinksrc);949}950}951if (args[i].equals("-src") ||952args[i].equals("-sourcepath") ||953args[i].equals("-modulepath") ||954args[i].equals("-classpath") ||955args[i].equals("-cp"))956{957// Reset the includes,excludes and excludefiles after they have been used.958includes = new LinkedList<String>();959excludes = new LinkedList<String>();960excludefiles = new LinkedList<String>();961includefiles = new LinkedList<String>();962}963}964return true;965}966967}968969970971