changeset 24067:76e7b6bbbd85

8035063: Option handling in sjavac needs to be rewritten Summary: Option handling code rewritten. Exclusion / inclusion patterns changed from package to directories. Reviewed-by: jjg, jfranck
author alundblad
date Tue, 22 Apr 2014 16:51:10 +0200
parents 1dfb66929538
children d8a1180faaa9
files langtools/src/share/classes/com/sun/tools/sjavac/CleanProperties.java langtools/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java langtools/src/share/classes/com/sun/tools/sjavac/CompileProperties.java langtools/src/share/classes/com/sun/tools/sjavac/CopyFile.java langtools/src/share/classes/com/sun/tools/sjavac/JavacState.java langtools/src/share/classes/com/sun/tools/sjavac/Log.java langtools/src/share/classes/com/sun/tools/sjavac/Main.java langtools/src/share/classes/com/sun/tools/sjavac/Source.java langtools/src/share/classes/com/sun/tools/sjavac/Transformer.java langtools/src/share/classes/com/sun/tools/sjavac/Util.java langtools/src/share/classes/com/sun/tools/sjavac/options/ArgumentIterator.java langtools/src/share/classes/com/sun/tools/sjavac/options/Option.java langtools/src/share/classes/com/sun/tools/sjavac/options/OptionHelper.java langtools/src/share/classes/com/sun/tools/sjavac/options/Options.java langtools/src/share/classes/com/sun/tools/sjavac/options/SourceLocation.java langtools/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java langtools/src/share/classes/com/sun/tools/sjavac/server/PortFile.java langtools/test/tools/sjavac/ExclPattern.java langtools/test/tools/sjavac/ExclPatternWrapper.java langtools/test/tools/sjavac/JavacOptionPrep.java langtools/test/tools/sjavac/JavacOptionPrepWrapper.java langtools/test/tools/sjavac/OptionDecoding.java langtools/test/tools/sjavac/OptionDecodingWrapper.java langtools/test/tools/sjavac/SJavacTestUtil.java langtools/test/tools/sjavac/SJavacWrapper.java langtools/test/tools/sjavac/Serialization.java langtools/test/tools/sjavac/SerializationWrapper.java langtools/test/tools/sjavac/util/OptionTestUtil.java
diffstat 28 files changed, 2429 insertions(+), 909 deletions(-) [+]
line wrap: on
line diff
--- a/langtools/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,13 +29,14 @@
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Properties;
 
+import com.sun.tools.sjavac.options.Options;
+
 /**
  * The clean properties transform should not be necessary.
  * Eventually we will cleanup the property file sources in the OpenJDK instead.
@@ -51,7 +52,7 @@
         // Any extra information is ignored for clean properties.
     }
 
-    public void setExtra(String[] a) {
+    public void setExtra(Options a) {
         // Any extra information is ignored for clean properties.
     }
 
--- a/langtools/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,16 @@
 
 package com.sun.tools.sjavac;
 
+import java.io.PrintStream;
 import java.net.URI;
 import java.util.Arrays;
 import java.util.Random;
 import java.util.Set;
 import java.util.Map;
 
+import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.server.JavacServer;
 import com.sun.tools.sjavac.server.SysInfo;
-import java.io.PrintStream;
 
 /**
  * This transform compiles a set of packages containing Java sources.
@@ -54,13 +55,12 @@
     // We hope to improve this in the future.
     final static int limitOnConcurrency = 3;
 
-    String serverSettings;
+    Options args;
+
     public void setExtra(String e) {
-        serverSettings = e;
     }
 
-    String[] args;
-    public void setExtra(String[] a) {
+    public void setExtra(Options a) {
         args = a;
     }
 
@@ -82,14 +82,14 @@
         boolean concurrentCompiles = true;
 
         // Fetch the id.
-        String id = Util.extractStringOption("id", serverSettings);
+        String id = Util.extractStringOption("id", args.getServerConf());
         if (id == null || id.equals("")) {
             // No explicit id set. Create a random id so that the requests can be
             // grouped properly in the server.
             id = "id"+(((new Random()).nextLong())&Long.MAX_VALUE);
         }
         // Only keep portfile and sjavac settings..
-        String psServerSettings = Util.cleanSubOptions("--server:", Util.set("portfile","sjavac","background","keepalive"), serverSettings);
+        String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), args.getServerConf());
 
         // Get maximum heap size from the server!
         SysInfo sysinfo = JavacServer.connectGetSysInfo(psServerSettings, out, err);
@@ -223,7 +223,7 @@
                 @Override
                 public void run() {
                                         rn[ii] = JavacServer.useServer(cleanedServerSettings,
-                                                           Main.removeWrapperArgs(args),
+                                                           args.prepJavacArgs(),
                                                                cc.srcs,
                                                            fvisible_sources,
                                                            fvisible_classes,
--- a/langtools/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,8 @@
 import java.util.HashSet;
 import java.util.Map;
 
+import com.sun.tools.sjavac.options.Options;
+
 /**
  * Compile properties transform a properties file into a Java source file.
  * Java has built in support for reading properties from either a text file
@@ -58,7 +60,7 @@
         extra = e;
     }
 
-    public void setExtra(String[] a) {
+    public void setExtra(Options a) {
     }
 
     public boolean transform(Map<String,Set<URI>> pkgSrcs,
--- a/langtools/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
 import java.util.HashSet;
 import java.util.Map;
 
+import com.sun.tools.sjavac.options.Options;
+
 /**
  * The copy file transform simply copies a matching file from -src to -d .
  * Such files are typically images, xml documents and other data files.
@@ -45,7 +47,7 @@
     public void setExtra(String e) {
     }
 
-    public void setExtra(String[] a) {
+    public void setExtra(Options a) {
     }
 
     public boolean transform(Map<String,Set<URI>> pkgSrcs,
--- a/langtools/src/share/classes/com/sun/tools/sjavac/JavacState.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/JavacState.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package com.sun.tools.sjavac;
 
 import java.io.*;
+import java.nio.file.Path;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Set;
@@ -37,6 +38,9 @@
 import java.net.URI;
 import java.util.*;
 
+import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.options.SourceLocation;
+
 /**
  * The javac state class maintains the previous (prev) and the current (now)
  * build states and everything else that goes into the javac_state file.
@@ -117,25 +121,20 @@
     // It can also map from a jar file to the set of visible classes for that jar file.
     Map<URI,Set<String>> visibleClasses;
 
-    // Setup two transforms that always exist.
-    private CopyFile            copyFiles = new CopyFile();
+    // Setup transform that always exist.
     private CompileJavaPackages compileJavaPackages = new CompileJavaPackages();
 
     // Where to send stdout and stderr.
     private PrintStream out, err;
 
-    JavacState(String[] args, File bd, File gd, File hd, boolean permitUnidentifiedArtifacts, boolean removeJavacState,
-            PrintStream o, PrintStream e) {
+    JavacState(Options options, boolean removeJavacState, PrintStream o, PrintStream e) {
         out = o;
         err = e;
-        numCores = Main.findNumberOption(args, "-j");
-        theArgs = "";
-        for (String a : removeArgsNotAffectingState(args)) {
-            theArgs = theArgs+a+" ";
-        }
-        binDir = bd;
-        gensrcDir = gd;
-        headerDir = hd;
+        numCores = options.getNumCores();
+        theArgs = options.getStateArgsString();
+        binDir = Util.pathToFile(options.getDestDir());
+        gensrcDir = Util.pathToFile(options.getGenSrcDir());
+        headerDir = Util.pathToFile(options.getHeaderDir());
         javacStateFilename = binDir.getPath()+File.separator+"javac_state";
         javacState = new File(javacStateFilename);
         if (removeJavacState && javacState.exists()) {
@@ -148,7 +147,7 @@
             // We do not want to risk building a broken incremental build.
             // BUT since the makefiles still copy things straight into the bin_dir et al,
             // we avoid deleting files here, if the option --permit-unidentified-classes was supplied.
-            if (!permitUnidentifiedArtifacts) {
+            if (!options.isUnidentifiedArtifactPermitted()) {
                 deleteContents(binDir);
                 deleteContents(gensrcDir);
                 deleteContents(headerDir);
@@ -301,9 +300,8 @@
     /**
      * Load a javac_state file.
      */
-    public static JavacState load(String[] args, File binDir, File gensrcDir, File headerDir,
-            boolean permitUnidentifiedArtifacts, PrintStream out, PrintStream err) {
-        JavacState db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, false, out, err);
+    public static JavacState load(Options options, PrintStream out, PrintStream err) {
+        JavacState db = new JavacState(options, false, out, err);
         Module  lastModule = null;
         Package lastPackage = null;
         Source  lastSource = null;
@@ -370,22 +368,22 @@
             noFileFound = true;
         } catch (IOException e) {
             Log.info("Dropping old javac_state because of errors when reading it.");
-            db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
+            db = new JavacState(options, true, out, err);
             foundCorrectVerNr = true;
             newCommandLine = false;
             syntaxError = false;
     }
         if (foundCorrectVerNr == false && !noFileFound) {
             Log.info("Dropping old javac_state since it is of an old version.");
-            db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
+            db = new JavacState(options, true, out, err);
         } else
         if (newCommandLine == true && !noFileFound) {
             Log.info("Dropping old javac_state since a new command line is used!");
-            db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
+            db = new JavacState(options, true, out, err);
         } else
         if (syntaxError == true) {
             Log.info("Dropping old javac_state since it contains syntax errors.");
-            db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
+            db = new JavacState(options, true, out, err);
         }
         db.prev.calculateDependents();
         return db;
@@ -467,12 +465,6 @@
         return sr;
     }
 
-    /**
-     * Acquire the copying transform.
-     */
-    public Transformer getCopier() {
-        return copyFiles;
-    }
 
     /**
      * If artifacts have gone missing, force a recompile of the packages
@@ -629,7 +621,7 @@
     public void performCopying(File binDir, Map<String,Transformer> suffixRules) {
         Map<String,Transformer> sr = new HashMap<>();
         for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
-            if (e.getValue() == copyFiles) {
+            if (e.getValue().getClass().equals(CopyFile.class)) {
                 sr.put(e.getKey(), e.getValue());
             }
         }
@@ -643,10 +635,11 @@
     public void performTranslation(File gensrcDir, Map<String,Transformer> suffixRules) {
         Map<String,Transformer> sr = new HashMap<>();
         for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
-            if (e.getValue() != copyFiles &&
-                e.getValue() != compileJavaPackages) {
-                sr.put(e.getKey(), e.getValue());
-            }
+            Class<?> trClass = e.getValue().getClass();
+            if (trClass == CompileJavaPackages.class || trClass == CopyFile.class)
+                continue;
+
+            sr.put(e.getKey(), e.getValue());
         }
         perform(gensrcDir, sr);
     }
@@ -654,14 +647,11 @@
     /**
      * Compile all the java sources. Return true, if it needs to be called again!
      */
-    public boolean performJavaCompilations(File binDir,
-                                           String serverSettings,
-                                           String[] args,
+    public boolean performJavaCompilations(Options args,
                                            Set<String> recentlyCompiled,
                                            boolean[] rcValue) {
         Map<String,Transformer> suffixRules = new HashMap<>();
         suffixRules.put(".java", compileJavaPackages);
-        compileJavaPackages.setExtra(serverSettings);
         compileJavaPackages.setExtra(args);
 
         rcValue[0] = perform(binDir, suffixRules);
@@ -813,7 +803,10 @@
         for (Source s : now.sources().values()) {
             // Don't include link only sources when comparing sources to compile
             if (!s.isLinkedOnly()) {
-                calculatedSources.add(s.file().getPath());
+                String path = s.file().getPath();
+                if (mightNeedRewriting)
+                    path = Util.normalizeDriveLetter(path);
+                calculatedSources.add(path);
             }
         }
         // Read in the file and create another set of filenames with full paths.
--- a/langtools/src/share/classes/com/sun/tools/sjavac/Log.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/Log.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -71,17 +71,19 @@
         err.println(msg);
     }
 
-    static public void setLogLevel(String l, PrintStream o, PrintStream e)
-        throws ProblemException {
+    static public void initializeLog(PrintStream o, PrintStream e) {
         out = o;
         err = e;
+    }
+
+    static public void setLogLevel(String l) {
         switch (l) {
             case "warn": level = WARN; break;
             case "info": level = INFO; break;
             case "debug": level = DEBUG; break;
             case "trace": level = TRACE; break;
             default:
-                throw new ProblemException("No such log level \"" + l + "\"");
+                throw new IllegalArgumentException("No such log level \"" + l + "\"");
         }
     }
 
--- a/langtools/src/share/classes/com/sun/tools/sjavac/Main.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/Main.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,14 @@
 
 package com.sun.tools.sjavac;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.nio.file.Path;
+import java.nio.file.Files;
 
+import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.options.SourceLocation;
 import com.sun.tools.sjavac.server.JavacServer;
 
 /**
@@ -151,24 +152,8 @@
         The resulting classes are written into bin.
     */
 
-    // This is the final destination for classes and copied files.
-    private File bin_dir;
-    // This is where the annotation process will put generated sources.
-    private File gensrc_dir;
-    // This is where javac -h puts the generated c-header files.
-    private File header_dir;
-
-    // This file contains the list of sources genereated by the makefile.
-    // We double check that our calculated list of sources matches this list,
-    // if not, then we terminate with an error!
-    private File makefile_source_list;
-    // The challenging task to manage an incremental build is done by javac_state.
     private JavacState javac_state;
 
-    // The suffix rules tells you for example, that .java files should be compiled,
-    // and .html files should be copied and .properties files be translated.
-    Map<String,Transformer> suffix_rules;
-
     public static void main(String... args)  {
         if (args.length > 0 && args[0].startsWith("--startserver:")) {
             if (args.length>1) {
@@ -199,118 +184,142 @@
     }
 
     public int go(String[] args, PrintStream out, PrintStream err) {
+
+        Log.initializeLog(out, err);
+
+        Options options;
         try {
-            if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
-                printHelp();
-                return 0;
-            }
+            options = Options.parseArgs(args);
+        } catch (IllegalArgumentException e) {
+            Log.error(e.getMessage());
+            return -1;
+        }
 
-            Log.setLogLevel(findLogLevel(args), out, err);
-            String server_settings = Util.findServerSettings(args);
-            args = verifyImplicitOption(args);
-            // Find the source root directories, and add the -src option before these, if not there already.
-            args = addSrcBeforeDirectories(args);
-            // Check that there is at least one -src supplied.
-            checkSrcOption(args);
-            // Check that there is one -d supplied.
-            bin_dir = findDirectoryOption(args,"-d","output", true, false, true);
-            gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);
-            header_dir = findDirectoryOption(args,"-h","headers", false, false, true);
-            makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);
+        Log.setLogLevel(options.getLogLevel());
 
-            // Load the prev build state database.
-            javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
-                    findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
+        if (!validateOptions(options))
+            return -1;
 
-            // Setup the suffix rules from the command line.
-            suffix_rules = javac_state.getJavaSuffixRule();
-            findTranslateOptions(args, suffix_rules);
-            if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {
-                Log.error("You have translators but no gensrc dir (-s) specified!");
-                return -1;
-            }
-            findCopyOptions(args, suffix_rules);
+        if (!createIfMissing(options.getDestDir()))
+            return -1;
 
-            // All found modules are put here.
-            Map<String,Module> modules = new HashMap<>();
-            // We start out in the legacy empty no-name module.
-            // As soon as we stumble on a module-info.java file we change to that module.
-            Module current_module = new Module("", "");
-            modules.put("", current_module);
+        Path gensrc = options.getGenSrcDir();
+        if (gensrc != null && !createIfMissing(gensrc))
+            return -1;
 
-            // Find all sources, use the suffix rules to know which files are sources.
-            Map<String,Source> sources = new HashMap<>();
-            // Find the files, this will automatically populate the found modules
-            // with found packages where the sources are found!
-            findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);
+        Path hdrdir = options.getHeaderDir();
+        if (hdrdir != null && !createIfMissing(hdrdir))
+            return -1;
 
-            if (sources.isEmpty()) {
-                Log.error("Found nothing to compile!");
-                return -1;
-            }
+        // Load the prev build state database.
+        javac_state = JavacState.load(options, out, err);
 
-            // Create a map of all source files that are available for linking. Both -src and
-            // -sourcepath point to such files. It is possible to specify multiple
-            // -sourcepath options to enable different filtering rules. If the
-            // filters are the same for multiple sourcepaths, they may be concatenated
-            // using :(;). Before sending the list of sourcepaths to javac, they are
-            // all concatenated. The list created here is used by the SmartFileWrapper to
-            // make sure only the correct sources are actually available.
-            // We might find more modules here as well.
-            Map<String,Source> sources_to_link_to = new HashMap<>();
-            findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true);
-            findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
-            // Rewrite the -src option to make it through to the javac instances.
-            rewriteOptions(args, "-src", "-sourcepath");
+        // Setup the suffix rules from the command line.
+        Map<String, Transformer> suffixRules = new HashMap<>();
 
-            // Find all class files allowable for linking.
-            // And pickup knowledge of all modules found here.
-            // This cannot currently filter classes inside jar files.
-//          Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
-//          findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
+        // Handling of .java-compilation
+        suffixRules.putAll(javac_state.getJavaSuffixRule());
 
-            // Find all module sources allowable for linking.
-//          Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
-//          findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
+        // Handling of -copy and -tr
+        suffixRules.putAll(options.getTranslationRules());
 
-            // Add the set of sources to the build database.
-            javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
-            javac_state.now().checkInternalState("checking sources", false, sources);
-            javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
-            javac_state.setVisibleSources(sources_to_link_to);
+        // All found modules are put here.
+        Map<String,Module> modules = new HashMap<>();
+        // We start out in the legacy empty no-name module.
+        // As soon as we stumble on a module-info.java file we change to that module.
+        Module current_module = new Module("", "");
+        modules.put("", current_module);
 
-            // If there is any change in the source files, taint packages
-            // and mark the database in need of saving.
-            javac_state.checkSourceStatus(false);
+        // Find all sources, use the suffix rules to know which files are sources.
+        Map<String,Source> sources = new HashMap<>();
 
-            // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
-            // in javac_state, simply because loading of the JavacState will clean out all artifacts
-            // that do not match the javac_state database.
-            javac_state.findAllArtifacts();
+        // Find the files, this will automatically populate the found modules
+        // with found packages where the sources are found!
+        findSourceFiles(options.getSources(),
+                        suffixRules.keySet(),
+                        sources,
+                        modules,
+                        current_module,
+                        options.isDefaultPackagePermitted(),
+                        false);
 
-            // Remove unidentified artifacts from the bin, gensrc and header dirs.
-            // (Unless we allow them to be there.)
-            // I.e. artifacts that are not known according to the build database (javac_state).
-            // For examples, files that have been manually copied into these dirs.
-            // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
-            // in javac_state) have already been removed when the javac_state was loaded.
-            if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
-                javac_state.removeUnidentifiedArtifacts();
-            }
-            // Go through all sources and taint all packages that miss artifacts.
-            javac_state.taintPackagesThatMissArtifacts();
+        if (sources.isEmpty()) {
+            Log.error("Found nothing to compile!");
+            return -1;
+        }
 
-            // Now clean out all known artifacts belonging to tainted packages.
-            javac_state.deleteClassArtifactsInTaintedPackages();
-            // Copy files, for example property files, images files, xml files etc etc.
-            javac_state.performCopying(bin_dir, suffix_rules);
-            // Translate files, for example compile properties or compile idls.
-            javac_state.performTranslation(gensrc_dir, suffix_rules);
-            // Add any potentially generated java sources to the tobe compiled list.
-            // (Generated sources must always have a package.)
-            Map<String,Source> generated_sources = new HashMap<>();
-            Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
-                   generated_sources, modules, current_module, false, true, false);
+        // Create a map of all source files that are available for linking. Both -src and
+        // -sourcepath point to such files. It is possible to specify multiple
+        // -sourcepath options to enable different filtering rules. If the
+        // filters are the same for multiple sourcepaths, they may be concatenated
+        // using :(;). Before sending the list of sourcepaths to javac, they are
+        // all concatenated. The list created here is used by the SmartFileWrapper to
+        // make sure only the correct sources are actually available.
+        // We might find more modules here as well.
+        Map<String,Source> sources_to_link_to = new HashMap<>();
+
+        List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
+        sourceResolutionLocations.addAll(options.getSources());
+        sourceResolutionLocations.addAll(options.getSourceSearchPaths());
+        findSourceFiles(sourceResolutionLocations,
+                        Collections.singleton(".java"),
+                        sources_to_link_to,
+                        modules,
+                        current_module,
+                        options.isDefaultPackagePermitted(),
+                        true);
+
+        // Find all class files allowable for linking.
+        // And pickup knowledge of all modules found here.
+        // This cannot currently filter classes inside jar files.
+//      Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
+//      findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
+
+        // Find all module sources allowable for linking.
+//      Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
+//      findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
+
+        // Add the set of sources to the build database.
+        javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
+        javac_state.now().checkInternalState("checking sources", false, sources);
+        javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
+        javac_state.setVisibleSources(sources_to_link_to);
+
+        // If there is any change in the source files, taint packages
+        // and mark the database in need of saving.
+        javac_state.checkSourceStatus(false);
+
+        // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
+        // in javac_state, simply because loading of the JavacState will clean out all artifacts
+        // that do not match the javac_state database.
+        javac_state.findAllArtifacts();
+
+        // Remove unidentified artifacts from the bin, gensrc and header dirs.
+        // (Unless we allow them to be there.)
+        // I.e. artifacts that are not known according to the build database (javac_state).
+        // For examples, files that have been manually copied into these dirs.
+        // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
+        // in javac_state) have already been removed when the javac_state was loaded.
+        if (!options.isUnidentifiedArtifactPermitted()) {
+            javac_state.removeUnidentifiedArtifacts();
+        }
+        // Go through all sources and taint all packages that miss artifacts.
+        javac_state.taintPackagesThatMissArtifacts();
+
+        // Now clean out all known artifacts belonging to tainted packages.
+        javac_state.deleteClassArtifactsInTaintedPackages();
+        // Copy files, for example property files, images files, xml files etc etc.
+        javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules);
+        // Translate files, for example compile properties or compile idls.
+        javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules);
+        // Add any potentially generated java sources to the tobe compiled list.
+        // (Generated sources must always have a package.)
+        Map<String,Source> generated_sources = new HashMap<>();
+
+        try {
+
+            Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), Util.set(".java"), null, null, null, null,
+                    generated_sources, modules, current_module, false, true, false);
             javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
             // Recheck the the source files and their timestamps again.
             javac_state.checkSourceStatus(true);
@@ -320,7 +329,7 @@
             // right, then incremental builds will fail with subtility.
             // If any difference is detected, then we will fail hard here.
             // This is an important safety net.
-            javac_state.compareWithMakefileList(makefile_source_list);
+            javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList()));
 
             // Do the compilations, repeatedly until no tainted packages exist.
             boolean again;
@@ -330,7 +339,7 @@
             do {
                 // Clean out artifacts in tainted packages.
                 javac_state.deleteClassArtifactsInTaintedPackages();
-                again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
+                again = javac_state.performJavaCompilations(options, recently_compiled, rc);
                 if (!rc[0]) break;
             } while (again);
             // Only update the state if the compile went well.
@@ -351,620 +360,71 @@
         }
     }
 
-    /**
-     * Are java source files passed on the command line?
-     */
-    private boolean findJavaSourceFiles(String[] args) {
-        String prev = "";
-        for (String s : args) {
-            if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
-                return true;
-            }
-            prev = s;
+    private static boolean validateOptions(Options options) {
+
+        String err = null;
+
+        if (options.getDestDir() == null) {
+            err = "Please specify output directory.";
+        } else if (options.isJavaFilesAmongJavacArgs()) {
+            err = "Sjavac does not handle explicit compilation of single .java files.";
+        } else if (options.isAtFilePresent()) {
+            err = "Sjavac does not handle @-files.";
+        } else if (options.getServerConf() == null) {
+            err = "No server configuration provided.";
+        } else if (!options.getImplicitPolicy().equals("none")) {
+            err = "The only allowed setting for sjavac is -implicit:none";
+        } else if (options.getSources().isEmpty()) {
+            err = "You have to specify -src.";
+        } else if (options.getTranslationRules().size() > 1
+                && options.getGenSrcDir() == null) {
+            err = "You have translators but no gensrc dir (-s) specified!";
         }
-        return false;
+
+        if (err != null)
+            Log.error(err);
+
+        return err == null;
+
     }
 
-    /**
-     * Is an at file passed on the command line?
-     */
-    private boolean findAtFile(String[] args) {
-        for (String s : args) {
-            if (s.startsWith("@")) {
-                return true;
-            }
+    private static boolean createIfMissing(Path dir) {
+
+        if (Files.isDirectory(dir))
+            return true;
+
+        if (Files.exists(dir)) {
+            Log.error(dir + " is not a directory.");
+            return false;
         }
-        return false;
-    }
 
-    /**
-     * Find the log level setting.
-     */
-    private String findLogLevel(String[] args) {
-        for (String s : args) {
-            if (s.startsWith("--log=") && s.length()>6) {
-                return s.substring(6);
-            }
-            if (s.equals("-verbose")) {
-                return "info";
-            }
+        try {
+            Files.createDirectories(dir);
+        } catch (IOException e) {
+            Log.error("Could not create directory: " + e.getMessage());
+            return false;
         }
-        return "info";
-    }
 
-    /**
-     * Remove smart javac wrapper arguments, before feeding
-     * the args to the plain javac.
-     */
-    static String[] removeWrapperArgs(String[] args) {
-        String[] out = new String[args.length];
-        // The first source path index is remembered
-        // here. So that all following can be concatenated to it.
-        int source_path = -1;
-        // The same for class path.
-        int class_path = -1;
-        // And module path.
-        int module_path = -1;
-        int j = 0;
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals("-src") ||
-                args[i].equals("-x") ||
-                args[i].equals("-i") ||
-                args[i].equals("-xf") ||
-                args[i].equals("-if") ||
-                args[i].equals("-copy") ||
-                args[i].equals("-tr") ||
-                args[i].equals("-j")) {
-                // Just skip it and skip following value
-                i++;
-            } else if (args[i].startsWith("--server:")) {
-                // Just skip it.
-            } else if (args[i].startsWith("--log=")) {
-                // Just skip it.
-            } else if (args[i].equals("--permit-unidentified-artifacts")) {
-                // Just skip it.
-            } else if (args[i].equals("--permit-sources-without-package")) {
-                // Just skip it.
-            } else if (args[i].equals("--compare-found-sources")) {
-                // Just skip it and skip verify file name
-                i++;
-            } else if (args[i].equals("-sourcepath")) {
-                if (source_path == -1) {
-                    source_path = j;
-                    out[j] = args[i];
-                    out[j+1] = args[i+1];
-                    j+=2;
-                    i++;
-                } else {
-                    // Skip this and its argument, but
-                    // append argument to found sourcepath.
-                    out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];
-                    i++;
-                }
-            } else if (args[i].equals("-classpath") || args[i].equals("-cp")) {
-                if (class_path == -1) {
-                    class_path = j;
-                    out[j] = args[i];
-                    out[j+1] = args[i+1];
-                    j+=2;
-                    i++;
-                } else {
-                    // Skip this and its argument, but
-                    // append argument to found sourcepath.
-                    out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];
-                    i++;
-                }
-            } else if (args[i].equals("-modulepath")) {
-                if (module_path == -1) {
-                    module_path = j;
-                    out[j] = args[i];
-                    out[j+1] = args[i+1];
-                    j+=2;
-                    i++;
-                } else {
-                    // Skip this and its argument, but
-                    // append argument to found sourcepath.
-                    out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];
-                    i++;
-                }
-             } else {
-                // Copy argument.
-                out[j] = args[i];
-                j++;
-            }
-        }
-        String[] ret = new String[j];
-        System.arraycopy(out, 0, ret, 0, j);
-        return ret;
-    }
-
-    /**
-     * Make sure directory exist, create it if not.
-     */
-    private static boolean makeSureExists(File dir) {
-        // Make sure the dest directories exist.
-        if (!dir.exists()) {
-            if (!dir.mkdirs()) {
-                Log.error("Could not create the directory "+dir.getPath());
-                return false;
-            }
-        }
         return true;
     }
 
-    /**
-     * Verify that a package pattern is valid.
-     */
-    private static void checkPattern(String s) throws ProblemException {
-        // Package names like foo.bar.gamma are allowed, and
-        // package names suffixed with .* like foo.bar.* are
-        // also allowed.
-        Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
-        Matcher m = p.matcher(s);
-        if (!m.matches()) {
-            throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
+
+    /** Find source files in the given source locations. */
+    public static void findSourceFiles(List<SourceLocation> sourceLocations,
+                                       Set<String> sourceTypes,
+                                       Map<String,Source> foundFiles,
+                                       Map<String, Module> foundModules,
+                                       Module currentModule,
+                                       boolean permitSourcesInDefaultPackage,
+                                       boolean inLinksrc) {
+
+        for (SourceLocation source : sourceLocations) {
+            source.findSourceFiles(sourceTypes,
+                                   foundFiles,
+                                   foundModules,
+                                   currentModule,
+                                   permitSourcesInDefaultPackage,
+                                   inLinksrc);
         }
     }
-
-    /**
-     * Verify that a translate pattern is valid.
-     */
-    private static void checkTranslatePattern(String s) throws ProblemException {
-        // .prop=com.sun.tools.javac.smart.CompileProperties
-        // .idl=com.sun.corba.CompileIdl
-        // .g3=antlr.CompileGrammar,debug=true
-        Pattern p = Pattern.compile(
-            "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+
-            "(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");
-        Matcher m = p.matcher(s);
-        if (!m.matches()) {
-            throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");
-        }
-    }
-
-    /**
-     * Verify that a copy pattern is valid.
-     */
-    private static void checkCopyPattern(String s) throws ProblemException {
-        // .gif
-        // .html
-        Pattern p = Pattern.compile(
-            "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");
-        Matcher m = p.matcher(s);
-        if (!m.matches()) {
-            throw new ProblemException("The string \""+s+"\" is not a proper suffix.");
-        }
-    }
-
-    /**
-     * Verify that a source file name is valid.
-     */
-    private static void checkFilePattern(String s) throws ProblemException {
-        // File names like foo/bar/gamma/Bar.java are allowed,
-        // as well as /bar/jndi.properties as well as,
-        // */bar/Foo.java
-        Pattern p = null;
-        if (File.separatorChar == '\\') {
-            p = Pattern.compile("\\*?(.+\\\\)*.+");
-        }
-        else if (File.separatorChar == '/') {
-            p = Pattern.compile("\\*?(.+/)*.+");
-        } else {
-            throw new ProblemException("This platform uses the unsupported "+File.separatorChar+
-                                      " as file separator character. Please add support for it!");
-        }
-        Matcher m = p.matcher(s);
-        if (!m.matches()) {
-            throw new ProblemException("The string \""+s+"\" is not a proper file name.");
-        }
-    }
-
-    /**
-     * Scan the arguments to find an option is used.
-     */
-    private static boolean hasOption(String[] args, String option) {
-        for (String a : args) {
-            if (a.equals(option)) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Check if -implicit is supplied, if so check that it is none.
-     * If -implicit is not supplied, supply -implicit:none
-     * Only implicit:none is allowed because otherwise the multicore compilations
-     * and dependency tracking will be tangled up.
-     */
-    private static String[] verifyImplicitOption(String[] args)
-        throws ProblemException {
-
-        boolean foundImplicit = false;
-        for (String a : args) {
-            if (a.startsWith("-implicit:")) {
-                foundImplicit = true;
-                if (!a.equals("-implicit:none")) {
-                    throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");
-                }
-            }
-        }
-        if (foundImplicit) {
-            return args;
-        }
-        // -implicit:none not found lets add it.
-        String[] newargs = new String[args.length+1];
-        System.arraycopy(args,0, newargs, 0, args.length);
-        newargs[args.length] = "-implicit:none";
-        return newargs;
-    }
-
-    /**
-     * Rewrite a single option into something else.
-     */
-    private static void rewriteOptions(String[] args, String option, String new_option) {
-        for (int i=0; i<args.length; ++i) {
-            if (args[i].equals(option)) {
-                args[i] = new_option;
-            }
-        }
-    }
-
-    /**
-     * Scan the arguments to find an option that specifies a directory.
-     * Create the directory if necessary.
-     */
-    private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)
-        throws ProblemException, ProblemException {
-        File dir = null;
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals(option)) {
-                if (dir != null) {
-                    throw new ProblemException("You have already specified the "+name+" dir!");
-                }
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a directory following "+option+".");
-                }
-                if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {
-                    throw new ProblemException("You must only specify a single directory for "+option+".");
-                }
-                dir = new File(args[i+1]);
-                if (!dir.exists()) {
-                    if (!create) {
-                         throw new ProblemException("This directory does not exist: "+dir.getPath());
-                    } else
-                    if (!makeSureExists(dir)) {
-                        throw new ProblemException("Cannot create directory "+dir.getPath());
-                    }
-                }
-                if (!dir.isDirectory()) {
-                    throw new ProblemException("\""+args[i+1]+"\" is not a directory.");
-                }
-            }
-        }
-        if (dir == null && needed) {
-            throw new ProblemException("You have to specify "+option);
-        }
-        try {
-            if (dir != null)
-                return dir.getCanonicalFile();
-        } catch (IOException e) {
-            throw new ProblemException(""+e);
-        }
-        return null;
-    }
-
-    /**
-     * Option is followed by path.
-     */
-    private static boolean shouldBeFollowedByPath(String o) {
-        return o.equals("-s") ||
-               o.equals("-h") ||
-               o.equals("-d") ||
-               o.equals("-sourcepath") ||
-               o.equals("-classpath") ||
-               o.equals("-cp") ||
-               o.equals("-bootclasspath") ||
-               o.equals("-src");
-    }
-
-    /**
-     * Add -src before source root directories if not already there.
-     */
-    private static String[] addSrcBeforeDirectories(String[] args) {
-        List<String> newargs = new ArrayList<>();
-        for (int i = 0; i<args.length; ++i) {
-            File dir = new File(args[i]);
-            if (dir.exists() && dir.isDirectory()) {
-                if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {
-                    newargs.add("-src");
-                }
-            }
-            newargs.add(args[i]);
-        }
-        return newargs.toArray(new String[0]);
-    }
-
-    /**
-     * Check the -src options.
-     */
-    private static void checkSrcOption(String[] args)
-        throws ProblemException {
-        Set<File> dirs = new HashSet<>();
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals("-src")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a directory following -src.");
-                }
-                StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);
-                while (st.hasMoreElements()) {
-                    File dir = new File(st.nextToken());
-                    if (!dir.exists()) {
-                        throw new ProblemException("This directory does not exist: "+dir.getPath());
-                    }
-                    if (!dir.isDirectory()) {
-                        throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");
-                    }
-                    if (dirs.contains(dir)) {
-                        throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");
-                    }
-                    dirs.add(dir);
-                }
-            }
-        }
-        if (dirs.isEmpty()) {
-            throw new ProblemException("You have to specify -src.");
-        }
-    }
-
-    /**
-     * Scan the arguments to find an option that specifies a file.
-     */
-    private static File findFileOption(String[] args, String option, String name, boolean needed)
-        throws ProblemException, ProblemException {
-        File file = null;
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals(option)) {
-                if (file != null) {
-                    throw new ProblemException("You have already specified the "+name+" file!");
-                }
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a file following "+option+".");
-                }
-                file = new File(args[i+1]);
-                if (file.isDirectory()) {
-                    throw new ProblemException("\""+args[i+1]+"\" is not a file.");
-                }
-                if (!file.exists() && needed) {
-                    throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");
-                }
-
-            }
-        }
-        if (file == null && needed) {
-            throw new ProblemException("You have to specify "+option);
-        }
-        return file;
-    }
-
-    /**
-     * Look for a specific switch, return true if found.
-     */
-    public static boolean findBooleanOption(String[] args, String option) {
-        for (String arg : args) {
-            if (arg.equals(option))
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Scan the arguments to find an option that specifies a number.
-     */
-    public static int findNumberOption(String[] args, String option) {
-        int rc = 0;
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals(option)) {
-                if (args.length > i+1) {
-                    rc = Integer.parseInt(args[i+1]);
-                }
-            }
-        }
-        return rc;
-    }
-
-    /**
-     * Scan the arguments to find the option (-tr) that setup translation rules to java source
-     * from different sources. For example: .properties are translated using CompileProperties
-     * The found translators are stored as suffix rules.
-     */
-    private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
-        throws ProblemException, ProblemException {
-
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals("-tr")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a translate rule following -tr.");
-                }
-                String s = args[i+1];
-                checkTranslatePattern(s);
-                int ep = s.indexOf("=");
-                String suffix = s.substring(0,ep);
-                String classname = s.substring(ep+1);
-                if (suffix_rules.get(suffix) != null) {
-                    throw new ProblemException("You have already specified a "+
-                                              "rule for the suffix "+suffix);
-                }
-                if (s.equals(".class")) {
-                    throw new ProblemException("You cannot have a translator for .class files!");
-                }
-                if (s.equals(".java")) {
-                    throw new ProblemException("You cannot have a translator for .java files!");
-                }
-                String extra = null;
-                int exp = classname.indexOf(",");
-                if (exp != -1) {
-                    extra = classname.substring(exp+1);
-                    classname = classname.substring(0,exp);
-                }
-                try {
-                    Class<?> cl = Class.forName(classname);
-                    Transformer t = (Transformer)cl.newInstance();
-                    t.setExtra(extra);
-                    suffix_rules.put(suffix, t);
-                }
-                catch (Exception e) {
-                    throw new ProblemException("Cannot use "+classname+" as a translator!");
-                }
-            }
-        }
-    }
-
-    /**
-     * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
-     * For example: -copy .html
-     * The found copiers are stored as suffix rules as well. No translation is done, just copying.
-     */
-    private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
-        throws ProblemException, ProblemException {
-
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals("-copy")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a translate rule following -tr.");
-                }
-                String s = args[i+1];
-                checkCopyPattern(s);
-                if (suffix_rules.get(s) != null) {
-                    throw new ProblemException("You have already specified a "+
-                                              "rule for the suffix "+s);
-                }
-                if (s.equals(".class")) {
-                    throw new ProblemException("You cannot have a copy rule for .class files!");
-                }
-                if (s.equals(".java")) {
-                    throw new ProblemException("You cannot have a copy rule for .java files!");
-                }
-                suffix_rules.put(s, javac_state.getCopier());
-            }
-        }
-    }
-
-    /**
-     * Rewrite a / separated path into \ separated, but only
-     * if we are running on a platform were File.separatorChar=='\', ie winapi.
-     */
-    private String fixupSeparator(String p) {
-        if (File.separatorChar == '/') return p;
-        return p.replaceAll("/", "\\\\");
-    }
-
-    /**
-     * Scan the arguments for -i -x -xf -if followed by the option
-     * -src, -sourcepath, -modulepath or -classpath and produce a map of all the
-     * files to referenced for that particular option.
-     *
-     * Store the found sources and the found modules in the supplied maps.
-     */
-    private boolean findFiles(String[] args, String option, Set<String> suffixes,
-                              Map<String,Source> found_files, Map<String, Module> found_modules,
-                              Module current_module, boolean inLinksrc)
-        throws ProblemException, ProblemException
-    {
-        // Track which source roots, source path roots and class path roots have been added.
-        Set<File> roots = new HashSet<>();
-        // Track the current set of package includes,excludes as well as excluded source files,
-        // to be used in the next -src/-sourcepath/-classpath
-        List<String> includes = new LinkedList<>();
-        List<String> excludes = new LinkedList<>();
-        List<String> excludefiles = new LinkedList<>();
-        List<String> includefiles = new LinkedList<>();
-        // This include is used to find all modules in the source.
-        List<String> moduleinfo = new LinkedList<>();
-        moduleinfo.add("module-info.java");
-
-        for (int i = 0; i<args.length; ++i) {
-            if (args[i].equals("-i")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a package pattern following -i");
-                }
-                String incl = args[i+1];
-                checkPattern(incl);
-                includes.add(incl);
-            }
-            if (args[i].equals("-x")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a package pattern following -x");
-                }
-                String excl = args[i+1];
-                checkPattern(excl);
-                excludes.add(excl);
-            }
-            if (args[i].equals("-xf")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a file following -xf");
-                }
-                String exclf = args[i+1];
-                checkFilePattern(exclf);
-                exclf = Util.normalizeDriveLetter(exclf);
-                excludefiles.add(fixupSeparator(exclf));
-            }
-            if (args[i].equals("-if")) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a file following -xf");
-                }
-                String inclf = args[i+1];
-                checkFilePattern(inclf);
-                inclf = Util.normalizeDriveLetter(inclf);
-                includefiles.add(fixupSeparator(inclf));
-            }
-            if (args[i].equals(option)) {
-                if (i+1 >= args.length) {
-                    throw new ProblemException("You have to specify a directory following "+option);
-                }
-                String[] root_dirs = args[i+1].split(File.pathSeparator);
-                for (String r : root_dirs) {
-                    File root = new File(r);
-                    if (!root.isDirectory()) {
-                        throw new ProblemException("\""+r+"\" is not a directory.");
-                    }
-                    try {
-                        root = root.getCanonicalFile();
-                    } catch (IOException e) {
-                        throw new ProblemException(""+e);
-                    }
-                    if (roots.contains(root)) {
-                        throw new ProblemException("\""+r+"\" has already been used for "+option);
-                    }
-                    if (root.equals(bin_dir)) {
-                        throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
-                    }
-                    if (root.equals(gensrc_dir)) {
-                        throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
-                    }
-                    if (root.equals(header_dir)) {
-                        throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
-                    }
-                    roots.add(root);
-                    Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
-                                    found_files, found_modules, current_module,
-                                    findBooleanOption(args, "--permit-sources-without-package"),
-                                    false, inLinksrc);
-                }
-            }
-            if (args[i].equals("-src") ||
-                args[i].equals("-sourcepath") ||
-                args[i].equals("-modulepath") ||
-                args[i].equals("-classpath") ||
-                args[i].equals("-cp"))
-            {
-                // Reset the includes,excludes and excludefiles after they have been used.
-                includes = new LinkedList<>();
-                excludes = new LinkedList<>();
-                excludefiles = new LinkedList<>();
-                includefiles = new LinkedList<>();
-            }
-        }
-        return true;
-    }
-
 }
-
--- a/langtools/src/share/classes/com/sun/tools/sjavac/Source.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/Source.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -201,7 +201,7 @@
         // It might contain other source files however, (for -tr and -copy) these will
         // always be included, since no package pattern can match the root directory.
         currentModule = addFilesInDir(root, root_prefix, root, suffixes, permitSourcesWithoutPackage,
-                                       excludeFiles, includeFiles, false,
+                                       excludeFiles, includeFiles,
                                        foundFiles, foundModules, currentModule,
                                        inGensrc, inLinksrc);
 
@@ -211,24 +211,28 @@
                 // Descend into the directory structure.
                 scanDirectory(d, root_prefix, root, suffixes,
                               excludes, includes, excludeFiles, includeFiles,
-                              false, foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
+                              foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
             }
         }
     }
 
     /**
      * Test if a path matches any of the patterns given.
-     * The pattern foo.bar matches only foo.bar
-     * The pattern foo.* matches foo.bar and foo.bar.zoo etc
+     * The pattern foo/bar matches only foo/bar
+     * The pattern foo/* matches foo/bar and foo/bar/zoo etc
      */
     static private boolean hasMatch(String path, List<String> patterns) {
+
+        // Convert Windows '\' to '/' for the sake of comparing with the patterns
+        path = path.replace(File.separatorChar, '/');
+
         for (String p : patterns) {
             // Exact match
-            if (p.equals(path)) {
+            if (p.equals(path))
                 return true;
-            }
+
             // Single dot the end matches this package and all its subpackages.
-            if (p.endsWith(".*")) {
+            if (p.endsWith("/*")) {
                 // Remove the wildcard
                 String patprefix = p.substring(0,p.length()-2);
                 // Does the path start with the pattern prefix?
@@ -237,7 +241,7 @@
                     // If the path is longer, then make sure that
                     // the next part of the path starts with a dot (.) to prevent
                     // wildcard matching in the middle of a package name.
-                    if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='.') {
+                    if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='/') {
                         return true;
                     }
                 }
@@ -251,6 +255,9 @@
      // The pattern foo/bar.java only matches foo/bar.java
      // The pattern */bar.java matches foo/bar.java and zoo/bar.java etc
     static private boolean hasFileMatch(String path, List<String> patterns) {
+        // Convert Windows '\' to '/' for the sake of comparing with the patterns
+        path = path.replace(File.separatorChar, '/');
+
         path = Util.normalizeDriveLetter(path);
         for (String p : patterns) {
             // Exact match
@@ -276,7 +283,7 @@
      */
     static private Module addFilesInDir(File dir, int rootPrefix, File root,
                                         Set<String> suffixes, boolean allow_javas,
-                                        List<String> excludeFiles, List<String> includeFiles, boolean all,
+                                        List<String> excludeFiles, List<String> includeFiles,
                                         Map<String,Source> foundFiles,
                                         Map<String,Module> foundModules,
                                         Module currentModule,
@@ -285,79 +292,82 @@
         throws ProblemException
     {
         for (File f : dir.listFiles()) {
-            if (f.isFile()) {
-                boolean should_add =
-                    (excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles))
-                    && (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles));
 
-                if (should_add) {
-                    if (!allow_javas && f.getName().endsWith(".java")) {
-                        throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+
-                                                   ", please remove "+f.getName());
-                    }
-                    // Extract the file name relative the root.
-                    String fn = f.getPath().substring(rootPrefix);
-                    // Extract the package name.
-                    int sp = fn.lastIndexOf(File.separatorChar);
-                    String pkg = "";
-                    if (sp != -1) {
-                        pkg = fn.substring(0,sp).replace(File.separatorChar,'.');
-                    }
-                    // Is this a module-info.java file?
-                    if (fn.endsWith("module-info.java")) {
-                        // Aha! We have recursed into a module!
-                        if (!currentModule.name().equals("")) {
-                            throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn);
+            if (!f.isFile())
+                continue;
+
+            boolean should_add =
+                (excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles))
+                && (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles));
+
+            if (!should_add)
+                continue;
+
+            if (!allow_javas && f.getName().endsWith(".java")) {
+                throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+
+                                           ", please remove "+f.getName());
+            }
+            // Extract the file name relative the root.
+            String fn = f.getPath().substring(rootPrefix);
+            // Extract the package name.
+            int sp = fn.lastIndexOf(File.separatorChar);
+            String pkg = "";
+            if (sp != -1) {
+                pkg = fn.substring(0,sp);
+            }
+            // Is this a module-info.java file?
+            if (fn.endsWith("module-info.java")) {
+                // Aha! We have recursed into a module!
+                if (!currentModule.name().equals("")) {
+                    throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn);
+                }
+                String module_name = fn.substring(0,fn.length()-16);
+                currentModule = new Module(module_name, f.getPath());
+                foundModules.put(module_name, currentModule);
+            }
+            // Extract the suffix.
+            int dp = fn.lastIndexOf(".");
+            String suffix = "";
+            if (dp > 0) {
+                suffix = fn.substring(dp);
+            }
+            // Should the file be added?
+            if (suffixes.contains(suffix)) {
+                Source of = foundFiles.get(f.getPath());
+                if (of != null) {
+                    throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath());
+                }
+                of = currentModule.lookupSource(f.getPath());
+                if (of != null) {
+                    // Oups, the source is already added, could be ok, could be not, lets check.
+                    if (inLinksrc) {
+                        // So we are collecting sources for linking only.
+                        if (of.isLinkedOnly()) {
+                            // Ouch, this one is also for linking only. Bad.
+                            throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath());
                         }
-                        String module_name = fn.substring(0,fn.length()-16);
-                        currentModule = new Module(module_name, f.getPath());
-                        foundModules.put(module_name, currentModule);
-                    }
-                    // Extract the suffix.
-                    int dp = fn.lastIndexOf(".");
-                    String suffix = "";
-                    if (dp > 0) {
-                        suffix = fn.substring(dp);
-                    }
-                    // Should the file be added?
-                    if (all || suffixes.contains(suffix)) {
-                        Source of = foundFiles.get(f.getPath());
-                        if (of != null) {
-                            throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath());
-                        }
-                        of = currentModule.lookupSource(f.getPath());
-                        if (of != null) {
-                            // Oups, the source is already added, could be ok, could be not, lets check.
-                            if (inLinksrc) {
-                                // So we are collecting sources for linking only.
-                                if (of.isLinkedOnly()) {
-                                    // Ouch, this one is also for linking only. Bad.
-                                    throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath());
-                                }
-                                // Ok, the existing source is to be compiled. Thus this link only is redundant
-                                // since all compiled are also linked to. Continue to the next source.
-                                // But we need to add the source, so that it will be visible to linking,
-                                // if not the multi core compile will fail because a JavaCompiler cannot
-                                // find the necessary dependencies for its part of the source.
-                                foundFiles.put(f.getPath(), of);
-                                continue;
-                            } else {
-                                // We are looking for sources to compile, if we find an existing to be compiled
-                                // source with the same name, it is an internal error, since we must
-                                // find the sources to be compiled before we find the sources to be linked to.
-                                throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath());
-                            }
-                        }
-                        Source s = new Source(currentModule, f.getPath(), f, root);
-                        if (inGensrc) s.markAsGenerated();
-                        if (inLinksrc) {
-                            s.markAsLinkedOnly();
-                        }
-                        pkg = currentModule.name()+":"+pkg;
-                        foundFiles.put(f.getPath(), s);
-                        currentModule.addSource(pkg, s);
+                        // Ok, the existing source is to be compiled. Thus this link only is redundant
+                        // since all compiled are also linked to. Continue to the next source.
+                        // But we need to add the source, so that it will be visible to linking,
+                        // if not the multi core compile will fail because a JavaCompiler cannot
+                        // find the necessary dependencies for its part of the source.
+                        foundFiles.put(f.getPath(), of);
+                        continue;
+                    } else {
+                        // We are looking for sources to compile, if we find an existing to be compiled
+                        // source with the same name, it is an internal error, since we must
+                        // find the sources to be compiled before we find the sources to be linked to.
+                        throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath());
                     }
                 }
+                Source s = new Source(currentModule, f.getPath(), f, root);
+                if (inGensrc) s.markAsGenerated();
+                if (inLinksrc) {
+                    s.markAsLinkedOnly();
+                }
+                pkg = currentModule.name()+":"+pkg;
+                foundFiles.put(f.getPath(), s);
+                currentModule.addSource(pkg, s);
             }
         }
         return currentModule;
@@ -368,23 +378,22 @@
     static private void scanDirectory(File dir, int rootPrefix, File root,
                                       Set<String> suffixes,
                                       List<String> excludes, List<String> includes,
-                                      List<String> excludeFiles, List<String> includeFiles, boolean all,
+                                      List<String> excludeFiles, List<String> includeFiles,
                                       Map<String,Source> foundFiles,
                                       Map<String,Module> foundModules,
                                       Module currentModule, boolean inGensrc, boolean inLinksrc)
         throws ProblemException {
 
-        String pkg_name = "";
-        // Remove the root prefix from the dir path, and replace file separator with dots
-        // to get the package name.
+        String path = "";
+        // Remove the root prefix from the dir path
         if (dir.getPath().length() > rootPrefix) {
-            pkg_name = dir.getPath().substring(rootPrefix).replace(File.separatorChar,'.');
+            path = dir.getPath().substring(rootPrefix);
         }
         // Should this package directory be included and not excluded?
-        if (all || ((includes==null || includes.isEmpty() || hasMatch(pkg_name, includes)) &&
-                    (excludes==null || excludes.isEmpty() || !hasMatch(pkg_name, excludes)))) {
+        if ((includes==null || includes.isEmpty() || hasMatch(path, includes)) &&
+            (excludes==null || excludes.isEmpty() || !hasMatch(path, excludes))) {
             // Add the source files.
-            currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles, all,
+            currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles,
                                           foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
         }
 
@@ -392,7 +401,7 @@
             if (d.isDirectory()) {
                 // Descend into the directory structure.
                 scanDirectory(d, rootPrefix, root, suffixes,
-                              excludes, includes, excludeFiles, includeFiles, all,
+                              excludes, includes, excludeFiles, includeFiles,
                               foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
             }
         }
--- a/langtools/src/share/classes/com/sun/tools/sjavac/Transformer.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/Transformer.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,8 @@
 import java.util.Set;
 import java.util.Map;
 
+import com.sun.tools.sjavac.options.Options;
+
 /**
  * The transform interface is used to transform content inside a package, from one form to another.
  * Usually the output form is an unpredictable number of output files. (eg class files)
@@ -95,5 +97,5 @@
                       PrintStream err);
 
     void setExtra(String e);
-    void setExtra(String[] args);
+    void setExtra(Options args);
 }
--- a/langtools/src/share/classes/com/sun/tools/sjavac/Util.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/Util.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package com.sun.tools.sjavac;
 
 import java.io.File;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -94,14 +95,12 @@
      * do settings = cleanOptions("--server:",Util.set("-portfile"),settings);
      *    now settings equals "--server:portfile=bar"
      *
-     * @param optionPrefix The option name, including colon, eg --server:
      * @param allowsSubOptions A set of the allowed sub options, id portfile etc.
      * @param s The option settings string.
      */
-    public static String cleanSubOptions(String optionPrefix, Set<String> allowedSubOptions, String s) {
+    public static String cleanSubOptions(Set<String> allowedSubOptions, String s) {
         StringBuilder sb = new StringBuilder();
-        if (!s.startsWith(optionPrefix)) return "";
-        StringTokenizer st = new StringTokenizer(s.substring(optionPrefix.length()), ",");
+        StringTokenizer st = new StringTokenizer(s, ",");
         while (st.hasMoreTokens()) {
             String o = st.nextToken();
             int p = o.indexOf('=');
@@ -157,4 +156,9 @@
         }
         return null;
     }
+
+    // TODO: Remove when refactoring from java.io.File to java.nio.file.Path.
+    public static File pathToFile(Path path) {
+        return path == null ? null : path.toFile();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/options/ArgumentIterator.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.sjavac.options;
+
+import java.util.Iterator;
+
+public class ArgumentIterator implements Iterator<String> {
+
+    /** The underlying argument iterator */
+    private Iterator<String> iter;
+
+    /** Extra state used to implement peek and current */
+    private String current;
+    private String buffered;
+
+    public ArgumentIterator(Iterable<String> iter) {
+        this.iter = iter.iterator();
+    }
+
+    @Override
+    public boolean hasNext() {
+        return buffered != null || iter.hasNext();
+    }
+
+    @Override
+    public String next() {
+        fillBuffer();
+        current = buffered;
+        buffered = null;
+        return current;
+    }
+
+    /**
+     * @return the last element returned by next() (or {@code null} if next has
+     * never been invoked on this iterator).
+     */
+    public String current() {
+        return current;
+    }
+
+    /** Can't remove current element, since we may have buffered it. */
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @return Returns the next element without advancing the iterator
+     */
+    public String peek() {
+        fillBuffer();
+        return buffered;
+    }
+
+    private void fillBuffer() {
+        if (buffered == null && iter.hasNext())
+            buffered = iter.next();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/options/Option.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.sjavac.options;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.sun.tools.sjavac.CopyFile;
+import com.sun.tools.sjavac.Transformer;
+
+
+/**
+ * Sjavac options can be classified as:
+ *
+ *  (1) relevant only for sjavac, such as --server
+ *  (2) relevant for sjavac and javac, such as -d, or
+ *  (3) relevant only for javac, such as -g.
+ *
+ * This enum represents all options from (1) and (2). Note that instances of
+ * this enum only entail static information about the option. For storage of
+ * option values, refer to com.sun.tools.sjavac.options.Options.
+ */
+public enum Option {
+
+    SRC("-src", "Location of source files to be compiled") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            List<Path> paths = getFileListArg(iter, helper);
+            if (paths != null)
+                helper.sourceRoots(paths);
+        }
+    },
+    SOURCEPATH("-sourcepath", "Specify search path for sources.") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            List<Path> paths = getFileListArg(iter, helper);
+            if (paths != null)
+                helper.sourcepath(paths);
+        }
+    },
+    MODULEPATH("-modulepath", "Specify search path for modules.") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            List<Path> paths = getFileListArg(iter, helper);
+            if (paths != null)
+                helper.modulepath(paths);
+        }
+    },
+    CLASSPATH("-classpath", "Specify search path for classes.") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            List<Path> paths = getFileListArg(iter, helper);
+            if (paths != null)
+                helper.classpath(paths);
+        }
+    },
+    CP("-cp", "An alias for -classpath") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            CLASSPATH.processMatching(iter, helper);
+        }
+    },
+    X("-x", "Exclude directory from the subsequent source directory") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            String pattern = getFilePatternArg(iter, helper);
+            if (pattern != null)
+                helper.exclude(pattern);
+        }
+    },
+    I("-i", "Include only the given directory from the subsequent source directory") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            String pattern = getFilePatternArg(iter, helper);
+            if (pattern != null)
+                helper.include(pattern);
+        }
+    },
+    XF("-xf", "Exclude a given file") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            String pattern = getFilePatternArg(iter, helper);
+            if (pattern != null)
+                helper.excludeFile(pattern);
+        }
+    },
+    IF("-if", "Include only the given file") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            String pattern = getFilePatternArg(iter, helper);
+            if (pattern != null)
+                helper.includeFile(pattern);
+        }
+    },
+    TR("-tr", "Translate resources") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+
+            if (!iter.hasNext()) {
+                helper.reportError(arg + " must be followed by a translation rule");
+                return;
+            }
+
+            String trArg = iter.next();
+
+            // Validate argument syntax. Examples:
+            //   .prop=com.sun.tools.javac.smart.CompileProperties
+            //   .idl=com.sun.corba.CompileIdl
+            //   .g3=antlr.CompileGrammar,debug=true
+            String ident = "[a-zA-Z_][a-zA-Z0-9_]*";
+            Pattern p = Pattern.compile("(?<suffix>\\." + ident + ")=" +
+                                        "(?<class>" + ident + "(\\." + ident + ")*)" +
+                                        "(?<extra>,.*)?");
+            // Check syntax
+            Matcher m = p.matcher(trArg);
+            if (!m.matches()) {
+                helper.reportError("The string \"" + trArg + "\" is not a " +
+                                   "valid translate pattern");
+                return;
+            }
+
+            // Extract relevant parts
+            String suffix = m.group("suffix");
+            String classname = m.group("class");
+            String extra = m.group("extra");
+
+            // Valid suffix?
+            if (suffix.matches("\\.(class|java)")) {
+                helper.reportError("You cannot have a translator for " +
+                                   suffix + " files!");
+                return;
+            }
+
+            // Construct transformer
+            try {
+                Class<?> trCls = Class.forName(classname);
+                Transformer transformer = (Transformer) trCls.newInstance();
+                transformer.setExtra(extra);
+                helper.addTransformer(suffix, transformer);
+            } catch (Exception e) {
+                helper.reportError("Cannot use " + classname +
+                                   " as a translator: " + e.getMessage());
+            }
+        }
+    },
+    COPY("-copy", "Copy resources") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            if (!iter.hasNext()) {
+                helper.reportError(arg + " must be followed by a resource type");
+                return;
+            }
+
+            String copyArg = iter.next();
+
+            // Validate argument syntax. Examples: .gif, .html
+            if (!copyArg.matches("\\.[a-zA-Z_][a-zA-Z0-9_]*")) {
+                helper.reportError("The string \"" + copyArg + "\" is not a " +
+                                   "valid resource type.");
+                return;
+            }
+
+            helper.addTransformer(copyArg, new CopyFile());
+        }
+    },
+    J("-j", "Number of cores") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            if (!iter.hasNext() || !iter.peek().matches("\\d+")) {
+                helper.reportError(arg + " must be followed by an integer");
+                return;
+            }
+            helper.numCores(Integer.parseInt(iter.next()));
+        }
+    },
+    SERVER("--server:", "Specify server configuration file of running server") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.serverConf(iter.current().substring(arg.length()));
+        }
+    },
+    STARTSERVER("--startserver:", "Start server and use the given configuration file") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.startServerConf(iter.current().substring(arg.length()));
+        }
+    },
+    IMPLICIT("-implicit:", "Specify how to treat implicitly referenced source code") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.implicit(iter.current().substring(arg.length()));
+        }
+    },
+    LOG("--log=", "Specify logging level") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.logLevel(iter.current().substring(arg.length()));
+        }
+    },
+    VERBOSE("-verbose", "Set verbosity level to \"info\"") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.logLevel("info");
+        }
+    },
+    PERMIT_UNIDENTIFIED_ARTIFACTS("--permit-unidentified-artifacts", "Keep unidentified artifacts in destination directory") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.permitUnidentifiedArtifacts();
+        }
+    },
+    PERMIT_SOURCES_WITHOUT_PACKAGE("--permit-sources-without-package", "Permit sources in the default package") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            helper.permitDefaultPackage();
+        }
+    },
+    COMPARE_FOUND_SOURCES("--compare-found-sources", "Compare found sources with given sources") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            Path referenceSourceList = getFileArg(iter, helper, true, false);
+            if (referenceSourceList != null)
+                helper.compareFoundSources(referenceSourceList);
+        }
+    },
+    D("-d", "Output destination directory") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            Path dir = getFileArg(iter, helper, false, true);
+            if (dir != null)
+                helper.destDir(dir);
+        }
+    },
+    S("-s", "Directory for generated sources") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            Path dir = getFileArg(iter, helper, false, true);
+            if (dir != null)
+                helper.generatedSourcesDir(dir);
+        }
+    },
+    H("-h", "Directory for header files") {
+        @Override
+        protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
+            Path dir = getFileArg(iter, helper, false, true);
+            if (dir != null)
+                helper.headerDir(dir);
+        }
+    };
+
+    public final String arg;
+
+    final String description;
+
+    private Option(String arg, String description) {
+        this.arg = arg;
+        this.description = description;
+    }
+
+    /** Retrieve and verify syntax of file list argument. */
+    List<Path> getFileListArg(ArgumentIterator iter, OptionHelper helper) {
+        if (!iter.hasNext()) {
+            helper.reportError(arg + " must be followed by a list of files " +
+                              "separated by " + File.pathSeparator);
+            return null;
+        }
+        List<Path> result = new ArrayList<>();
+        for (String pathStr : iter.next().split(File.pathSeparator))
+            result.add(Paths.get(pathStr));
+        return result;
+    }
+
+    /** Retrieve and verify syntax of file argument. */
+    Path getFileArg(ArgumentIterator iter, OptionHelper helper, boolean fileAcceptable, boolean dirAcceptable) {
+
+        if (!iter.hasNext()) {
+            String errmsg = arg + " must be followed by ";
+            if (fileAcceptable && dirAcceptable) errmsg += "a file or directory.";
+            else if (fileAcceptable) errmsg += "a file.";
+            else if (dirAcceptable)  errmsg += "a directory.";
+            else throw new IllegalArgumentException("File or directory must be acceptable.");
+            helper.reportError(errmsg);
+            return null;
+        }
+
+        return Paths.get(iter.next());
+    }
+
+    /** Retrieve the next file or package argument. */
+    String getFilePatternArg(ArgumentIterator iter, OptionHelper helper) {
+
+        if (!iter.hasNext()) {
+            helper.reportError(arg + " must be followed by a file or directory pattern.");
+            return null;
+        }
+
+        return iter.next();
+    }
+
+    // Future cleanup: Change the "=" syntax to ":" syntax to be consistent and
+    // to follow the javac-option style.
+
+    public boolean hasOption() {
+        return arg.endsWith(":") || arg.endsWith("=");
+    }
+
+
+    /**
+     * Process current argument of argIter.
+     *
+     * It's final, since the option customization is typically done in
+     * processMatching.
+     *
+     * @param argIter Iterator to read current and succeeding arguments from.
+     * @param helper The helper to report back to.
+     * @return true iff the argument was processed by this option.
+     */
+    public final boolean processCurrent(ArgumentIterator argIter,
+                                        OptionHelper helper) {
+        String fullArg = argIter.current(); // "-tr" or "-log=level"
+        if (hasOption() ? fullArg.startsWith(arg) : fullArg.equals(arg)) {
+            processMatching(argIter, helper);
+            return true;
+        }
+        // Did not match
+        return false;
+    }
+
+    /** Called by process if the current argument matches this option. */
+    protected abstract void processMatching(ArgumentIterator argIter,
+                                            OptionHelper helper);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/options/OptionHelper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.sjavac.options;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+
+import com.sun.tools.sjavac.Transformer;
+
+/**
+ * This class is used to decode sjavac options.
+ * See com.sun.tools.sjavac.options.Options for example usage.
+ */
+public abstract class OptionHelper {
+
+    /** Handle error */
+    public abstract void reportError(String msg);
+
+    /** Record a package exclusion pattern */
+    public abstract void exclude(String excl);
+
+    /** Record a package inclusion pattern */
+    public abstract void include(String incl);
+
+    /** Record a file exclusion */
+    public abstract void excludeFile(String exclFile);
+
+    /** Record a file inclusion */
+    public abstract void includeFile(String inclFile);
+
+    /** Record a root of sources to be compiled */
+    public abstract void sourceRoots(List<Path> path);
+
+    /** Record a suffix + transformer */
+    public abstract void addTransformer(String suffix, Transformer tr);
+
+    /** Record a sourcepath to be used */
+    public abstract void sourcepath(List<Path> path);
+
+    /** Record a modulepath to be used */
+    public abstract void modulepath(List<Path> path);
+
+    /** Record a classpath to be used */
+    public abstract void classpath(List<Path> path);
+
+    /** Record the number of cores */
+    public abstract void numCores(int parseInt);
+
+    /** Record desired log level */
+    public abstract void logLevel(String level);
+
+    /** Record path for reference source list */
+    public abstract void compareFoundSources(Path referenceList);
+
+    /** Record the fact that unidentified artifacts are permitted */
+    public abstract void permitUnidentifiedArtifacts();
+
+    /** Record the fact that sources in the default package are permitted */
+    public abstract void permitDefaultPackage();
+
+    /** Record server configuration parameters */
+    public abstract void serverConf(String serverConf);
+
+    /** Record server launch configuration parameters */
+    public abstract void startServerConf(String serverConf);
+
+    /** Record some arguments to be passed on to javac */
+    public abstract void javacArg(String... arg);
+
+    /** Sets the destination directory for the compilation */
+    public abstract void destDir(Path dir);
+
+    /** Sets the directory for generated sources */
+    public abstract void generatedSourcesDir(Path genSrcDir);
+
+    /** Sets the directory for generated headers */
+    public abstract void headerDir(Path dir);
+
+    /** Sets the implicit policy */
+    public abstract void implicit(String policy);
+
+
+    /**
+     * Traverses an array of arguments and performs the appropriate callbacks.
+     *
+     * @param args the arguments to traverse.
+     */
+    void traverse(String[] args) {
+
+        ArgumentIterator argIter = new ArgumentIterator(Arrays.asList(args));
+
+        nextArg:
+        while (argIter.hasNext()) {
+
+            String arg = argIter.next();
+
+            if (arg.startsWith("-")) {
+                for (Option opt : Option.values()) {
+                    if (opt.processCurrent(argIter, this))
+                        continue nextArg;
+                }
+
+                javacArg(arg);
+
+                // Does this javac argument take an argument? If so, don't
+                // let it pass on to sjavac as a source root directory.
+                for (com.sun.tools.javac.main.Option javacOpt : com.sun.tools.javac.main.Option.values()) {
+                    if (javacOpt.matches(arg)) {
+                        boolean takesArgument = javacOpt.hasArg();
+                        boolean separateToken = !arg.contains(":") && !arg.contains("=");
+                        if (takesArgument && separateToken)
+                            javacArg(argIter.next());
+                    }
+                }
+            } else {
+                sourceRoots(Arrays.asList(Paths.get(arg)));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/options/Options.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.sjavac.options;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.tools.sjavac.Transformer;
+
+/**
+ * Instances of this class represent values for sjavac command line options.
+ */
+public class Options {
+
+    // Output directories
+    private Path destDir, genSrcDir, headerDir;
+
+    // Input directories
+    private List<SourceLocation> sources = new ArrayList<>();
+    private List<SourceLocation> sourceSearchPaths = new ArrayList<>();
+    private List<SourceLocation> classSearchPaths = new ArrayList<>();
+    private List<SourceLocation> moduleSearchPaths = new ArrayList<>();
+
+    private String logLevel = "info";
+
+    private boolean permitUnidentifiedArtifact = false;
+    private boolean permitSourcesInDefaultPackage = false;
+
+    private Path sourceReferenceList;
+    private int numCores = 4;
+    private String implicitPolicy = "none";
+    private List<String> javacArgs = new ArrayList<>();
+
+    private Map<String, Transformer> trRules = new HashMap<>();
+
+    private boolean startServer = false;
+
+    // Server configuration string
+    private String serverConf;
+
+    /** Get the policy for implicit classes */
+    public String getImplicitPolicy() {
+        return implicitPolicy;
+    }
+
+    /** Get the path for generated sources (or null if no such path is set) */
+    public Path getGenSrcDir() {
+        return genSrcDir;
+    }
+
+    /** Get the path for the destination directory */
+    public Path getDestDir() {
+        return destDir;
+    }
+
+    /** Get the path for the header directory (or null if no such path is set) */
+    public Path getHeaderDir() {
+        return headerDir;
+    }
+
+    /** Get all source locations for files to be compiled */
+    public List<SourceLocation> getSources() {
+        return sources;
+    }
+
+    /**
+     * Get all paths to search for classes in .java format. (Java-files in
+     * found here should not be compiled.
+     */
+    public List<SourceLocation> getSourceSearchPaths() {
+        return sourceSearchPaths;
+    }
+
+    /** Get all paths to search for classes in. */
+    public List<SourceLocation> getClassSearchPath() {
+        return classSearchPaths;
+    }
+
+    /** Get all paths to search for modules in. */
+    public List<SourceLocation> getModuleSearchPaths() {
+        return moduleSearchPaths;
+    }
+
+    /** Get the log level. */
+    public String getLogLevel() {
+        return logLevel;
+    }
+
+    /** Returns true iff artifacts in the output directories should be kept,
+     * even if they would not be generated in a clean build. */
+    public boolean isUnidentifiedArtifactPermitted() {
+        return permitUnidentifiedArtifact;
+    }
+
+    /** Returns true iff sources in the default package should be permitted. */
+    public boolean isDefaultPackagePermitted() {
+        return permitSourcesInDefaultPackage;
+    }
+
+    /** Get the path to the list of reference sources (or null if none is set) */
+    public Path getSourceReferenceList() {
+        return sourceReferenceList;
+    }
+
+    /** Get the number of cores to be used by sjavac */
+    public int getNumCores() {
+        return numCores;
+    }
+
+    /** Returns all arguments relevant to javac but irrelevant to sjavac. */
+    public List<String> getJavacArgs() {
+        return javacArgs;
+    }
+
+    /**
+     * Get a map which maps suffixes to transformers (for example
+     * ".java" -> CompileJavaPackages)
+     */
+    public Map<String, Transformer> getTranslationRules() {
+        return trRules;
+    }
+
+    /** Return true iff a new server should be started */
+    public boolean startServerFlag() {
+        return startServer;
+    }
+
+    /** Return the server configuration string. */
+    public String getServerConf() {
+        return serverConf;
+    }
+
+    /**
+     * Parses the given argument array and returns a corresponding Options
+     * instance.
+     */
+    public static Options parseArgs(String... args) {
+        Options options = new Options();
+        options.new ArgDecoderOptionHelper().traverse(args);
+        return options;
+    }
+
+    /** Returns true iff a .java file is among the javac arguments */
+    public boolean isJavaFilesAmongJavacArgs() {
+        for (String javacArg : javacArgs)
+            if (javacArg.endsWith(".java"))
+                return true;
+        return false;
+    }
+
+    /** Returns true iff an @-file is among the javac arguments */
+    public boolean isAtFilePresent() {
+        for (String javacArg : javacArgs)
+            if (javacArg.startsWith("@"))
+                return true;
+        return false;
+    }
+
+    /**
+     * Returns a string representation of the options that affect the result of
+     * the compilation. (Used for saving the state of the options used in a
+     * previous compile.)
+     */
+    public String getStateArgsString() {
+
+        // Local utility class for collecting the arguments
+        class StateArgs {
+
+            private List<String> args = new ArrayList<>();
+
+            void addArg(Option opt) {
+                args.add(opt.arg);
+            }
+
+            void addArg(Option opt, Object val) {
+                addArg(opt);
+                args.add(val.toString());
+            }
+
+            void addSourceLocations(Option opt, List<SourceLocation> locs) {
+                for (SourceLocation sl : locs) {
+                    for (String pkg : sl.includes) addArg(Option.I, pkg);
+                    for (String pkg : sl.excludes) addArg(Option.X, pkg);
+                    for (String f : sl.excludedFiles) addArg(Option.XF, f);
+                    for (String f : sl.includedFiles) addArg(Option.IF, f);
+                    addArg(opt, sl.getPath());
+                }
+            }
+
+            String getResult() {
+                String result = "";
+                for (String s : args)
+                    result += s + " ";
+                return result.trim();
+            }
+
+            public void addAll(Collection<String> toAdd) {
+                args.addAll(toAdd);
+            }
+        }
+
+        StateArgs args = new StateArgs();
+
+        // Directories
+        if (genSrcDir != null)
+            args.addArg(Option.S, genSrcDir.normalize());
+
+        if (headerDir != null)
+            args.addArg(Option.H, headerDir.normalize());
+
+        if (destDir != null)
+            args.addArg(Option.D, destDir.normalize());
+
+        // Source roots
+        args.addSourceLocations(Option.SRC, sources);
+        args.addSourceLocations(Option.SOURCEPATH, sourceSearchPaths);
+        args.addSourceLocations(Option.CLASSPATH,  classSearchPaths);
+        args.addSourceLocations(Option.MODULEPATH, moduleSearchPaths);
+
+        // Boolean options
+        if (permitSourcesInDefaultPackage)
+            args.addArg(Option.PERMIT_SOURCES_WITHOUT_PACKAGE);
+
+        if (permitUnidentifiedArtifact)
+            args.addArg(Option.PERMIT_UNIDENTIFIED_ARTIFACTS);
+
+        // Translation rules
+        for (Map.Entry<String, Transformer> tr : trRules.entrySet()) {
+            String val = tr.getKey() + "=" + tr.getValue().getClass().getName();
+            args.addArg(Option.TR, val);
+        }
+
+        // Javac args
+        args.addAll(javacArgs);
+
+        return args.getResult();
+    }
+
+
+    /** Extract the arguments to be passed on to javac. */
+    public String[] prepJavacArgs() {
+        List<String> args = new ArrayList<>();
+
+        // Output directories
+        args.add("-d");
+        args.add(destDir.toString());
+
+        if (getGenSrcDir() != null) {
+            args.add("-s");
+            args.add(genSrcDir.toString());
+        }
+
+        if (headerDir != null) {
+            args.add("-h");
+            args.add(headerDir.toString());
+        }
+
+        // Prep sourcepath
+        List<SourceLocation> sourcepath = new ArrayList<>();
+        sourcepath.addAll(sources);
+        sourcepath.addAll(sourceSearchPaths);
+        if (sourcepath.size() > 0) {
+            args.add("-sourcepath");
+            args.add(concatenateSourceLocations(sourcepath));
+        }
+
+        // Prep classpath
+        if (classSearchPaths.size() > 0) {
+            args.add("-classpath");
+            args.add(concatenateSourceLocations(classSearchPaths));
+        }
+
+        // This can't be anything but 'none'. Enforced by sjavac main method.
+        args.add("-implicit:" + implicitPolicy);
+
+        // Append javac-options (i.e. pass through options not recognized by
+        // sjavac to javac.)
+        args.addAll(javacArgs);
+
+        return args.toArray(new String[args.size()]);
+    }
+
+    // Helper method to join a list of source locations separated by
+    // File.pathSeparator
+    private static String concatenateSourceLocations(List<SourceLocation> locs) {
+        String s = "";
+        for (SourceLocation loc : locs)
+            s += (s.isEmpty() ? "" : java.io.File.pathSeparator) + loc.getPath();
+        return s;
+    }
+
+    // OptionHelper that records the traversed options in this Options instance.
+    private class ArgDecoderOptionHelper extends OptionHelper {
+
+        List<String> includes, excludes, includeFiles, excludeFiles;
+        {
+            resetFilters();
+        }
+
+        boolean headerProvided = false;
+        boolean genSrcProvided = false;
+
+        @Override
+        public void reportError(String msg) {
+            throw new IllegalArgumentException(msg);
+        }
+
+        @Override
+        public void sourceRoots(List<Path> paths) {
+            sources.addAll(createSourceLocations(paths));
+        }
+
+        @Override
+        public void exclude(String exclPattern) {
+            excludes.add(exclPattern);
+        }
+
+        @Override
+        public void include(String inclPattern) {
+            includes.add(inclPattern);
+        }
+
+        @Override
+        public void excludeFile(String exclFilePattern) {
+            excludeFiles.add(exclFilePattern);
+        }
+
+        @Override
+        public void includeFile(String inclFilePattern) {
+            includeFiles.add(inclFilePattern);
+        }
+
+        @Override
+        public void addTransformer(String suffix, Transformer tr) {
+            if (trRules.containsKey(suffix)) {
+                reportError("More than one transformer specified for " +
+                            "suffix " + suffix + ".");
+                return;
+            }
+            trRules.put(suffix, tr);
+        }
+
+        @Override
+        public void sourcepath(List<Path> paths) {
+            sourceSearchPaths.addAll(createSourceLocations(paths));
+        }
+
+        @Override
+        public void modulepath(List<Path> paths) {
+            moduleSearchPaths.addAll(createSourceLocations(paths));
+        }
+
+        @Override
+        public void classpath(List<Path> paths) {
+            classSearchPaths.addAll(createSourceLocations(paths));
+        }
+
+        @Override
+        public void numCores(int n) {
+            numCores = n;
+        }
+
+        @Override
+        public void logLevel(String level) {
+            logLevel = level;
+        }
+
+        @Override
+        public void compareFoundSources(Path referenceList) {
+            sourceReferenceList = referenceList;
+        }
+
+        @Override
+        public void permitUnidentifiedArtifacts() {
+            permitUnidentifiedArtifact = true;
+        }
+
+        @Override
+        public void permitDefaultPackage() {
+            permitSourcesInDefaultPackage = true;
+        }
+
+        @Override
+        public void serverConf(String conf) {
+            if (serverConf != null)
+                reportError("Can not specify more than one server configuration.");
+            else
+                serverConf = conf;
+        }
+
+        @Override
+        public void implicit(String policy) {
+            implicitPolicy = policy;
+        }
+
+        @Override
+        public void startServerConf(String conf) {
+            if (serverConf != null)
+                reportError("Can not specify more than one server configuration.");
+            else {
+                startServer = true;
+                serverConf = conf;
+            }
+        }
+
+        @Override
+        public void javacArg(String... arg) {
+            javacArgs.addAll(Arrays.asList(arg));
+        }
+
+        @Override
+        public void destDir(Path dir) {
+            if (destDir != null) {
+                reportError("Destination directory already specified.");
+                return;
+            }
+            destDir = dir.toAbsolutePath();
+        }
+
+        @Override
+        public void generatedSourcesDir(Path dir) {
+            if (genSrcProvided) {
+                reportError("Directory for generated sources already specified.");
+                return;
+            }
+            genSrcProvided = true;
+            genSrcDir = dir.toAbsolutePath();
+        }
+
+        @Override
+        public void headerDir(Path dir) {
+            if (headerProvided) {
+                reportError("Header directory already specified.");
+                return;
+            }
+            headerProvided = true;
+            headerDir = dir.toAbsolutePath();
+        }
+
+        private List<SourceLocation> createSourceLocations(List<Path> paths) {
+            List<SourceLocation> result = new ArrayList<>();
+            for (Path path : paths) {
+                result.add(new SourceLocation(
+                        path,
+                        includes,
+                        excludes,
+                        includeFiles,
+                        excludeFiles));
+            }
+            resetFilters();
+            return result;
+        }
+
+        private void resetFilters() {
+            includes = new ArrayList<>();
+            excludes = new ArrayList<>();
+            includeFiles = new ArrayList<>();
+            excludeFiles = new ArrayList<>();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/options/SourceLocation.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.sjavac.options;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.sun.tools.sjavac.Module;
+import com.sun.tools.sjavac.ProblemException;
+import com.sun.tools.sjavac.Source;
+
+/**
+ * Represents a directory to be used for input to sjavac. (For instance a
+ * sourcepath or classpath.)
+ */
+public class SourceLocation {
+
+    // Path to the root directory
+    private Path path;
+
+    // Package include / exclude patterns and file includes / excludes.
+    List<String> includes, excludes, includedFiles, excludedFiles;
+
+    public SourceLocation(Path path,
+                          List<String> includes,
+                          List<String> excludes,
+                          List<String> includedFiles,
+                          List<String> excludedFiles) {
+        this.path = path;
+        this.includes = includes;
+        this.excludes = excludes;
+        this.includedFiles = includedFiles;
+        this.excludedFiles = excludedFiles;
+    }
+
+
+    /**
+     * Finds all files with the given suffix that pass the include / exclude
+     * filters in this source location.
+     *
+     * @param suffixes The set of suffixes to search for
+     * @param foundFiles The map in which to store the found files
+     * @param foundModules The map in which to store the found modules
+     * @param currentModule The current module
+     * @param permitSourcesInDefaultPackage true if sources in default package
+     *                                      are to be permitted
+     * @param inLinksrc true if in link source
+     */
+    public void findSourceFiles(Set<String> suffixes,
+                                Map<String, Source> foundFiles,
+                                Map<String, Module> foundModules,
+                                Module currentModule,
+                                boolean permitSourcesInDefaultPackage,
+                                boolean inLinksrc) {
+        try {
+            Source.scanRoot(path.toFile(), suffixes, excludes, includes,
+                    excludedFiles, includedFiles, foundFiles, foundModules,
+                    currentModule, permitSourcesInDefaultPackage, false,
+                    inLinksrc);
+        } catch (ProblemException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /** Get the root directory of this source location */
+    public Path getPath() {
+        return path;
+    }
+
+    /** Get the package include patterns */
+    public List<String> getIncludes() {
+        return includes;
+    }
+
+    /** Get the package exclude patterns */
+    public List<String> getExcludes() {
+        return excludes;
+    }
+
+    /** Get the file include patterns */
+    public List<String> getIncludedFiles() {
+        return includedFiles;
+    }
+
+    /** Get the file exclude patterns */
+    public List<String> getExcludedFiles() {
+        return excludedFiles;
+    }
+
+}
--- a/langtools/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Tue Apr 22 16:51:10 2014 +0200
@@ -104,6 +104,17 @@
             allPortFiles = new HashMap<>();
         }
         PortFile pf = allPortFiles.get(filename);
+
+        // Port file known. Does it still exist?
+        if (pf != null) {
+            try {
+                if (!pf.exists())
+                    pf = null;
+            } catch (IOException ioex) {
+                ioex.printStackTrace();
+            }
+        }
+
         if (pf == null) {
             pf = new PortFile(filename);
             allPortFiles.put(filename, pf);
@@ -305,7 +316,7 @@
                     // We could not connect to the server. Try again.
                     attempts++;
                     try {
-                        Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
+                        Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS * 1000);
                     } catch (InterruptedException e) {
                     }
                 }
--- a/langtools/src/share/classes/com/sun/tools/sjavac/server/PortFile.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/sjavac/server/PortFile.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/ExclPattern.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class ExclPattern {
+
+    public static void main(String[] ignore) throws IOException {
+
+        String toBeExcluded = "pkg/excl-dir/excluded.txt";
+        String toBeIncluded = "pkg/incl-dir/included.txt";
+
+        // Set up source directory with directory to be excluded
+        populate(Paths.get("srcdir"),
+            "pkg/SomeClass.java",
+            "package pkg; public class SomeClass { }",
+
+            toBeExcluded,
+            "This file should not end up in the dest directory.",
+
+            toBeIncluded,
+            "This file should end up in the dest directory.");
+
+        String[] args = {
+                "-x", "pkg/excl-dir/*",
+                "-src", "srcdir",
+                "-d", "dest",
+                "-j", "1",
+                "-copy", ".txt",
+                "--server:portfile=testserver,background=false",
+                "--log=debug"
+        };
+
+        int rc = new com.sun.tools.sjavac.Main().go(args, System.out, System.err);
+        if (rc != 0) throw new RuntimeException("Error during compile!");
+
+        if (!Files.exists(Paths.get("dest/" + toBeIncluded)))
+            throw new AssertionError("File missing: " + toBeIncluded);
+
+        if (Files.exists(Paths.get("dest/" + toBeExcluded)))
+            throw new AssertionError("File present: " + toBeExcluded);
+    }
+
+    static void populate(Path root, String... args) throws IOException {
+        if (!Files.exists(root))
+            Files.createDirectory(root);
+        for (int i = 0; i < args.length; i += 2) {
+            String filename = args[i];
+            String content = args[i+1];
+            Path p = root.resolve(filename);
+            Files.createDirectories(p.getParent());
+            try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(p,
+                    Charset.defaultCharset()))) {
+                out.println(content);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/ExclPatternWrapper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8037085
+ * @summary Ensures that sjavac can handle various exclusion patterns.
+ * @run main ExclPatternWrapper
+ */
+public class ExclPatternWrapper {
+    public static void main(String... args) throws Exception {
+        SJavacTestUtil.runSjavacTest("ExclPattern", args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/JavacOptionPrep.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import com.sun.tools.sjavac.options.Options;
+
+
+public class JavacOptionPrep {
+
+    enum TestPath {
+        CP1, CP2, SRC1, SRC2, SOURCEPATH1, SOURCEPATH2;
+
+        public String toString() {
+            return name().toLowerCase();
+        }
+    }
+
+    private final static String SEP = File.pathSeparator;
+
+    public static void main(String[] unused) throws IOException {
+
+        for (TestPath p : TestPath.values())
+            Files.createDirectory(Paths.get(p.toString()));
+
+        // Test some various cases:
+        //  - Paths combined with File.pathSeparator (CP1 / CP2)
+        //  - Paths given as duplicate options (SOURCEPATH1 / SOURCEPATH2)
+        //  - Sources provided by -src (SRC1)
+        //  - Sources provided without preceding option (SRC2)
+        //  - An unrecognized option which is to be passed on to javac
+        String sjavacArgs = "-cp " + TestPath.CP1 + SEP + TestPath.CP2 +
+                            " -d dest " +
+                            " -h header" +
+                            " -sourcepath " + TestPath.SOURCEPATH1 +
+                            " -src " + TestPath.SRC1 +
+                            " -s gensrc" +
+                            " -sourcepath " + TestPath.SOURCEPATH2 +
+                            " " + TestPath.SRC2 +
+                            " -unrecognized";
+
+        Options options = Options.parseArgs(sjavacArgs.split(" "));
+
+        // Extract javac-options
+        String[] javacArgs = options.prepJavacArgs();
+
+        // Check the result
+        boolean destDirFound = false;
+        boolean headerDirFound = false;
+        boolean gensrcDirFound = false;
+        boolean classPathFound = false;
+        boolean sourcePathFound = false;
+        boolean unrecognizedFound = false;
+        boolean implicitNoneFound = false;
+
+        Iterator<String> javacArgIter = Arrays.asList(javacArgs).iterator();
+        while (javacArgIter.hasNext()) {
+
+            String option = javacArgIter.next();
+
+            switch (option) {
+            case "-classpath":
+            case "-cp":
+                classPathFound = true;
+                assertEquals(TestPath.CP1 + SEP + TestPath.CP2,
+                             javacArgIter.next());
+                break;
+
+            case "-d":
+                destDirFound = true;
+                assertEquals(Paths.get("dest").toAbsolutePath().toString(),
+                             javacArgIter.next());
+                break;
+
+            case "-h":
+                headerDirFound = true;
+                assertEquals(Paths.get("header").toAbsolutePath().toString(),
+                             javacArgIter.next());
+                break;
+
+            case "-s":
+                gensrcDirFound = true;
+                assertEquals(Paths.get("gensrc").toAbsolutePath().toString(),
+                             javacArgIter.next());
+                break;
+
+            case "-sourcepath":
+                sourcePathFound = true;
+                assertEquals(TestPath.SRC1 + SEP +
+                             TestPath.SRC2 + SEP +
+                             TestPath.SOURCEPATH1 + SEP +
+                             TestPath.SOURCEPATH2,
+                             javacArgIter.next());
+                break;
+
+            case "-unrecognized":
+                unrecognizedFound = true;
+                break;
+
+            case "-implicit:none":
+                implicitNoneFound = true;
+                break;
+
+                // Note that *which* files to actually compile is not dealt
+                // with by prepJavacArgs.
+
+            default:
+                throw new AssertionError("Unexpected option found: " + option);
+            }
+        }
+
+        if (!destDirFound)
+            throw new AssertionError("Dest directory not found.");
+
+        if (!headerDirFound)
+            throw new AssertionError("Header directory not found.");
+
+        if (!gensrcDirFound)
+            throw new AssertionError("Generated source directory not found.");
+
+        if (!classPathFound)
+            throw new AssertionError("Class path not found.");
+
+        if (!sourcePathFound)
+            throw new AssertionError("Source path not found.");
+
+        if (!unrecognizedFound)
+            throw new AssertionError("\"-unrecognized\" not found.");
+
+        if (!implicitNoneFound)
+            throw new AssertionError("\"-implicit:none\" not found.");
+
+    }
+
+    static void assertEquals(Object expected, Object actual) {
+        if (!expected.equals(actual))
+            throw new AssertionError("Expected " + expected + " but got " + actual);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/JavacOptionPrepWrapper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8035063
+ * @summary Tests the preparation of javac-arguments.
+ * @run main JavacOptionPrepWrapper
+ */
+public class JavacOptionPrepWrapper {
+    public static void main(String... args) throws Exception {
+        SJavacTestUtil.runSjavacTest("JavacOptionPrep", args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/OptionDecoding.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static util.OptionTestUtil.assertEquals;
+import static util.OptionTestUtil.checkFilesFound;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.tools.sjavac.CopyFile;
+import com.sun.tools.sjavac.Main;
+import com.sun.tools.sjavac.Module;
+import com.sun.tools.sjavac.Source;
+import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.options.SourceLocation;
+
+public class OptionDecoding {
+
+    public static void main(String[] args) throws IOException {
+
+        testPaths();
+        testDupPaths();
+        testSourceLocations();
+        testSimpleOptions();
+        testServerConf();
+        testSearchPaths();
+        testTranslationRules();
+
+    }
+
+    // Test decoding of output paths
+    static void testPaths() throws IOException {
+
+        final String H = "headers";
+        final String G = "gensrc";
+        final String D = "dest";
+        final String CMP = "srcRefList.txt";
+
+        Options options = Options.parseArgs("-h", H, "-s", G, "-d", D,
+                                            "--compare-found-sources", CMP);
+
+        assertEquals(Paths.get(H).toAbsolutePath(), options.getHeaderDir());
+        assertEquals(Paths.get(G).toAbsolutePath(), options.getGenSrcDir());
+        assertEquals(Paths.get(D).toAbsolutePath(), options.getDestDir());
+        assertEquals(Paths.get(CMP), options.getSourceReferenceList());
+
+    }
+
+    // Providing duplicate header / dest / gensrc paths should produce an error.
+    static void testDupPaths() throws IOException {
+
+        try {
+            Options.parseArgs("-h", "dir1", "-h", "dir2");
+            throw new RuntimeException("Duplicate header directories should fail.");
+        } catch (IllegalArgumentException iae) {
+            // Expected
+        }
+
+        try {
+            Options.parseArgs("-s", "dir1", "-s", "dir2");
+            throw new RuntimeException("Duplicate paths for generated sources should fail.");
+        } catch (IllegalArgumentException iae) {
+            // Expected
+        }
+
+        try {
+            Options.parseArgs("-d", "dir1", "-d", "dir2");
+            throw new RuntimeException("Duplicate destination directories should fail.");
+        } catch (IllegalArgumentException iae) {
+            // Expected
+        }
+
+    }
+
+    // Test source locations and -x, -i, -xf, -if filters
+    static void testSourceLocations() throws IOException {
+
+        Path a1 = Paths.get("root/pkg1/ClassA1.java");
+        Path a2 = Paths.get("root/pkg1/ClassA2.java");
+        Path b1 = Paths.get("root/pkg1/pkg2/ClassB1.java");
+        Path b2 = Paths.get("root/pkg1/pkg2/ClassB2.java");
+        Path c1 = Paths.get("root/pkg3/ClassC1.java");
+        Path c2 = Paths.get("root/pkg3/ClassC2.java");
+
+        for (Path p : Arrays.asList(a1, a2, b1, b2, c1, c2)) {
+            Files.createDirectories(p.getParent());
+            Files.createFile(p);
+        }
+
+        // Test -if
+        {
+            Options options = Options.parseArgs("-if", "root/pkg1/ClassA1.java", "root");
+
+            Map<String, Source> foundFiles = new HashMap<>();
+            Main.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
+                    new HashMap<String, Module>(), new Module("", ""), false, true);
+
+            checkFilesFound(foundFiles.keySet(), a1);
+        }
+
+        // Test -i
+        System.out.println("--------------------------- CHECKING -i ----------------");
+        {
+            Options options = Options.parseArgs("-i", "pkg1/*", "root");
+
+            Map<String, Source> foundFiles = new HashMap<>();
+            Main.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
+                    new HashMap<String, Module>(), new Module("", ""), false, true);
+
+            checkFilesFound(foundFiles.keySet(), a1, a2, b1, b2);
+        }
+        System.out.println("--------------------------------------------------------");
+
+        // Test -xf
+        {
+            Options options = Options.parseArgs("-xf", "root/pkg1/ClassA1.java", "root");
+
+            Map<String, Source> foundFiles = new HashMap<>();
+            Main.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
+                    new HashMap<String, Module>(), new Module("", ""), false, true);
+
+            checkFilesFound(foundFiles.keySet(), a2, b1, b2, c1, c2);
+        }
+
+        // Test -x
+        {
+            Options options = Options.parseArgs("-i", "pkg1/*", "root");
+
+            Map<String, Source> foundFiles = new HashMap<>();
+            Main.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
+                    new HashMap<String, Module>(), new Module("", ""), false, true);
+
+            checkFilesFound(foundFiles.keySet(), a1, a2, b1, b2);
+        }
+
+        // Test -x and -i
+        {
+            Options options = Options.parseArgs("-i", "pkg1/*", "-x", "pkg1/pkg2/*", "root");
+
+            Map<String, Source> foundFiles = new HashMap<>();
+            Main.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
+                    new HashMap<String, Module>(), new Module("", ""), false, true);
+
+            checkFilesFound(foundFiles.keySet(), a1, a2);
+        }
+
+    }
+
+    // Test basic options
+    static void testSimpleOptions() {
+
+        Options options = Options.parseArgs("-j", "17", "--log=debug");
+        assertEquals(17, options.getNumCores());
+        assertEquals("debug", options.getLogLevel());
+        assertEquals(false, options.isDefaultPackagePermitted());
+        assertEquals(false, options.isUnidentifiedArtifactPermitted());
+
+        options = Options.parseArgs("--permit-unidentified-artifacts",
+                                    "--permit-sources-without-package");
+        assertEquals("info", options.getLogLevel());
+        assertEquals(true, options.isDefaultPackagePermitted());
+        assertEquals(true, options.isUnidentifiedArtifactPermitted());
+    }
+
+    // Test server configuration options
+    static void testServerConf() {
+        Options options = Options.parseArgs("--server:someServerConfiguration");
+        assertEquals("someServerConfiguration", options.getServerConf());
+        assertEquals(false, options.startServerFlag());
+
+        options = Options.parseArgs("--startserver:someServerConfiguration");
+        assertEquals("someServerConfiguration", options.getServerConf());
+        assertEquals(true, options.startServerFlag());
+    }
+
+    // Test input paths
+    static void testSearchPaths() {
+
+        List<String> i, x, iF, xF;
+        i = x = iF = xF = new ArrayList<>();
+
+        SourceLocation dir1 = new SourceLocation(Paths.get("dir1"), i, x, iF, xF);
+        SourceLocation dir2 = new SourceLocation(Paths.get("dir2"), i, x, iF, xF);
+
+        Options options = Options.parseArgs("-sourcepath", "dir1:dir2");
+        assertEquals(options.getSourceSearchPaths(), Arrays.asList(dir1, dir2));
+
+        options = Options.parseArgs("-modulepath", "dir1:dir2");
+        assertEquals(options.getModuleSearchPaths(), Arrays.asList(dir1, dir2));
+
+        options = Options.parseArgs("-classpath", "dir1:dir2");
+        assertEquals(options.getClassSearchPath(), Arrays.asList(dir1, dir2));
+    }
+
+    // Test -tr option
+    static void testTranslationRules() {
+
+        Class<?> cls = com.sun.tools.sjavac.CompileJavaPackages.class;
+
+        Options options = Options.parseArgs(
+                "-tr", ".exa=" + cls.getName(),
+                "-tr", ".exb=" + cls.getName(),
+                "-copy", ".html");
+
+        assertEquals(cls, options.getTranslationRules().get(".exa").getClass());
+        assertEquals(cls, options.getTranslationRules().get(".exb").getClass());
+        assertEquals(CopyFile.class, options.getTranslationRules().get(".html").getClass());
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/OptionDecodingWrapper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8035063
+ * @summary Tests decoding of String[] into Options.
+ * @run main OptionDecodingWrapper
+ */
+public class OptionDecodingWrapper {
+    public static void main(String... args) throws Exception {
+        SJavacTestUtil.runSjavacTest("OptionDecoding", args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/SJavacTestUtil.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.lang.reflect.Method;
+
+
+public class SJavacTestUtil {
+
+    public static void runSjavacTest(String testClassName, String[] args)
+            throws Exception {
+
+        if (!isSJavacOnClassPath()) {
+            System.out.println("sjavac not available: pass by default");
+            return;
+        }
+
+        File srcDir = new File(System.getProperty("test.src"));
+        File clsDir = new File(System.getProperty("test.classes"));
+
+        File src = new File(srcDir, testClassName + ".java");
+        File cls = new File(clsDir, testClassName + ".class");
+
+        if (cls.lastModified() < src.lastModified()) {
+            System.err.println("Recompiling test class...");
+            String[] javacArgs = { "-d", clsDir.getPath(), src.getPath() };
+            int rc = com.sun.tools.javac.Main.compile(javacArgs);
+            if (rc != 0)
+                throw new Exception("compilation failed");
+        }
+
+        Class<?> sjavac = Class.forName(testClassName);
+        Method main = sjavac.getMethod("main", String[].class);
+        main.invoke(null, new Object[] { args });
+
+    }
+
+    private static boolean isSJavacOnClassPath() {
+        String cls = "com/sun/tools/sjavac/Main.class";
+        return SJavacTestUtil.class.getClassLoader().getResource(cls) != null;
+    }
+}
--- a/langtools/test/tools/sjavac/SJavacWrapper.java	Mon Apr 21 22:51:49 2014 +0100
+++ b/langtools/test/tools/sjavac/SJavacWrapper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,37 +31,8 @@
  * @run main SJavacWrapper
  */
 
-import java.io.*;
-import java.lang.reflect.Method;
-import java.net.*;
-
-
-public
-class SJavacWrapper {
-
+public class SJavacWrapper {
     public static void main(String... args) throws Exception {
-        URL url = SJavacWrapper.class.getClassLoader().getResource("com/sun/tools/sjavac/Main.class");
-        if (url == null) {
-            // No sjavac in the classpath.
-            System.out.println("sjavac not available: pass by default");
-            return;
-        }
-
-        File testSrc = new File(System.getProperty("test.src"));
-        File sjavac_java = new File(testSrc, "SJavac.java");
-        File testClasses = new File(System.getProperty("test.classes"));
-        File sjavac_class = new File(testClasses, "SJavac.class");
-        if (sjavac_class.lastModified() < sjavac_java.lastModified()) {
-            String[] javac_args = { "-d", testClasses.getPath(), sjavac_java.getPath() };
-            System.err.println("Recompiling SJavac.java");
-            int rc = com.sun.tools.javac.Main.compile(javac_args);
-            if (rc != 0)
-                throw new Exception("compilation failed");
-        }
-
-        Class<?> sjavac = Class.forName("SJavac");
-        Method sjavac_main = sjavac.getMethod("main", String[].class);
-        sjavac_main.invoke(null, new Object[] { args });
+        SJavacTestUtil.runSjavacTest("SJavac", args);
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/Serialization.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static util.OptionTestUtil.assertEquals;
+
+import java.io.IOException;
+import java.util.Map;
+
+import com.sun.tools.sjavac.CompileJavaPackages;
+import com.sun.tools.sjavac.Transformer;
+import com.sun.tools.sjavac.options.Option;
+import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.options.SourceLocation;
+
+
+public class Serialization {
+
+    public static void main(String[] args) throws IOException {
+
+        // Create reference options
+        Options options1 = Options.parseArgs(
+                Option.H.arg, "headers",
+                Option.S.arg, "gensrc",
+                Option.D.arg, "dest",
+                Option.I.arg, "pkg/*",
+                Option.X.arg, "pkg/pkg/*",
+                Option.IF.arg, "root/pkg/MyClass1.java",
+                Option.XF.arg, "root/pkg/MyClass2.java",
+                Option.SRC.arg, "root",
+                Option.SOURCEPATH.arg, "sourcepath",
+                Option.CLASSPATH.arg, "classpath",
+                Option.MODULEPATH.arg, "modulepath",
+                Option.PERMIT_SOURCES_WITHOUT_PACKAGE.arg,
+                Option.PERMIT_UNIDENTIFIED_ARTIFACTS.arg,
+                Option.TR.arg, ".prop=" + CompileJavaPackages.class.getName(),
+                Option.J.arg, "999",
+                "-someJavacArg",
+                "-someOtherJavacArg");
+
+        // Serialize
+        String serialized = options1.getStateArgsString();
+
+        // Deserialize
+        Options options2 = Options.parseArgs(serialized.split(" "));
+
+        // Make sure we got the same result
+        assertEquals(options1.getHeaderDir(), options2.getHeaderDir());
+        assertEquals(options1.getGenSrcDir(), options2.getGenSrcDir());
+        assertEquals(options1.getDestDir(), options2.getDestDir());
+
+        SourceLocation sl1 = options1.getSources().get(0);
+        SourceLocation sl2 = options2.getSources().get(0);
+        assertEquals(sl1.getPath(), sl2.getPath());
+        assertEquals(sl1.getIncludes(), sl2.getIncludes());
+        assertEquals(sl1.getExcludes(), sl2.getExcludes());
+        assertEquals(sl1.getIncludedFiles(), sl2.getIncludedFiles());
+        assertEquals(sl1.getExcludedFiles(), sl2.getExcludedFiles());
+
+        assertEquals(options1.getClassSearchPath(), options2.getClassSearchPath());
+        assertEquals(options1.getSourceSearchPaths(), options2.getSourceSearchPaths());
+        assertEquals(options1.getModuleSearchPaths(), options2.getModuleSearchPaths());
+
+        Map<String, Transformer> trRules1 = options1.getTranslationRules();
+        Map<String, Transformer> trRules2 = options2.getTranslationRules();
+        assertEquals(trRules1.keySet(), trRules2.keySet());
+        assertEquals(trRules1.values().iterator().next().getClass(),
+                     trRules2.values().iterator().next().getClass());
+        assertEquals(options1.getJavacArgs(), options2.getJavacArgs());
+
+        assertEquals(999, options1.getNumCores());
+        if (options2.getNumCores() == 999)
+            throw new AssertionError("Num cores should not be part of serialization");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/SerializationWrapper.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8035063
+ *
+ * @summary Tests serialization of options. The options needs to be serialized
+ *          and saved in the state file since the files need to be recompiled
+ *          if new options are provided.
+ *
+ * @run main SerializationWrapper
+ */
+public class SerializationWrapper {
+    public static void main(String... args) throws Exception {
+        SJavacTestUtil.runSjavacTest("Serialization", args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/sjavac/util/OptionTestUtil.java	Tue Apr 22 16:51:10 2014 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package util;
+
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import com.sun.tools.sjavac.options.SourceLocation;
+
+
+public class OptionTestUtil {
+
+    public static void checkFilesFound(Collection<String> found, Path... expected) {
+
+        Collection<String> expectedStrs = new HashSet<String>();
+        for (Path p : expected)
+            expectedStrs.add(p.toString());
+
+        if (!expectedStrs.containsAll(found))
+            throw new AssertionError("Expected (" + expectedStrs + ") does not " +
+                                     "contain all actual (" + found + ")");
+
+        if (!found.containsAll(expectedStrs))
+            throw new AssertionError("Actual (" + found + ") does not " +
+                                     "contain all expected (" + expectedStrs + ")");
+    }
+
+    public static void assertEquals(List<SourceLocation> expected, List<SourceLocation> actual) {
+        if (expected.size() != actual.size())
+            throw new AssertionError("Expected locs of length " + expected.size() + " but got something of size " + actual.size());
+
+        Iterator<SourceLocation> iter1 = expected.iterator();
+        Iterator<SourceLocation> iter2 = actual.iterator();
+
+        while (iter1.hasNext()) {
+            SourceLocation sl1 = iter1.next();
+            SourceLocation sl2 = iter2.next();
+
+            if (!sl1.getPath().equals(sl2.getPath()) ||
+                    !sl1.getIncludes().equals(sl2.getIncludes()) ||
+                    !sl1.getExcludes().equals(sl2.getExcludes()) ||
+                    !sl1.getIncludedFiles().equals(sl2.getIncludedFiles()) ||
+                    !sl1.getExcludedFiles().equals(sl2.getExcludedFiles()))
+                throw new AssertionError("Expected " + sl1 + " but got " + sl2);
+        }
+    }
+
+    public static void assertEquals(Object expected, Object actual) {
+        if (!expected.equals(actual))
+            throw new AssertionError("Expected " + expected + " but got " + actual);
+    }
+
+}