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