changeset 1503:22e417cdddee

8004658: Add internal smart javac wrapper to solve JEP 139 Reviewed-by: jjg
author ohrstrom
date Fri, 18 Jan 2013 00:16:21 +0100
parents 2d2b2be57c78
children 3d84ae209919
files make/build.properties make/build.xml src/share/classes/com/sun/tools/sjavac/BuildState.java src/share/classes/com/sun/tools/sjavac/CleanProperties.java src/share/classes/com/sun/tools/sjavac/CompileChunk.java src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java src/share/classes/com/sun/tools/sjavac/CompileProperties.java src/share/classes/com/sun/tools/sjavac/CopyFile.java src/share/classes/com/sun/tools/sjavac/JavacState.java src/share/classes/com/sun/tools/sjavac/Log.java src/share/classes/com/sun/tools/sjavac/Main.java src/share/classes/com/sun/tools/sjavac/Module.java src/share/classes/com/sun/tools/sjavac/Package.java src/share/classes/com/sun/tools/sjavac/ProblemException.java src/share/classes/com/sun/tools/sjavac/Source.java src/share/classes/com/sun/tools/sjavac/Transformer.java src/share/classes/com/sun/tools/sjavac/Util.java src/share/classes/com/sun/tools/sjavac/comp/Dependencies.java src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/PubapiVisitor.java src/share/classes/com/sun/tools/sjavac/comp/ResolveWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/SmartFileManager.java src/share/classes/com/sun/tools/sjavac/comp/SmartFileObject.java src/share/classes/com/sun/tools/sjavac/comp/SmartWriter.java src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java src/share/classes/com/sun/tools/sjavac/server/JavacServer.java src/share/classes/com/sun/tools/sjavac/server/PortFile.java src/share/classes/com/sun/tools/sjavac/server/SysInfo.java test/tools/sjavac/SJavac.java
diffstat 30 files changed, 7449 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/make/build.properties	Thu Jan 17 18:15:20 2013 +0000
+++ b/make/build.properties	Fri Jan 18 00:16:21 2013 +0100
@@ -29,18 +29,18 @@
 # Override this path as needed, either on the command line or in
 # one of the standard user build.properties files (see build.xml)
 
-# boot.java.home = /opt/jdk/1.6.0
+# boot.java.home = /opt/jdk/1.7.0
 boot.java = ${boot.java.home}/bin/java
 boot.javac = ${boot.java.home}/bin/javac
-boot.javac.source = 6
-boot.javac.target = 6
+boot.javac.source = 7
+boot.javac.target = 7
 
 # This is the JDK used to run the product version of the tools,
 # for example, for testing. If you're building a complete JDK, specify that.
 # Override this path as needed, either on the command line or in
 # one of the standard user build.properties files (see build.xml)
 
-# target.java.home = /opt/jdk/1.7.0
+# target.java.home = /opt/jdk/1.8.0
 target.java = ${target.java.home}/bin/java
 
 # Version info -- override as needed
@@ -161,6 +161,14 @@
 
 #
 
+sjavac.includes = \
+        com/sun/tools/sjavac/ 
+
+sjavac.tests = \
+        tools/sjavac
+        
+#
+
 # The following files require the latest JDK to be available.
 # The API can be provided by using a suitable boot.java.home
 # or by setting import.jdk
--- a/make/build.xml	Thu Jan 17 18:15:20 2013 +0000
+++ b/make/build.xml	Fri Jan 18 00:16:21 2013 +0100
@@ -241,15 +241,15 @@
     </target>
 
     <target name="build-bootstrap-tools"
-        depends="build-bootstrap-javac,build-bootstrap-javadoc,build-bootstrap-doclets,build-bootstrap-javah"
+        depends="build-bootstrap-javac,build-bootstrap-javadoc,build-bootstrap-doclets,build-bootstrap-javah,build-bootstrap-sjavac"
     />
 
     <target name="build-all-tools"
-        depends="build-javac,build-javadoc,build-doclets,build-javah,build-javap"
+        depends="build-javac,build-javadoc,build-doclets,build-javah,build-javap,build-sjavac"
     />
 
     <target name="build-all-classes" depends="build-bootstrap-javac,-create-import-jdk-stubs">
-        <build-classes includes="${javac.includes} ${javadoc.includes} ${doclets.includes} ${javah.includes} ${javap.includes}"/>
+        <build-classes includes="${javac.includes} ${javadoc.includes} ${doclets.includes} ${javah.includes} ${javap.includes} ${sjavac.includes}"/>
     </target>
 
     <!-- clean -->
@@ -656,6 +656,40 @@
 
     <target name="javap" depends="build-javap,jtreg-javap,findbugs-javap"/>
 
+    <!--
+    **** sjavac targets.
+    -->
+
+    <target name="build-bootstrap-sjavac"
+            depends="-def-build-bootstrap-classes,-def-build-bootstrap-jar,-def-build-bootstrap-tool">
+        <build-bootstrap-classes includes="${sjavac.includes}"/>
+        <build-bootstrap-jar     name="sjavac" includes="${sjavac.includes}"
+                                 jarmainclass="com.sun.tools.sjavac.Main"/>
+        <build-bootstrap-tool    name="sjavac"/>
+    </target>
+
+    <target name="build-classes-sjavac" depends="build-classes-javac">
+        <build-classes includes="${sjavac.includes}"/>
+    </target>
+
+    <target name="build-sjavac" depends="build-classes-sjavac">
+        <build-jar  name="sjavac" includes="${sjavac.includes}"
+                    jarmainclass="com.sun.tools.sjavac.Main"
+                    jarclasspath="sjavac.jar"/>
+        <build-tool name="sjavac"/>
+    </target>
+    
+    <!-- (no javadoc for javap) -->
+
+    <target name="jtreg-sjavac" depends="build-sjavac,-def-jtreg">
+        <jtreg-tool name="sjavac" tests="${sjavac.tests}"/>
+    </target>
+
+    <target name="findbugs-sjavac" depends="build-sjavac,-def-findbugs">
+        <findbugs-tool name="sjavac"/>
+    </target>
+
+    <target name="sjavac" depends="build-sjavac,jtreg-sjavac,findbugs-sjavac"/>
 
     <!--
     **** Create import JDK stubs.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/BuildState.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The build state class captures the source code and generated artifacts
+ * from a build. There are usually two build states, the previous one (prev),
+ * loaded from the javac_state file, and the current one (now).
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class BuildState {
+    private Map<String,Module> modules = new HashMap<String,Module>();
+    private Map<String,Package> packages = new HashMap<String,Package>();
+    private Map<String,Source> sources = new HashMap<String,Source>();
+    private Map<String,File> artifacts = new HashMap<String,File>();
+    // Map from package to a set of packages that depend on said package.
+    private Map<String,Set<String>> dependents = new HashMap<String,Set<String>>();
+
+    public  Map<String,Module> modules() { return modules; }
+    public  Map<String,Package> packages() { return packages; }
+    public  Map<String,Source> sources() { return sources; }
+    public  Map<String,File> artifacts() { return artifacts; }
+    public  Map<String,Set<String>> dependents() { return dependents; }
+
+    /**
+     * Lookup a module from a name. Create the module if it does
+     * not exist yet.
+     */
+    public Module lookupModule(String mod) {
+        Module m = modules.get(mod);
+        if (m == null) {
+            m = new Module(mod, "???");
+            modules.put(mod, m);
+        }
+        return m;
+    }
+
+    /**
+     * Find a module from a given package name. For example:
+     * The package name "base:java.lang" will fetch the module named "base".
+     * The package name ":java.net" will fetch the default module.
+     */
+    Module findModuleFromPackageName(String pkg) {
+        int cp = pkg.indexOf(':');
+        assert(cp != -1);
+        String mod = pkg.substring(0, cp);
+        return lookupModule(mod);
+    }
+
+    /**
+     * Collect all packages, sources and artifacts for all modules
+     * into the build state.
+     *
+     * @param m The set of modules.
+     */
+    public void collectPackagesSourcesAndArtifacts(Map<String,Module> m) {
+        modules = m;
+        // Extract all the found packages.
+        for (Module i : modules.values()) {
+            for (Map.Entry<String,Package> j : i.packages().entrySet()) {
+                Package p = packages.get(j.getKey());
+                // Check that no two different packages are stored under same name.
+                assert(p == null || p == j.getValue());
+                if (p == null) {
+                    p = j.getValue();
+                    packages.put(j.getKey(),j.getValue());
+                }
+                for (Map.Entry<String,Source> k : p.sources().entrySet()) {
+                    Source s = sources.get(k.getKey());
+                    // Check that no two different sources are stored under same name.
+                    assert(s == null || s == k.getValue());
+                    if (s == null) {
+                        s = k.getValue();
+                        sources.put(k.getKey(), k.getValue());
+                    }
+                }
+                for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
+                    File f = artifacts.get(g.getKey());
+                    // Check that no two artifacts are stored under the same file.
+                    assert(f == null || f == g.getValue());
+                    if (f == null) {
+                        f = g.getValue();
+                        artifacts.put(g.getKey(), g.getValue());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Collect all the artifacts of all modules and packages.
+     *
+     * @param m The set of modules.
+     */
+    public void collectArtifacts(Map<String,Module> m) {
+        modules = m;
+        // Extract all the found packages.
+        for (Module i : modules.values()) {
+            for (Map.Entry<String,Package> j : i.packages().entrySet()) {
+                Package p = packages.get(j.getKey());
+                // Check that no two different packages are stored under same name.
+                assert(p == null || p == j.getValue());
+                p = j.getValue();
+                packages.put(j.getKey(),j.getValue());
+                for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
+                    File f = artifacts.get(g.getKey());
+                    // Check that no two artifacts are stored under the same file.
+                    assert(f == null || f == g.getValue());
+                    artifacts.put(g.getKey(), g.getValue());
+                }
+            }
+        }
+    }
+
+    /**
+     * Calculate the package dependents (ie the reverse of the dependencies).
+     */
+    public void calculateDependents() {
+        dependents = new HashMap<String,Set<String>>();
+        for (String s : packages.keySet()) {
+            Package p = packages.get(s);
+            for (String d : p.dependencies()) {
+                Set<String> ss = dependents.get(d);
+                if (ss == null) {
+                    ss = new HashSet<String>();
+                    dependents.put(d, ss);
+                }
+                // Add the dependent information to the global dependent map.
+                ss.add(s);
+                Package dp = packages.get(d);
+                // Also add the dependent information to the package specific map.
+                // Normally, you do not compile java.lang et al. Therefore
+                // there are several packages that p depends upon that you
+                // do not have in your state database. This is perfectly fine.
+                if (dp != null) {
+                    // But this package did exist in the state database.
+                    dp.addDependent(p.name());
+                }
+            }
+        }
+    }
+
+    /**
+     * Verify that the setModules method above did the right thing when
+     * running through the module->package->source structure.
+     */
+    public void checkInternalState(String msg, boolean linkedOnly, Map<String,Source> srcs) {
+        boolean baad = false;
+        Map<String,Source> original = new HashMap<String,Source>();
+        Map<String,Source> calculated = new HashMap<String,Source>();
+
+        for (String s : sources.keySet()) {
+            Source ss = sources.get(s);
+            if (ss.isLinkedOnly() == linkedOnly) {
+                calculated.put(s,ss);
+            }
+        }
+        for (String s : srcs.keySet()) {
+            Source ss = srcs.get(s);
+            if (ss.isLinkedOnly() == linkedOnly) {
+                original.put(s,ss);
+            }
+        }
+        if (original.size() != calculated.size()) {
+            Log.error("INTERNAL ERROR "+msg+" original and calculated are not the same size!");
+            baad = true;
+        }
+        if (!original.keySet().equals(calculated.keySet())) {
+            Log.error("INTERNAL ERROR "+msg+" original and calculated do not have the same domain!");
+            baad = true;
+        }
+        if (!baad) {
+            for (String s : original.keySet()) {
+                Source s1 = original.get(s);
+                Source s2 = calculated.get(s);
+                if (s1 == null || s2 == null || !s1.equals(s2)) {
+                    Log.error("INTERNAL ERROR "+msg+" original and calculated have differing elements for "+s);
+                }
+                baad = true;
+            }
+        }
+        if (baad) {
+            for (String s : original.keySet()) {
+                Source ss = original.get(s);
+                Source sss = calculated.get(s);
+                if (sss == null) {
+                    Log.error("The file "+s+" does not exist in calculated tree of sources.");
+                }
+            }
+            for (String s : calculated.keySet()) {
+                Source ss = calculated.get(s);
+                Source sss = original.get(s);
+                if (sss == null) {
+                    Log.error("The file "+s+" does not exist in original set of found sources.");
+                }
+            }
+        }
+    }
+
+    /**
+     * Load a module from the javac state file.
+     */
+    public Module loadModule(String l) {
+        Module m = Module.load(l);
+        modules.put(m.name(), m);
+        return m;
+    }
+
+    /**
+     * Load a package from the javac state file.
+     */
+    public Package loadPackage(Module lastModule, String l) {
+        Package p = Package.load(lastModule, l);
+        lastModule.addPackage(p);
+        packages.put(p.name(), p);
+        return p;
+    }
+
+    /**
+     * Load a source from the javac state file.
+     */
+    public Source loadSource(Package lastPackage, String l, boolean is_generated) {
+        Source s = Source.load(lastPackage, l, is_generated);
+        lastPackage.addSource(s);
+        sources.put(s.name(), s);
+        return s;
+    }
+
+    /**
+     * During an incremental compile we need to copy the old javac state
+     * information about packages that were not recompiled.
+     */
+    public void copyPackagesExcept(BuildState prev, Set<String> recompiled, Set<String> removed) {
+        for (String pkg : prev.packages().keySet()) {
+            // Do not copy recompiled or removed packages.
+            if (recompiled.contains(pkg) || removed.contains(pkg)) continue;
+            Module mnew = findModuleFromPackageName(pkg);
+            Package pprev = prev.packages().get(pkg);
+            mnew.addPackage(pprev);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2001, 2012, 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;
+
+import java.io.*;
+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;
+
+/**
+ * The clean properties transform should not be necessary.
+ * Eventually we will cleanup the property file sources in the OpenJDK instead.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CleanProperties implements Transformer
+{
+    public void setExtra(String e) {
+        // Any extra information is ignored for clean properties.
+    }
+
+    public void setExtra(String[] a) {
+        // Any extra information is ignored for clean properties.
+    }
+
+    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+                             Set<URI>             visibleSrcs,
+                             Map<URI,Set<String>> visibleClasses,
+                             Map<String,Set<String>> oldPackageDependencies,
+                             URI destRoot,
+                             Map<String,Set<URI>>    packageArtifacts,
+                             Map<String,Set<String>> packageDependencies,
+                             Map<String,String>      packagePublicApis,
+                             int debugLevel,
+                             boolean incremental,
+                             int numCores,
+                             PrintStream out,
+                             PrintStream err)
+    {
+        boolean rc = true;
+        for (String pkgName : pkgSrcs.keySet()) {
+            String pkgNameF = pkgName.replace('.',File.separatorChar);
+            for (URI u : pkgSrcs.get(pkgName)) {
+                File src = new File(u);
+                boolean r = clean(pkgName, pkgNameF, src, new File(destRoot), debugLevel,
+                                  packageArtifacts);
+                if (r == false) {
+                    rc = false;
+                }
+            }
+        }
+        return rc;
+    }
+
+    boolean clean(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
+                  Map<String,Set<URI>> packageArtifacts)
+    {
+        // Load the properties file.
+        Properties p = new Properties();
+        try {
+            p.load(new FileInputStream(src));
+        } catch (IOException e) {
+            Log.error("Error reading file "+src.getPath());
+            return false;
+        }
+
+        // Sort the properties in increasing key order.
+        List<String> sortedKeys = new ArrayList<String>();
+        for (Object key : p.keySet()) {
+            sortedKeys.add((String)key);
+        }
+        Collections.sort(sortedKeys);
+        Iterator<String> keys = sortedKeys.iterator();
+
+        // Collect the properties into a string buffer.
+        StringBuilder data = new StringBuilder();
+        while (keys.hasNext()) {
+            String key = keys.next();
+            data.append(CompileProperties.escape(key)+":"+CompileProperties.escape((String)p.get(key))+"\n");
+        }
+
+        String destFilename = destRoot.getPath()+File.separator+pkgNameF+File.separator+src.getName();
+        File dest = new File(destFilename);
+
+        // Make sure the dest directories exist.
+        if (!dest.getParentFile().isDirectory()) {
+            if (!dest.getParentFile().mkdirs()) {
+                Log.error("Could not create the directory "+dest.getParentFile().getPath());
+                return false;
+            }
+        }
+
+        Set<URI> as = packageArtifacts.get(pkgName);
+        if (as == null) {
+            as = new HashSet<URI>();
+            packageArtifacts.put(pkgName, as);
+        }
+        as.add(dest.toURI());
+
+        if (dest.exists() && dest.lastModified() > src.lastModified()) {
+            // A cleaned property file exists, and its timestamp is newer than the source.
+            // Assume that we do not need to clean!
+            // Thus we are done.
+            return true;
+        }
+
+        Log.info("Cleaning property file "+pkgNameF+File.separator+src.getName());
+        try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest)))) {
+            writer.write(data.toString());
+        } catch ( IOException e ) {
+            Log.error("Could not write file "+dest.getPath());
+            return false;
+        }
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/CompileChunk.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A compile chunk is a list of sources/packages to be compiled. Possibly a subset of
+ * the total number of sources/packages to be compiled for this sjavac invocation.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CompileChunk implements Comparable<CompileChunk> {
+    public int numPackages;
+    public int numDependents;
+    public Set<URI> srcs = new HashSet<URI>();
+    public StringBuilder pkgNames = new StringBuilder();
+    public String pkgFromTos = "";
+
+    public int compareTo(CompileChunk c) {
+        if (numDependents == c.numDependents) return 0;
+        if (numDependents > c.numDependents) return -1;
+        return -1;
+    }
+
+    boolean equal(CompileChunk c) {
+        return numDependents == c.numDependents;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2012, 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;
+
+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.server.JavacServer;
+import com.sun.tools.sjavac.server.SysInfo;
+import java.io.PrintStream;
+
+/**
+ * This transform compiles a set of packages containing Java sources.
+ * The compile request is divided into separate sets of source files.
+ * For each set a separate request thread is dispatched to a javac server
+ * and the meta data is accumulated. The number of sets correspond more or
+ * less to the number of cores. Less so now, than it will in the future.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CompileJavaPackages implements Transformer {
+
+    // The current limited sharing of data between concurrent JavaCompilers
+    // in the server will not give speedups above 3 cores. Thus this limit.
+    // We hope to improve this in the future.
+    final static int limitOnConcurrency = 3;
+
+    String serverSettings;
+    public void setExtra(String e) {
+        serverSettings = e;
+    }
+
+    String[] args;
+    public void setExtra(String[] a) {
+        args = a;
+    }
+
+    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+                             Set<URI>             visibleSources,
+                             Map<URI,Set<String>> visibleClasses,
+                             Map<String,Set<String>> oldPackageDependents,
+                             URI destRoot,
+                             final Map<String,Set<URI>>    packageArtifacts,
+                             final Map<String,Set<String>> packageDependencies,
+                             final Map<String,String>      packagePubapis,
+                             int debugLevel,
+                             boolean incremental,
+                             int numCores,
+                             PrintStream out,
+                             PrintStream err)
+    {
+        boolean rc = true;
+        boolean concurrentCompiles = true;
+
+        // Fetch the id.
+        String id = Util.extractStringOption("id", serverSettings);
+        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);
+
+        // Get maximum heap size from the server!
+        SysInfo sysinfo = JavacServer.connectGetSysInfo(psServerSettings, out, err);
+        if (sysinfo.numCores == -1) {
+            Log.error("Could not query server for sysinfo!");
+            return false;
+        }
+        int numMBytes = (int)(sysinfo.maxMemory / ((long)(1024*1024)));
+        Log.debug("Server reports "+numMBytes+"MiB of memory and "+sysinfo.numCores+" cores");
+
+        if (numCores <= 0) {
+            // Set the requested number of cores to the number of cores on the server.
+            numCores = sysinfo.numCores;
+            Log.debug("Number of jobs not explicitly set, defaulting to "+sysinfo.numCores);
+        } else if (sysinfo.numCores < numCores) {
+            // Set the requested number of cores to the number of cores on the server.
+            Log.debug("Limiting jobs from explicitly set "+numCores+" to cores available on server: "+sysinfo.numCores);
+            numCores = sysinfo.numCores;
+        } else {
+            Log.debug("Number of jobs explicitly set to "+numCores);
+        }
+        // More than three concurrent cores does not currently give a speedup, at least for compiling the jdk
+        // in the OpenJDK. This will change in the future.
+        int numCompiles = numCores;
+        if (numCores > limitOnConcurrency) numCompiles = limitOnConcurrency;
+        // Split the work up in chunks to compiled.
+
+        int numSources = 0;
+        for (String s : pkgSrcs.keySet()) {
+            Set<URI> ss = pkgSrcs.get(s);
+            numSources += ss.size();
+        }
+
+        int sourcesPerCompile = numSources / numCompiles;
+
+        // For 64 bit Java, it seems we can compile the OpenJDK 8800 files with a 1500M of heap
+        // in a single chunk, with reasonable performance.
+        // For 32 bit java, it seems we need 1G of heap.
+        // Number experimentally determined when compiling the OpenJDK.
+        // Includes space for reasonably efficient garbage collection etc,
+        // Calculating backwards gives us a requirement of
+        // 1500M/8800 = 175 KiB for 64 bit platforms
+        // and 1G/8800 = 119 KiB for 32 bit platform
+        // for each compile.....
+        int kbPerFile = 175;
+        String osarch = System.getProperty("os.arch");
+        if (osarch.equals("i386")) {
+            // For 32 bit platforms, assume it is slightly smaller
+            // because of smaller object headers and pointers.
+            kbPerFile = 119;
+        }
+        int numRequiredMBytes = (kbPerFile*numSources)/1024;
+        Log.debug("For os.arch "+osarch+" the empirically determined heap required per file is "+kbPerFile+"KiB");
+        Log.debug("Server has "+numMBytes+"MiB of heap.");
+        Log.debug("Heuristics say that we need "+numRequiredMBytes+"MiB of heap for all source files.");
+        // Perform heuristics to see how many cores we can use,
+        // or if we have to the work serially in smaller chunks.
+        if (numMBytes < numRequiredMBytes) {
+            // Ouch, cannot fit even a single compile into the heap.
+            // Split it up into several serial chunks.
+            concurrentCompiles = false;
+            // Limit the number of sources for each compile to 500.
+            if (numSources < 500) {
+                numCompiles = 1;
+                sourcesPerCompile = numSources;
+                Log.debug("Compiling as a single source code chunk to stay within heap size limitations!");
+            } else if (sourcesPerCompile > 500) {
+                // This number is very low, and tuned to dealing with the OpenJDK
+                // where the source is >very< circular! In normal application,
+                // with less circularity the number could perhaps be increased.
+                numCompiles = numSources / 500;
+                sourcesPerCompile = numSources/numCompiles;
+                Log.debug("Compiling source as "+numCompiles+" code chunks serially to stay within heap size limitations!");
+            }
+        } else {
+            if (numCompiles > 1) {
+                // Ok, we can fit at least one full compilation on the heap.
+                float usagePerCompile = (float)numRequiredMBytes / ((float)numCompiles * (float)0.7);
+                int usage = (int)(usagePerCompile * (float)numCompiles);
+                Log.debug("Heuristics say that for "+numCompiles+" concurrent compiles we need "+usage+"MiB");
+                if (usage > numMBytes) {
+                    // Ouch it does not fit. Reduce to a single chunk.
+                    numCompiles = 1;
+                    sourcesPerCompile = numSources;
+                    // What if the relationship betweem number of compile_chunks and num_required_mbytes
+                    // is not linear? Then perhaps 2 chunks would fit where 3 does not. Well, this is
+                    // something to experiment upon in the future.
+                    Log.debug("Limiting compile to a single thread to stay within heap size limitations!");
+                }
+            }
+        }
+
+        Log.debug("Compiling sources in "+numCompiles+" chunk(s)");
+
+        // Create the chunks to be compiled.
+        final CompileChunk[] compileChunks = createCompileChunks(pkgSrcs, oldPackageDependents,
+                numCompiles, sourcesPerCompile);
+
+        if (Log.isDebugging()) {
+            int cn = 1;
+            for (CompileChunk cc : compileChunks) {
+                Log.debug("Chunk "+cn+" for "+id+" ---------------");
+                cn++;
+                for (URI u : cc.srcs) {
+                    Log.debug(""+u);
+                }
+            }
+        }
+
+        // The return values for each chunked compile.
+        final int[] rn = new int[numCompiles];
+        // The requets, might or might not run as a background thread.
+        final Thread[] requests  = new Thread[numCompiles];
+
+        final Set<URI>             fvisible_sources = visibleSources;
+        final Map<URI,Set<String>> fvisible_classes = visibleClasses;
+
+        long start = System.currentTimeMillis();
+
+        for (int i=0; i<numCompiles; ++i) {
+            final int ii = i;
+            final CompileChunk cc = compileChunks[i];
+
+            // Pass the num_cores and the id (appended with the chunk number) to the server.
+            final String cleanedServerSettings = psServerSettings+",poolsize="+numCores+",id="+id+"-"+ii;
+            final PrintStream fout = out;
+            final PrintStream ferr = err;
+
+            requests[ii] = new Thread() {
+                @Override
+                public void run() {
+                                        rn[ii] = JavacServer.useServer(cleanedServerSettings,
+                                                           Main.removeWrapperArgs(args),
+                                                               cc.srcs,
+                                                           fvisible_sources,
+                                                           fvisible_classes,
+                                                           packageArtifacts,
+                                                           packageDependencies,
+                                                           packagePubapis,
+                                                           null,
+                                                           fout, ferr);
+                }
+            };
+
+            if (cc.srcs.size() > 0) {
+                String numdeps = "";
+                if (cc.numDependents > 0) numdeps = "(with "+cc.numDependents+" dependents) ";
+                if (!incremental || cc.numPackages > 16) {
+                    String info = "("+cc.pkgFromTos+")";
+                    if (info.equals("( to )")) {
+                        info = "";
+                    }
+                    Log.info("Compiling "+cc.srcs.size()+" files "+numdeps+"in "+cc.numPackages+" packages "+info);
+                } else {
+                    Log.info("Compiling "+cc.pkgNames+numdeps);
+                }
+                if (concurrentCompiles) {
+                    requests[ii].start();
+                }
+                else {
+                    requests[ii].run();
+                    // If there was an error, then stop early when running single threaded.
+                    if (rn[i] != 0) {
+                        return false;
+                    }
+                }
+            }
+        }
+        if (concurrentCompiles) {
+            // If there are background threads for the concurrent compiles, then join them.
+            for (int i=0; i<numCompiles; ++i) {
+                try { requests[i].join(); } catch (InterruptedException e) { }
+            }
+        }
+
+        // Check the return values.
+        for (int i=0; i<numCompiles; ++i) {
+            if (compileChunks[i].srcs.size() > 0) {
+                if (rn[i] != 0) {
+                    rc = false;
+                }
+            }
+        }
+        long duration = System.currentTimeMillis() - start;
+        long minutes = duration/60000;
+        long seconds = (duration-minutes*60000)/1000;
+        Log.debug("Compilation of "+numSources+" source files took "+minutes+"m "+seconds+"s");
+
+        return rc;
+    }
+
+
+    /**
+     * Split up the sources into compile chunks. If old package dependents information
+     * is available, sort the order of the chunks into the most dependent first!
+     * (Typically that chunk contains the java.lang package.) In the future
+     * we could perhaps improve the heuristics to put the sources into even more sensible chunks.
+     * Now the package are simple sorted in alphabetical order and chunked, then the chunks
+     * are sorted on how dependent they are.
+     *
+     * @param pkgSrcs The sources to compile.
+     * @param oldPackageDependents Old package dependents, if non-empty, used to sort the chunks.
+     * @param numCompiles The number of chunks.
+     * @param sourcesPerCompile The number of sources per chunk.
+     * @return
+     */
+    CompileChunk[] createCompileChunks(Map<String,Set<URI>> pkgSrcs,
+                                 Map<String,Set<String>> oldPackageDependents,
+                                 int numCompiles,
+                                 int sourcesPerCompile) {
+
+        CompileChunk[] compileChunks = new CompileChunk[numCompiles];
+        for (int i=0; i<compileChunks.length; ++i) {
+            compileChunks[i] = new CompileChunk();
+        }
+
+        // Now go through the packages and spread out the source on the different chunks.
+        int ci = 0;
+        // Sort the packages
+        String[] packageNames = pkgSrcs.keySet().toArray(new String[0]);
+        Arrays.sort(packageNames);
+        String from = null;
+        for (String pkgName : packageNames) {
+            CompileChunk cc = compileChunks[ci];
+            Set<URI> s = pkgSrcs.get(pkgName);
+            if (cc.srcs.size()+s.size() > sourcesPerCompile && ci < numCompiles-1) {
+                from = null;
+                ci++;
+                cc = compileChunks[ci];
+            }
+            cc.numPackages++;
+            cc.srcs.addAll(s);
+
+            // Calculate nice package names to use as information when compiling.
+            String justPkgName = Util.justPackageName(pkgName);
+            // Fetch how many packages depend on this package from the old build state.
+            Set<String> ss = oldPackageDependents.get(pkgName);
+            if (ss != null) {
+                // Accumulate this information onto this chunk.
+                cc.numDependents += ss.size();
+            }
+            if (from == null || from.trim().equals("")) from = justPkgName;
+            cc.pkgNames.append(justPkgName+"("+s.size()+") ");
+            cc.pkgFromTos = from+" to "+justPkgName;
+        }
+        // If we are compiling serially, sort the chunks, so that the chunk (with the most dependents) (usually the chunk
+        // containing java.lang.Object, is to be compiled first!
+        // For concurrent compilation, this does not matter.
+        Arrays.sort(compileChunks);
+        return compileChunks;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.*;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Compile properties transform a properties file into a Java source file.
+ * Java has built in support for reading properties from either a text file
+ * in the source or a compiled java source file.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CompileProperties implements Transformer
+{
+    // Any extra information passed from the command line, for example if:
+    // -tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
+    // then extra will be "sun.util.resources.LocaleNamesBundle"
+    String extra;
+
+    public void setExtra(String e) {
+        extra = e;
+    }
+
+    public void setExtra(String[] a) {
+    }
+
+    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+                             Set<URI>             visibleSrcs,
+                             Map<URI,Set<String>> visibleClasses,
+                             Map<String,Set<String>> oldPackageDependents,
+                             URI destRoot,
+                             Map<String,Set<URI>>    packageArtifacts,
+                             Map<String,Set<String>> packageDependencies,
+                             Map<String,String>      packagePublicApis,
+                             int debugLevel,
+                             boolean incremental,
+                             int numCores,
+                             PrintStream out,
+                             PrintStream err) {
+        boolean rc = true;
+        for (String pkgName : pkgSrcs.keySet()) {
+            String pkgNameF = Util.toFileSystemPath(pkgName);
+            for (URI u : pkgSrcs.get(pkgName)) {
+                File src = new File(u);
+                boolean r = compile(pkgName, pkgNameF, src, new File(destRoot), debugLevel,
+                                    packageArtifacts);
+                if (r == false) {
+                    rc = false;
+                }
+            }
+        }
+        return rc;
+    }
+
+    boolean compile(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
+                    Map<String,Set<URI>> packageArtifacts)
+    {
+        String superClass = "java.util.ListResourceBundle";
+
+        if (extra != null) {
+            superClass = extra;
+        }
+        // Load the properties file.
+        Properties p = new Properties();
+        try {
+            p.load(new FileInputStream(src));
+        } catch (IOException e) {
+            Log.error("Error reading file "+src.getPath());
+            return false;
+        }
+
+        // Calculate the name of the Java source file to be generated.
+        int dp = src.getName().lastIndexOf(".");
+        String classname = src.getName().substring(0,dp);
+
+        // Sort the properties in increasing key order.
+        List<String> sortedKeys = new ArrayList<String>();
+        for (Object key : p.keySet()) {
+            sortedKeys.add((String)key);
+        }
+        Collections.sort(sortedKeys);
+        Iterator<String> keys = sortedKeys.iterator();
+
+        // Collect the properties into a string buffer.
+        StringBuilder data = new StringBuilder();
+        while (keys.hasNext()) {
+            String key = keys.next();
+            data.append("            { \"" + escape(key) + "\", \"" +
+                        escape((String)p.get(key)) + "\" },\n");
+        }
+
+        // Create dest file name. It is derived from the properties file name.
+        String destFilename = destRoot.getPath()+File.separator+pkgNameF+File.separator+classname+".java";
+        File dest = new File(destFilename);
+
+        // Make sure the dest directories exist.
+        if (!dest.getParentFile().isDirectory()) {
+            if (!dest.getParentFile().mkdirs()) {
+                Log.error("Could not create the directory "+dest.getParentFile().getPath());
+                return false;
+            }
+        }
+
+        Set<URI> as = packageArtifacts.get(pkgName);
+        if (as == null) {
+            as = new HashSet<URI>();
+            packageArtifacts.put(pkgName, as);
+        }
+        as.add(dest.toURI());
+
+        if (dest.exists() && dest.lastModified() > src.lastModified()) {
+            // A generated file exists, and its timestamp is newer than the source.
+            // Assume that we do not need to regenerate the dest file!
+            // Thus we are done.
+            return true;
+        }
+
+        String packageString = "package " + pkgNameF.replace(File.separatorChar,'.') + ";\n\n";
+
+        Log.info("Compiling property file "+pkgNameF+File.separator+src.getName());
+        try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest)))) {
+            MessageFormat format = new MessageFormat(FORMAT);
+            writer.write(format.format(new Object[] { packageString, classname, superClass, data }));
+        } catch ( IOException e ) {
+            Log.error("Could not write file "+dest.getPath());
+            return false;
+        }
+        return true;
+    }
+
+    private static final String FORMAT =
+            "{0}" +
+            "public final class {1} extends {2} '{'\n" +
+            "    protected final Object[][] getContents() '{'\n" +
+            "        return new Object[][] '{'\n" +
+            "{3}" +
+            "        };\n" +
+            "    }\n" +
+            "}\n";
+
+    public static String escape(String theString) {
+        int len = theString.length();
+        StringBuilder outBuffer = new StringBuilder(len*2);
+
+        for(int x=0; x<len; x++) {
+            char aChar = theString.charAt(x);
+            switch(aChar) {
+                case '\\':outBuffer.append('\\'); outBuffer.append('\\');
+                break;
+                case '\t':outBuffer.append('\\'); outBuffer.append('t');
+                break;
+                case '\n':outBuffer.append('\\'); outBuffer.append('n');
+                break;
+                case '\r':outBuffer.append('\\'); outBuffer.append('r');
+                break;
+                case '\f':outBuffer.append('\\'); outBuffer.append('f');
+                break;
+                default:
+                    if ((aChar < 0x0020) || (aChar > 0x007e)) {
+                        outBuffer.append('\\');
+                        outBuffer.append('u');
+                        outBuffer.append(toHex((aChar >> 12) & 0xF));
+                        outBuffer.append(toHex((aChar >>  8) & 0xF));
+                        outBuffer.append(toHex((aChar >>  4) & 0xF));
+                        outBuffer.append(toHex( aChar        & 0xF));
+                    } else {
+                        if (aChar == '"') {
+                            outBuffer.append('\\');
+                        }
+                        outBuffer.append(aChar);
+                    }
+            }
+        }
+        return outBuffer.toString();
+    }
+
+    private static char toHex(int nibble) {
+        return hexDigit[(nibble & 0xF)];
+    }
+
+    private static final char[] hexDigit = {
+        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.*;
+import java.net.URI;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * The copy file transform simply copies a matching file from -src to -d .
+ * Such files are typically images, xml documents and other data files.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CopyFile implements Transformer {
+
+    public void setExtra(String e) {
+    }
+
+    public void setExtra(String[] a) {
+    }
+
+    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+                             Set<URI> visibleSrcs,
+                             Map<URI,Set<String>> visibleClasses,
+                             Map<String,Set<String>> oldPackageDependents,
+                             URI destRoot,
+                             Map<String,Set<URI>>    packageArtifacts,
+                             Map<String,Set<String>> packageDependencies,
+                             Map<String,String>      packagePubapis,
+                             int debugLevel,
+                             boolean incremental,
+                             int numCores,
+                             PrintStream out,
+                             PrintStream err)
+    {
+        boolean rc = true;
+        String dest_filename;
+        File dest;
+
+        for (String pkgName : pkgSrcs.keySet()) {
+            String pkgNameF = Util.toFileSystemPath(pkgName);
+            for (URI u : pkgSrcs.get(pkgName)) {
+                File src = new File(u);
+                File destDir;
+                destDir = new File(destRoot.getPath()+File.separator+pkgNameF);
+                dest_filename = destRoot.getPath()+File.separator+pkgNameF+File.separator+src.getName();
+                dest = new File(dest_filename);
+
+                if (!destDir.isDirectory()) {
+                    if (!destDir.mkdirs()) {
+                       Log.error("Error: The copier could not create the directory "+
+                                           destDir.getPath());
+                        return false;
+                    }
+                }
+
+                Set<URI> as = packageArtifacts.get(pkgName);
+                if (as == null) {
+                    as = new HashSet<URI>();
+                    packageArtifacts.put(pkgName, as);
+                }
+                as.add(dest.toURI());
+
+                if (dest.exists() && dest.lastModified() > src.lastModified()) {
+                    // A copied file exists, and its timestamp is newer than the source.
+                    continue;
+                }
+
+                Log.info("Copying "+pkgNameF+File.separator+src.getName());
+
+                try (InputStream fin = new FileInputStream(src);
+                     OutputStream fout = new FileOutputStream(dest)) {
+                    byte[] buf = new byte[1024];
+                    int len;
+                    while ((len = fin.read(buf)) > 0){
+                        fout.write(buf, 0, len);
+                    }
+                }
+                catch(IOException e){
+                    Log.error("Could not copy the file "+src.getPath()+" to "+dest.getPath());
+                    rc = false;
+                }
+            }
+        }
+        return rc;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/JavacState.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.text.SimpleDateFormat;
+import java.net.URI;
+import java.util.*;
+
+/**
+ * The javac state class maintains the previous (prev) and the current (now)
+ * build states and everything else that goes into the javac_state file.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class JavacState
+{
+    // The arguments to the compile. If not identical, then it cannot
+    // be an incremental build!
+    String theArgs;
+    // The number of cores limits how many threads are used for heavy concurrent work.
+    int numCores;
+
+    // The bin_dir/javac_state
+    private String javacStateFilename;
+    private File javacState;
+
+    // The previous build state is loaded from javac_state
+    private BuildState prev;
+    // The current build state is constructed during the build,
+    // then saved as the new javac_state.
+    private BuildState now;
+
+    // Something has changed in the javac_state. It needs to be saved!
+    private boolean needsSaving;
+    // If this is a new javac_state file, then do not print unnecessary messages.
+    private boolean newJavacState;
+
+    // These are packages where something has changed and the package
+    // needs to be recompiled. Actions that trigger recompilation:
+    // * source belonging to the package has changed
+    // * artifact belonging to the package is lost, or its timestamp has been changed.
+    // * an unknown artifact has appeared, we simply delete it, but we also trigger a recompilation.
+    // * a package that is tainted, taints all packages that depend on it.
+    private Set<String> taintedPackages;
+    // After a compile, the pubapis are compared with the pubapis stored in the javac state file.
+    // Any packages where the pubapi differ are added to this set.
+    // Later we use this set and the dependency information to taint dependent packages.
+    private Set<String> packagesWithChangedPublicApis;
+    // When a module-info.java file is changed, taint the module,
+    // then taint all modules that depend on that that module.
+    // A module dependency can occur directly through a require, or
+    // indirectly through a module that does a public export for the first tainted module.
+    // When all modules are tainted, then taint all packages belonging to these modules.
+    // Then rebuild. It is perhaps possible (and valuable?) to do a more finegrained examination of the
+    // change in module-info.java, but that will have to wait.
+    private Set<String> taintedModules;
+    // The set of all packages that has been recompiled.
+    // Copy over the javac_state for the packages that did not need recompilation,
+    // verbatim from the previous (prev) to the new (now) build state.
+    private Set<String> recompiledPackages;
+
+    // The output directories filled with tasty artifacts.
+    private File binDir, gensrcDir, headerDir;
+
+    // The current status of the file system.
+    private Set<File> binArtifacts;
+    private Set<File> gensrcArtifacts;
+    private Set<File> headerArtifacts;
+
+    // The status of the sources.
+    Set<Source> removedSources = null;
+    Set<Source> addedSources = null;
+    Set<Source> modifiedSources = null;
+
+    // Visible sources for linking. These are the only
+    // ones that -sourcepath is allowed to see.
+    Set<URI> visibleSrcs;
+
+    // Visible classes for linking. These are the only
+    // ones that -classpath is allowed to see.
+    // It maps from a classpath root to the set of visible classes for that root.
+    // If the set is empty, then all classes are visible for that root.
+    // 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();
+    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) {
+        out = o;
+        err = e;
+        numCores = Main.findNumberOption(args, "-j");
+        theArgs = "";
+        for (String a : removeArgsNotAffectingState(args)) {
+            theArgs = theArgs+a+" ";
+        }
+        binDir = bd;
+        gensrcDir = gd;
+        headerDir = hd;
+        javacStateFilename = binDir.getPath()+File.separator+"javac_state";
+        javacState = new File(javacStateFilename);
+        if (removeJavacState && javacState.exists()) {
+            javacState.delete();
+        }
+        newJavacState = false;
+        if (!javacState.exists()) {
+            newJavacState = true;
+            // If there is no javac_state then delete the contents of all the artifact dirs!
+            // 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) {
+                deleteContents(binDir);
+                deleteContents(gensrcDir);
+                deleteContents(headerDir);
+            }
+            needsSaving = true;
+        }
+        prev = new BuildState();
+        now = new BuildState();
+        taintedPackages = new HashSet<String>();
+        recompiledPackages = new HashSet<String>();
+        packagesWithChangedPublicApis = new HashSet<String>();
+    }
+
+    public BuildState prev() { return prev; }
+    public BuildState now() { return now; }
+
+    /**
+     * Remove args not affecting the state.
+     */
+    static String[] removeArgsNotAffectingState(String[] args) {
+        String[] out = new String[args.length];
+        int j = 0;
+        for (int i = 0; i<args.length; ++i) {
+            if (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("--compare-found-sources")) {
+                // Just skip it and skip verify file name
+                i++;
+            } else {
+                // Copy argument.
+                out[j] = args[i];
+                j++;
+            }
+        }
+        String[] ret = new String[j];
+        System.arraycopy(out, 0, ret, 0, j);
+        return ret;
+    }
+
+    /**
+     * Specify which sources are visible to the compiler through -sourcepath.
+     */
+    public void setVisibleSources(Map<String,Source> vs) {
+        visibleSrcs = new HashSet<URI>();
+        for (String s : vs.keySet()) {
+            Source src = vs.get(s);
+            visibleSrcs.add(src.file().toURI());
+        }
+    }
+
+    /**
+     * Specify which classes are visible to the compiler through -classpath.
+     */
+    public void setVisibleClasses(Map<String,Source> vs) {
+        visibleSrcs = new HashSet<URI>();
+        for (String s : vs.keySet()) {
+            Source src = vs.get(s);
+            visibleSrcs.add(src.file().toURI());
+        }
+    }
+    /**
+     * Returns true if this is an incremental build.
+     */
+    public boolean isIncremental() {
+        return !prev.sources().isEmpty();
+    }
+
+    /**
+     * Find all artifacts that exists on disk.
+     */
+    public void findAllArtifacts() {
+        binArtifacts = findAllFiles(binDir);
+        gensrcArtifacts = findAllFiles(gensrcDir);
+        headerArtifacts = findAllFiles(headerDir);
+    }
+
+    /**
+     * Lookup the artifacts generated for this package in the previous build.
+     */
+    private Map<String,File> fetchPrevArtifacts(String pkg) {
+        Package p = prev.packages().get(pkg);
+        if (p != null) {
+            return p.artifacts();
+        }
+        return new HashMap<String,File>();
+    }
+
+    /**
+     * Delete all prev artifacts in the currently tainted packages.
+     */
+    public void deleteClassArtifactsInTaintedPackages() {
+        for (String pkg : taintedPackages) {
+            Map<String,File> arts = fetchPrevArtifacts(pkg);
+            for (File f : arts.values()) {
+                if (f.exists() && f.getName().endsWith(".class")) {
+                    f.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * Mark the javac_state file to be in need of saving and as a side effect,
+     * it gets a new timestamp.
+     */
+    private void needsSaving() {
+        needsSaving = true;
+    }
+
+    /**
+     * Save the javac_state file.
+     */
+    public void save() throws IOException {
+        if (!needsSaving) return;
+        try (FileWriter out = new FileWriter(javacStateFilename)) {
+            StringBuilder b = new StringBuilder();
+            long millisNow = System.currentTimeMillis();
+            Date d = new Date(millisNow);
+            SimpleDateFormat df =
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
+            b.append("# javac_state ver 0.3 generated "+millisNow+" "+df.format(d)+"\n");
+            b.append("# This format might change at any time. Please do not depend on it.\n");
+            b.append("# M module\n");
+            b.append("# P package\n");
+            b.append("# S C source_tobe_compiled timestamp\n");
+            b.append("# S L link_only_source timestamp\n");
+            b.append("# G C generated_source timestamp\n");
+            b.append("# A artifact timestamp\n");
+            b.append("# D dependency\n");
+            b.append("# I pubapi\n");
+            b.append("# R arguments\n");
+            b.append("R ").append(theArgs).append("\n");
+
+            // Copy over the javac_state for the packages that did not need recompilation.
+            now.copyPackagesExcept(prev, recompiledPackages, new HashSet<String>());
+            // Save the packages, ie package names, dependencies, pubapis and artifacts!
+            // I.e. the lot.
+            Module.saveModules(now.modules(), b);
+
+            String s = b.toString();
+            out.write(s, 0, s.length());
+        }
+    }
+
+    /**
+     * 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);
+        Module  lastModule = null;
+        Package lastPackage = null;
+        Source  lastSource = null;
+        boolean noFileFound = false;
+        boolean foundCorrectVerNr = false;
+        boolean newCommandLine = false;
+        boolean syntaxError = false;
+
+        try (BufferedReader in = new BufferedReader(new FileReader(db.javacStateFilename))) {
+            for (;;) {
+                String l = in.readLine();
+                if (l==null) break;
+                if (l.length()>=3 && l.charAt(1) == ' ') {
+                    char c = l.charAt(0);
+                    if (c == 'M') {
+                        lastModule = db.prev.loadModule(l);
+                    } else
+                    if (c == 'P') {
+                        if (lastModule == null) { syntaxError = true; break; }
+                        lastPackage = db.prev.loadPackage(lastModule, l);
+                    } else
+                    if (c == 'D') {
+                        if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
+                        lastPackage.loadDependency(l);
+                    } else
+                    if (c == 'I') {
+                        if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
+                        lastPackage.loadPubapi(l);
+                    } else
+                    if (c == 'A') {
+                        if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
+                        lastPackage.loadArtifact(l);
+                    } else
+                    if (c == 'S') {
+                        if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
+                        lastSource = db.prev.loadSource(lastPackage, l, false);
+                    } else
+                    if (c == 'G') {
+                        if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
+                        lastSource = db.prev.loadSource(lastPackage, l, true);
+                    } else
+                    if (c == 'R') {
+                        String ncmdl = "R "+db.theArgs;
+                        if (!l.equals(ncmdl)) {
+                            newCommandLine = true;
+                        }
+                    } else
+                         if (c == '#') {
+                        if (l.startsWith("# javac_state ver ")) {
+                            int sp = l.indexOf(" ", 18);
+                            if (sp != -1) {
+                                String ver = l.substring(18,sp);
+                                if (!ver.equals("0.3")) {
+                    break;
+                                 }
+                foundCorrectVerNr = true;
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Silently create a new javac_state file.
+            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);
+            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);
+        } 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);
+        } 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.prev.calculateDependents();
+        return db;
+    }
+
+    /**
+     * Mark a java package as tainted, ie it needs recompilation.
+     */
+    public void taintPackage(String name, String because) {
+        if (!taintedPackages.contains(name)) {
+            if (because != null) Log.debug("Tainting "+Util.justPackageName(name)+" because "+because);
+            // It has not been tainted before.
+            taintedPackages.add(name);
+            needsSaving();
+            Package nowp = now.packages().get(name);
+            if (nowp != null) {
+                for (String d : nowp.dependents()) {
+                    taintPackage(d, because);
+                }
+            }
+        }
+    }
+
+    /**
+     * This packages need recompilation.
+     */
+    public Set<String> taintedPackages() {
+        return taintedPackages;
+    }
+
+    /**
+     * Clean out the tainted package set, used after the first round of compiles,
+     * prior to propagating dependencies.
+     */
+    public void clearTaintedPackages() {
+        taintedPackages = new HashSet<String>();
+    }
+
+    /**
+     * Go through all sources and check which have been removed, added or modified
+     * and taint the corresponding packages.
+     */
+    public void checkSourceStatus(boolean check_gensrc) {
+        removedSources = calculateRemovedSources();
+        for (Source s : removedSources) {
+            if (!s.isGenerated() || check_gensrc) {
+                taintPackage(s.pkg().name(), "source "+s.name()+" was removed");
+            }
+        }
+
+        addedSources = calculateAddedSources();
+        for (Source s : addedSources) {
+            String msg = null;
+            if (isIncremental()) {
+                // When building from scratch, there is no point
+                // printing "was added" for every file since all files are added.
+                // However for an incremental build it makes sense.
+                msg = "source "+s.name()+" was added";
+            }
+            if (!s.isGenerated() || check_gensrc) {
+                taintPackage(s.pkg().name(), msg);
+            }
+        }
+
+        modifiedSources = calculateModifiedSources();
+        for (Source s : modifiedSources) {
+            if (!s.isGenerated() || check_gensrc) {
+                taintPackage(s.pkg().name(), "source "+s.name()+" was modified");
+            }
+        }
+    }
+
+    /**
+     * Acquire the compile_java_packages suffix rule for .java files.
+     */
+    public Map<String,Transformer> getJavaSuffixRule() {
+        Map<String,Transformer> sr = new HashMap<String,Transformer>();
+        sr.put(".java", compileJavaPackages);
+        return sr;
+    }
+
+    /**
+     * Acquire the copying transform.
+     */
+    public Transformer getCopier() {
+        return copyFiles;
+    }
+
+    /**
+     * If artifacts have gone missing, force a recompile of the packages
+     * they belong to.
+     */
+    public void taintPackagesThatMissArtifacts() {
+        for (Package pkg : prev.packages().values()) {
+            for (File f : pkg.artifacts().values()) {
+                if (!f.exists()) {
+                    // Hmm, the artifact on disk does not exist! Someone has removed it....
+                    // Lets rebuild the package.
+                    taintPackage(pkg.name(), ""+f+" is missing.");
+                }
+            }
+        }
+    }
+
+    /**
+     * Propagate recompilation through the dependency chains.
+     * Avoid re-tainting packages that have already been compiled.
+     */
+    public void taintPackagesDependingOnChangedPackages(Set<String> pkgs, Set<String> recentlyCompiled) {
+        for (Package pkg : prev.packages().values()) {
+            for (String dep : pkg.dependencies()) {
+                if (pkgs.contains(dep) && !recentlyCompiled.contains(pkg.name())) {
+                    taintPackage(pkg.name(), " its depending on "+dep);
+                }
+            }
+        }
+    }
+
+    /**
+     * Scan all output dirs for artifacts and remove those files (artifacts?)
+     * that are not recognized as such, in the javac_state file.
+     */
+    public void removeUnidentifiedArtifacts() {
+        Set<File> allKnownArtifacts = new HashSet<File>();
+        for (Package pkg : prev.packages().values()) {
+            for (File f : pkg.artifacts().values()) {
+                allKnownArtifacts.add(f);
+            }
+        }
+        // Do not forget about javac_state....
+        allKnownArtifacts.add(javacState);
+
+        for (File f : binArtifacts) {
+            if (!allKnownArtifacts.contains(f)) {
+                Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
+                f.delete();
+            }
+        }
+        for (File f : headerArtifacts) {
+            if (!allKnownArtifacts.contains(f)) {
+                Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
+                f.delete();
+            }
+        }
+        for (File f : gensrcArtifacts) {
+            if (!allKnownArtifacts.contains(f)) {
+                Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
+                f.delete();
+            }
+        }
+    }
+
+    /**
+     * Remove artifacts that are no longer produced when compiling!
+     */
+    public void removeSuperfluousArtifacts(Set<String> recentlyCompiled) {
+        // Nothing to do, if nothing was recompiled.
+        if (recentlyCompiled.size() == 0) return;
+
+        for (String pkg : now.packages().keySet()) {
+            // If this package has not been recompiled, skip the check.
+            if (!recentlyCompiled.contains(pkg)) continue;
+            Collection<File> arts = now.artifacts().values();
+            for (File f : fetchPrevArtifacts(pkg).values()) {
+                if (!arts.contains(f)) {
+                    Log.debug("Removing "+f.getPath()+" since it is now superfluous!");
+                    if (f.exists()) f.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * Return those files belonging to prev, but not now.
+     */
+    private Set<Source> calculateRemovedSources() {
+        Set<Source> removed = new HashSet<Source>();
+        for (String src : prev.sources().keySet()) {
+            if (now.sources().get(src) == null) {
+                removed.add(prev.sources().get(src));
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Return those files belonging to now, but not prev.
+     */
+    private Set<Source> calculateAddedSources() {
+        Set<Source> added = new HashSet<Source>();
+        for (String src : now.sources().keySet()) {
+            if (prev.sources().get(src) == null) {
+                added.add(now.sources().get(src));
+            }
+        }
+        return added;
+    }
+
+    /**
+     * Return those files where the timestamp is newer.
+     * If a source file timestamp suddenly is older than what is known
+     * about it in javac_state, then consider it modified, but print
+     * a warning!
+     */
+    private Set<Source> calculateModifiedSources() {
+        Set<Source> modified = new HashSet<Source>();
+        for (String src : now.sources().keySet()) {
+            Source n = now.sources().get(src);
+            Source t = prev.sources().get(src);
+            if (prev.sources().get(src) != null) {
+                if (t != null) {
+                    if (n.lastModified() > t.lastModified()) {
+                        modified.add(n);
+                    } else if (n.lastModified() < t.lastModified()) {
+                        modified.add(n);
+                        Log.warn("The source file "+n.name()+" timestamp has moved backwards in time.");
+                    }
+                }
+            }
+        }
+        return modified;
+    }
+
+    /**
+     * Recursively delete a directory and all its contents.
+     */
+    private static void deleteContents(File dir) {
+        if (dir != null && dir.exists()) {
+            for (File f : dir.listFiles()) {
+                if (f.isDirectory()) {
+                    deleteContents(f);
+                }
+                f.delete();
+            }
+        }
+    }
+
+    /**
+     * Run the copy translator only.
+     */
+    public void performCopying(File binDir, Map<String,Transformer> suffixRules) {
+        Map<String,Transformer> sr = new HashMap<String,Transformer>();
+        for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
+            if (e.getValue() == copyFiles) {
+                sr.put(e.getKey(), e.getValue());
+            }
+        }
+        perform(binDir, sr);
+    }
+
+    /**
+     * Run all the translators that translate into java source code.
+     * I.e. all translators that are not copy nor compile_java_source.
+     */
+    public void performTranslation(File gensrcDir, Map<String,Transformer> suffixRules) {
+        Map<String,Transformer> sr = new HashMap<String,Transformer>();
+        for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
+            if (e.getValue() != copyFiles &&
+                e.getValue() != compileJavaPackages) {
+                sr.put(e.getKey(), e.getValue());
+            }
+        }
+        perform(gensrcDir, sr);
+    }
+
+    /**
+     * Compile all the java sources. Return true, if it needs to be called again!
+     */
+    public boolean performJavaCompilations(File binDir,
+                                           String serverSettings,
+                                           String[] args,
+                                           Set<String> recentlyCompiled,
+                                           boolean[] rcValue) {
+        Map<String,Transformer> suffixRules = new HashMap<String,Transformer>();
+        suffixRules.put(".java", compileJavaPackages);
+        compileJavaPackages.setExtra(serverSettings);
+        compileJavaPackages.setExtra(args);
+
+        rcValue[0] = perform(binDir, suffixRules);
+        recentlyCompiled.addAll(taintedPackages());
+        clearTaintedPackages();
+        boolean again = !packagesWithChangedPublicApis.isEmpty();
+        taintPackagesDependingOnChangedPackages(packagesWithChangedPublicApis, recentlyCompiled);
+        packagesWithChangedPublicApis = new HashSet<String>();
+        return again && rcValue[0];
+    }
+
+    /**
+     * Store the source into the set of sources belonging to the given transform.
+     */
+    private void addFileToTransform(Map<Transformer,Map<String,Set<URI>>> gs, Transformer t, Source s) {
+        Map<String,Set<URI>> fs = gs.get(t);
+        if (fs == null) {
+            fs = new HashMap<String,Set<URI>>();
+            gs.put(t, fs);
+        }
+        Set<URI> ss = fs.get(s.pkg().name());
+        if (ss == null) {
+            ss = new HashSet<URI>();
+            fs.put(s.pkg().name(), ss);
+        }
+        ss.add(s.file().toURI());
+    }
+
+    /**
+     * For all packages, find all sources belonging to the package, group the sources
+     * based on their transformers and apply the transformers on each source code group.
+     */
+    private boolean perform(File outputDir, Map<String,Transformer> suffixRules)
+    {
+        boolean rc = true;
+        // Group sources based on transforms. A source file can only belong to a single transform.
+        Map<Transformer,Map<String,Set<URI>>> groupedSources = new HashMap<Transformer,Map<String,Set<URI>>>();
+        for (Source src : now.sources().values()) {
+            Transformer t = suffixRules.get(src.suffix());
+               if (t != null) {
+                if (taintedPackages.contains(src.pkg().name()) && !src.isLinkedOnly()) {
+                    addFileToTransform(groupedSources, t, src);
+                }
+            }
+        }
+        // Go through the transforms and transform them.
+        for (Map.Entry<Transformer,Map<String,Set<URI>>> e : groupedSources.entrySet()) {
+            Transformer t = e.getKey();
+            Map<String,Set<URI>> srcs = e.getValue();
+            // These maps need to be synchronized since multiple threads will be writing results into them.
+            Map<String,Set<URI>> packageArtifacts = Collections.synchronizedMap(new HashMap<String,Set<URI>>());
+            Map<String,Set<String>> packageDependencies = Collections.synchronizedMap(new HashMap<String,Set<String>>());
+            Map<String,String> packagePublicApis = Collections.synchronizedMap(new HashMap<String,String>());
+
+            boolean  r = t.transform(srcs,
+                                     visibleSrcs,
+                                     visibleClasses,
+                                     prev.dependents(),
+                                     outputDir.toURI(),
+                                     packageArtifacts,
+                                     packageDependencies,
+                                     packagePublicApis,
+                                     0,
+                                     isIncremental(),
+                                     numCores,
+                                     out,
+                                     err);
+            if (!r) rc = false;
+
+            for (String p : srcs.keySet()) {
+                recompiledPackages.add(p);
+            }
+            // The transform is done! Extract all the artifacts and store the info into the Package objects.
+            for (Map.Entry<String,Set<URI>> a : packageArtifacts.entrySet()) {
+                Module mnow = now.findModuleFromPackageName(a.getKey());
+                mnow.addArtifacts(a.getKey(), a.getValue());
+            }
+            // Extract all the dependencies and store the info into the Package objects.
+            for (Map.Entry<String,Set<String>> a : packageDependencies.entrySet()) {
+                Set<String> deps = a.getValue();
+                Module mnow = now.findModuleFromPackageName(a.getKey());
+                mnow.setDependencies(a.getKey(), deps);
+            }
+            // Extract all the pubapis and store the info into the Package objects.
+            for (Map.Entry<String,String> a : packagePublicApis.entrySet()) {
+                Module mprev = prev.findModuleFromPackageName(a.getKey());
+                List<String> pubapi = Package.pubapiToList(a.getValue());
+                Module mnow = now.findModuleFromPackageName(a.getKey());
+                mnow.setPubapi(a.getKey(), pubapi);
+                if (mprev.hasPubapiChanged(a.getKey(), pubapi)) {
+                    // Aha! The pubapi of this package has changed!
+                    // It can also be a new compile from scratch.
+                    if (mprev.lookupPackage(a.getKey()).existsInJavacState()) {
+                        // This is an incremental compile! The pubapi
+                        // did change. Trigger recompilation of dependents.
+                        packagesWithChangedPublicApis.add(a.getKey());
+                        Log.info("The pubapi of "+Util.justPackageName(a.getKey())+" has changed!");
+                    }
+                }
+            }
+        }
+        return rc;
+    }
+
+    /**
+     * Utility method to recursively find all files below a directory.
+     */
+    private static Set<File> findAllFiles(File dir) {
+        Set<File> foundFiles = new HashSet<File>();
+        if (dir == null) {
+            return foundFiles;
+        }
+        recurse(dir, foundFiles);
+        return foundFiles;
+    }
+
+    private static void recurse(File dir, Set<File> foundFiles) {
+        for (File f : dir.listFiles()) {
+            if (f.isFile()) {
+                foundFiles.add(f);
+            } else if (f.isDirectory()) {
+                recurse(f, foundFiles);
+            }
+        }
+    }
+
+    /**
+     * Compare the calculate source list, with an explicit list, usually supplied from the makefile.
+     * Used to detect bugs where the makefile and sjavac have different opinions on which files
+     * should be compiled.
+     */
+    public void compareWithMakefileList(File makefileSourceList)
+            throws ProblemException
+    {
+        // If we are building on win32 using for example cygwin the paths in the makefile source list
+        // might be /cygdrive/c/.... which does not match c:\....
+        // We need to adjust our calculated sources to be identical, if necessary.
+        boolean mightNeedRewriting = File.pathSeparatorChar == ';';
+
+        if (makefileSourceList == null) return;
+
+        Set<String> calculatedSources = new HashSet<String>();
+        Set<String> listedSources = new HashSet<String>();
+
+        // Create a set of filenames with full paths.
+        for (Source s : now.sources().values()) {
+            calculatedSources.add(s.file().getPath());
+        }
+        // Read in the file and create another set of filenames with full paths.
+        try {
+            BufferedReader in = new BufferedReader(new FileReader(makefileSourceList));
+            for (;;) {
+                String l = in.readLine();
+                if (l==null) break;
+                l = l.trim();
+                if (mightNeedRewriting) {
+                    if (l.indexOf(":") == 1 && l.indexOf("\\") == 2) {
+                        // Everything a-ok, the format is already C:\foo\bar
+                    } else if (l.indexOf(":") == 1 && l.indexOf("/") == 2) {
+                        // The format is C:/foo/bar, rewrite into the above format.
+                        l = l.replaceAll("/","\\\\");
+                    } else if (l.charAt(0) == '/' && l.indexOf("/",1) != -1) {
+                        // The format might be: /cygdrive/c/foo/bar, rewrite into the above format.
+                        // Do not hardcode the name cygdrive here.
+                        int slash = l.indexOf("/",1);
+                        l = l.replaceAll("/","\\\\");
+                        l = ""+l.charAt(slash+1)+":"+l.substring(slash+2);
+                    }
+                    if (Character.isLowerCase(l.charAt(0))) {
+                        l = Character.toUpperCase(l.charAt(0))+l.substring(1);
+                    }
+                }
+                listedSources.add(l);
+            }
+        } catch (FileNotFoundException e) {
+            throw new ProblemException("Could not open "+makefileSourceList.getPath()+" since it does not exist!");
+        } catch (IOException e) {
+            throw new ProblemException("Could not read "+makefileSourceList.getPath());
+        }
+
+        for (String s : listedSources) {
+            if (!calculatedSources.contains(s)) {
+                 throw new ProblemException("The makefile listed source "+s+" was not calculated by the smart javac wrapper!");
+            }
+        }
+
+        for (String s : calculatedSources) {
+            if (!listedSources.contains(s)) {
+                throw new ProblemException("The smart javac wrapper calculated source "+s+" was not listed by the makefiles!");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Log.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.PrintStream;
+
+/**
+ * Utility class only for sjavac logging.
+ * The log level can be set using for example --log=DEBUG on the sjavac command line.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Log {
+    private static PrintStream out, err;
+
+    public final static int WARN = 1;
+    public final static int INFO = 2;
+    public final static int DEBUG = 3;
+    public final static int TRACE = 4;
+    private static int level = WARN;
+
+    static public void trace(String msg) {
+        if (level >= TRACE) {
+            out.println(msg);
+        }
+    }
+
+    static public void debug(String msg) {
+        if (level >= DEBUG) {
+            out.println(msg);
+        }
+    }
+
+    static public void info(String msg) {
+        if (level >= INFO) {
+            out.println(msg);
+        }
+    }
+
+    static public void warn(String msg) {
+        err.println(msg);
+    }
+
+    static public void error(String msg) {
+        err.println(msg);
+    }
+
+    static public void setLogLevel(String l, PrintStream o, PrintStream e)
+        throws ProblemException {
+        out = o;
+        err = e;
+        if (l.equals("warn")) level = WARN;
+        else if (l.equals("info")) level = INFO;
+        else if (l.equals("debug")) level = DEBUG;
+        else if (l.equals("trace")) level = TRACE;
+        else throw new ProblemException("No such log level \""+l+"\"");
+    }
+
+    static public boolean isTracing() {
+        return level >= TRACE;
+    }
+
+    static public boolean isDebugging() {
+        return level >= DEBUG;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Main.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,969 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import com.sun.tools.sjavac.server.JavacServer;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.*;
+
+/**
+ * The main class of the smart javac wrapper tool.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Main {
+
+    /*  This is a smart javac wrapper primarily used when building the OpenJDK,
+        though other projects are welcome to use it too. But please be aware
+        that it is not an official api and will change in the future.
+        (We really mean it!)
+
+        Goals:
+
+        ** Create a state file, containing information about the build, so
+           that incremental builds only rebuild what is necessary. Also the
+           state file can be used by make/ant to detect when to trigger
+           a call to the smart javac wrapper.
+
+           This file is called bin/javac_state (assuming that you specified "-d bin")
+           Thus the simplest makefile is:
+
+           SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main
+           SRCS=$(shell find src -name "*.java")
+           bin/javac_state : $(SRCS)
+                  $(SJAVAC) src -d bin
+
+           This makefile will run very fast and detect properly when Java code needs to
+           be recompiled. The smart javac wrapper will then use the information in java_state
+           to do an efficient incremental compile.
+
+           Previously it was near enough impossible to write an efficient makefile for Java
+           with support for incremental builds and dependency tracking.
+
+        ** Separate java sources to be compiled from java
+           sources used >only< for linking. The options:
+
+           "dir" points to root dir with sources to be compiled
+           "-sourcepath dir" points to root dir with sources used only for linking
+           "-classpath dir" points to dir with classes used only for linking (as before)
+
+        ** Use all cores for compilation by default.
+           "-j 4" limit the number of cores to 4.
+           For the moment, the sjavac server additionally limits the number of cores to three.
+           This will improve in the future when more sharing is performed between concurrent JavaCompilers.
+
+        ** Basic translation support from other sources to java, and then compilation of the generated java.
+           This functionality might be moved into annotation processors instead.
+           Again this is driven by the OpenJDK sources where properties and a few other types of files
+           are converted into Java sources regularily. The javac_state embraces copy and tr, and perform
+           incremental recompiles and copying for these as well. META-INF will be a special copy rule
+           that will copy any files found below any META-INF dir in src to the bin/META-INF dir.
+           "-copy .gif"
+           "-copy META-INF"
+           "-tr .prop=com.sun.tools.javac.smart.CompileProperties
+           "-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle
+           "-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
+
+        ** Control which classes in the src,sourcepath and classpath that javac is allowed to see.
+           Again, this is necessary to deal with the source code structure of the OpenJDK which is
+           intricate (read messy).
+
+           "-i tools.*" to include the tools package and all its subpackages in the build.
+           "-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages.
+           "-x tools.net.drums" to exclude the drums package only, keep its subpackages.
+           "-xf tools/net/Bar.java" // Do not compile this file...
+           "-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java!
+           "-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used.
+
+        ** The smart javac wrapper is driven by the modification time on the source files and compared
+           to the modification times written into the javac_state file.
+
+           It does not compare the modification time of the source with the modification time of the artifact.
+           However it will detect if the modification time of an artifact has changed compared to the java_state,
+           and this will trigger a delete of the artifact and a subsequent recompile of the source.
+
+           The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source
+           as the final step before the output dir is finalized and immediately jared, or jmodded. The output
+           dir should be considered opaque. Do not write into the outputdir yourself!
+           Any artifacts found in the outputdir that javac_state does not know of, will be deleted!
+           This can however be prevented, using the switch --permit-unidentified-artifacts
+           This switch is necessary when build the OpenJDK because its makefiles still write directly to
+           the output classes dirs.
+
+           Any makefile/ant rules that want to put contents into the outputdir should put the content
+           in one of several source roots. Static content that is under version control, can be put in the same source
+           code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should
+           be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments:
+           "gensrc_stuff src -d bin"
+
+        The command line:
+        java -cp tools.jar com.sun.tools.sjavac.Main \
+             -i "com.bar.*" -x "com.bar.foo.*" \
+             first_root \
+             -i "com.bar.foo.*" \
+             second_root \
+             -x "org.net.*" \
+             -sourcepath link_root_sources \
+             -classpath link_root_classes \
+             -d bin
+
+        Will compile all sources for package com.bar and its subpackages, found below first_root,
+        except the package com.bar.foo (and its subpackages), for which the sources are picked
+        from second_root instead. It will link against classes in link_root_classes and against
+        sources in link_root_sources, but will not see (try to link against) sources matching org.net.*
+        but will link against org.net* classes (if they exist) in link_root_classes.
+
+        (If you want a set of complex filter rules to be applied to several source directories, without
+         having to repeat the the filter rules for each root. You can use the explicit -src option. For example:
+         sjavac -x "com.foo.*" -src root1:root2:root3  )
+
+        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) {
+                Log.error("When spawning a background server, only a single --startserver argument is allowed.");
+                return;
+            }
+            // Spawn a background server.
+            int rc = JavacServer.startServer(args[0], System.err);
+            System.exit(rc);
+        }
+        Main main = new Main();
+        int rc = main.go(args, System.out, System.err);
+        // Remove the portfile, but only if this background=false was used.
+        JavacServer.cleanup(args);
+        System.exit(rc);
+    }
+
+    private void printHelp() {
+        System.out.println("Usage: sjavac <options>\n"+
+                           "where required options are:\n"+
+                           "dir                        Compile all sources in dir recursively\n"+
+                           "-d dir                     Store generated classes here and the javac_state file\n"+
+                           "--server:portfile=/tmp/abc Use a background sjavac server\n\n"+
+                           "All other arguments as javac, except -implicit:none which is forced by default.\n"+
+                           "No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+
+                           "Warning!\n"+
+                           "This tool might disappear at any time, and its command line options might change at any time!");
+    }
+
+    public int go(String[] args, PrintStream out, PrintStream err) {
+        try {
+            if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
+                printHelp();
+                return 0;
+            }
+
+            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);
+
+            // Load the prev build state database.
+            javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
+                    findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
+
+            // 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);
+
+            // All found modules are put here.
+            Map<String,Module> modules = new HashMap<String,Module>();
+            // 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);
+
+            // Find all sources, use the suffix rules to know which files are sources.
+            Map<String,Source> sources = new HashMap<String,Source>();
+            // 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);
+
+            if (sources.isEmpty()) {
+                Log.error("Found nothing to compile!");
+                return -1;
+            }
+
+            // Find all source files allowable for linking.
+            // We might find more modules here as well.
+            Map<String,Source> sources_to_link_to = new HashMap<String,Source>();
+            // Always reuse -src for linking as well! This means that we might
+            // get two -sourcepath on the commandline after the rewrite, which is
+            // fine. We can have as many as we like. You need to have separate -src/-sourcepath/-classpath
+            // if you need different filtering rules for different roots. If you have the same filtering
+            // rules for all sourcepath roots, you can concatenate them using :(;) as before.
+              rewriteOptions(args, "-src", "-sourcepath");
+            findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, 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().collectPackagesSourcesAndArtifacts(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 (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
+                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(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<String,Source>();
+            Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
+                   generated_sources, modules, current_module, false, true, false);
+            javac_state.now().collectPackagesSourcesAndArtifacts(modules);
+            // Recheck the the source files and their timestamps again.
+            javac_state.checkSourceStatus(true);
+
+            // Now do a safety check that the list of source files is identical
+            // to the list Make believes we are compiling. If we do not get this
+            // 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);
+
+            // Do the compilations, repeatedly until no tainted packages exist.
+            boolean again;
+            // Collect the name of all compiled packages.
+            Set<String> recently_compiled = new HashSet<String>();
+            boolean[] rc = new boolean[1];
+            do {
+                // Clean out artifacts in tainted packages.
+                javac_state.deleteClassArtifactsInTaintedPackages();
+                again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
+                if (!rc[0]) break;
+            } while (again);
+            // Only update the state if the compile went well.
+            if (rc[0]) {
+                javac_state.save();
+                // Collect all the artifacts.
+                javac_state.now().collectArtifacts(modules);
+                // Remove artifacts that were generated during the last compile, but not this one.
+                javac_state.removeSuperfluousArtifacts(recently_compiled);
+            }
+            return rc[0] ? 0 : -1;
+        } catch (ProblemException e) {
+            Log.error(e.getMessage());
+            return -1;
+        } catch (Exception e) {
+            e.printStackTrace(err);
+            return -1;
+        }
+    }
+
+    /**
+     * 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;
+        }
+        return false;
+    }
+
+    /**
+     * Is an at file passed on the command line?
+     */
+    private boolean findAtFile(String[] args) {
+        for (String s : args) {
+            if (s.startsWith("@")) {
+                return true;
+            }
+        }
+        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";
+            }
+        }
+        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")) {
+                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.");
+        }
+    }
+
+    /**
+     * 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("-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<String>();
+        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<File>();
+        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 (int i = 0; i<args.length; ++i) {
+            if (args[i].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<File>();
+        // 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<String>();
+        List<String> excludes = new LinkedList<String>();
+        List<String> excludefiles = new LinkedList<String>();
+        List<String> includefiles = new LinkedList<String>();
+        // This include is used to find all modules in the source.
+        List<String> moduleinfo = new LinkedList<String>();
+        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 (roots.equals(bin_dir)) {
+                        throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
+                    }
+                    if (roots.equals(gensrc_dir)) {
+                        throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
+                    }
+                    if (roots.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"))
+            {
+                // Reset the includes,excludes and excludefiles after they have been used.
+                includes = new LinkedList<String>();
+                excludes = new LinkedList<String>();
+                excludefiles = new LinkedList<String>();
+                includefiles = new LinkedList<String>();
+            }
+        }
+        return true;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Module.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The module is the root of a set of packages/sources/artifacts.
+ * At the moment there is only one module in use, the empty/no-name/default module.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Module implements Comparable<Module> {
+    private String name;
+    private String dirname;
+    private Map<String,Package> packages = new HashMap<String,Package>();
+    private Map<String,Source> sources = new HashMap<String,Source>();
+    private Map<String,File> artifacts = new HashMap<String,File>();
+
+    public Module(String n, String dn) {
+        name = n;
+        dirname = n;
+    }
+
+    public String name() { return name; }
+    public String dirname() { return dirname; }
+    public Map<String,Package> packages() { return packages; }
+    public Map<String,Source> sources() { return sources; }
+    public Map<String,File> artifacts() { return artifacts; }
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof Module) && name.equals(((Module)o).name);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public int compareTo(Module o) {
+        return name.compareTo(o.name);
+    }
+
+    public void save(StringBuilder b) {
+        b.append("M ").append(name).append(":").append("\n");
+        Package.savePackages(packages, b);
+    }
+
+    public static Module load(String l) {
+        int cp = l.indexOf(':',2);
+        if (cp == -1) return null;
+        String name = l.substring(2,cp);
+        return new Module(name, "");
+    }
+
+    public static void saveModules(Map<String,Module> ms, StringBuilder b)
+    {
+        for (Module m : ms.values()) {
+            m.save(b);
+        }
+    }
+
+    public void addPackage(Package p) {
+        packages.put(p.name(), p);
+    }
+
+    public Package lookupPackage(String pkg) {
+        Package p = packages.get(pkg);
+        if (p == null) {
+            p = new Package(this, pkg);
+            packages.put(pkg, p);
+        }
+        return p;
+    }
+
+    public void addSource(String pkg, Source src) {
+        Package p = lookupPackage(pkg);
+        src.setPackage(p);
+        p.addSource(src);
+        sources.put(src.file().getPath(), src);
+    }
+
+    public Source lookupSource(String path) {
+        return sources.get(path);
+    }
+
+    public void addArtifacts(String pkg, Set<URI> as) {
+        Package p = lookupPackage(pkg);
+        for (URI u : as) {
+            p.addArtifact(new File(u));
+        }
+    }
+
+    public void setDependencies(String pkg, Set<String> deps) {
+        Package p = lookupPackage(pkg);
+        p.setDependencies(deps);
+    }
+
+    public void setPubapi(String pkg, List<String> ps) {
+        Package p = lookupPackage(pkg);
+        p.setPubapi(ps);
+    }
+
+    public boolean hasPubapiChanged(String pkg, List<String> ps) {
+        Package p = lookupPackage(pkg);
+        return p.hasPubapiChanged(ps);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Package.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The Package class maintains meta information about a package.
+ * For example its sources, dependents,its pubapi and its artifacts.
+ *
+ * It might look odd that we track dependents/pubapi/artifacts on
+ * a package level, but it makes sense since recompiling a full package
+ * takes as long as recompiling a single java file in that package,
+ * if you take into account the startup time of the jvm.
+ *
+ * Also the dependency information will be much smaller (good for the javac_state file size)
+ * and it simplifies tracking artifact generation, you do not always know from which
+ * source a class file was generated, but you always know which package it belongs to.
+ *
+ * It is also educational to see package dependencies triggering recompilation of
+ * other packages. Even though the recompilation was perhaps not necessary,
+ * the visible recompilation of the dependent packages indicates how much circular
+ * dependencies your code has.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Package implements Comparable<Package> {
+    // The module this package belongs to. (There is a legacy module with an empty string name,
+    // used for all legacy sources.)
+    private Module mod;
+    // Name of this package, module:pkg
+    // ex1 jdk.base:java.lang
+    // ex2 :java.lang (when in legacy mode)
+    private String name;
+    // The directory path to the package. If the package belongs to a module,
+    // then that module's file system name is part of the path.
+    private String dirname;
+    // This package depends on these packages.
+    private Set<String> dependencies = new HashSet<String>();
+    // This package has the following dependents, that depend on this package.
+    private Set<String> dependents = new HashSet<String>();
+    // This is the public api of this package.
+    private List<String> pubapi = new ArrayList<String>();
+    // Map from source file name to Source info object.
+    private Map<String,Source> sources = new HashMap<String,Source>();
+    // This package generated these artifacts.
+    private Map<String,File> artifacts = new HashMap<String,File>();
+
+    public Package(Module m, String n) {
+        int c = n.indexOf(":");
+        assert(c != -1);
+        String mn = n.substring(0,c);
+        assert(m.name().equals(m.name()));
+        name = n;
+        dirname = n.replace('.', File.separatorChar);
+        if (m.name().length() > 0) {
+            // There is a module here, prefix the module dir name to the path.
+            dirname = m.dirname()+File.separatorChar+dirname;
+        }
+    }
+
+    public Module mod() { return mod; }
+    public String name() { return name; }
+    public String dirname() { return dirname; }
+    public Map<String,Source> sources() { return sources; }
+    public Map<String,File> artifacts() { return artifacts; }
+    public List<String> pubapi() { return pubapi; }
+
+    public Set<String> dependencies() { return dependencies; }
+    public Set<String> dependents() { return dependents; }
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof Package) && name.equals(((Package)o).name);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public int compareTo(Package o) {
+        return name.compareTo(o.name);
+    }
+
+    public void addSource(Source s) {
+        sources.put(s.file().getPath(), s);
+    }
+
+    public void addDependency(String d) {
+        dependencies.add(d);
+    }
+
+    public void addDependent(String d) {
+        dependents.add(d);
+    }
+
+    public void addPubapi(String p) {
+        pubapi.add(p);
+    }
+
+    /**
+     * Check if we have knowledge in the javac state that
+     * describe the results of compiling this package before.
+     */
+    public boolean existsInJavacState() {
+        return artifacts.size() > 0 || pubapi.size() > 0;
+    }
+
+    public static List<String> pubapiToList(String ps)
+    {
+        String[] lines = ps.split("\n");
+        List<String> r = new ArrayList<String>();
+        for (String l : lines) {
+            r.add(l);
+        }
+        return r;
+    }
+
+    public boolean hasPubapiChanged(List<String> ps) {
+        Iterator<String> i = ps.iterator();
+        Iterator<String> j = pubapi.iterator();
+        int line = 0;
+        while (i.hasNext() && j.hasNext()) {
+            String is = i.next();
+            String js = j.next();
+            if (!is.equals(js)) {
+                Log.debug("Change in pubapi for package "+name+" line "+line);
+                Log.debug("Old: "+js);
+                Log.debug("New: "+is);
+                return true;
+            }
+            line++;
+        }
+        if ((i.hasNext() && !j.hasNext() ) ||
+            (!i.hasNext() && j.hasNext())) {
+            Log.debug("Change in pubapi for package "+name);
+            if (i.hasNext()) {
+                Log.debug("New has more lines!");
+            } else {
+                Log.debug("Old has more lines!");
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void setPubapi(List<String> ps) {
+        pubapi = ps;
+    }
+
+    public void setDependencies(Set<String> ds) {
+        dependencies = ds;
+    }
+
+    public void save(StringBuilder b) {
+        b.append("P ").append(name).append("\n");
+        Source.saveSources(sources, b);
+        saveDependencies(b);
+        savePubapi(b);
+        saveArtifacts(b);
+    }
+
+    static public Package load(Module module, String l) {
+        String name = l.substring(2);
+        return new Package(module, name);
+    }
+
+    public void loadDependency(String l) {
+        String n = l.substring(2);
+        addDependency(n);
+    }
+
+    public void loadPubapi(String l) {
+        String pi = l.substring(2);
+        addPubapi(pi);
+    }
+
+    public void saveDependencies(StringBuilder b) {
+        List<String> sorted_dependencies = new ArrayList<String>();
+        for (String key : dependencies) {
+            sorted_dependencies.add(key);
+        }
+        Collections.sort(sorted_dependencies);
+        for (String a : sorted_dependencies) {
+            b.append("D "+a+"\n");
+        }
+    }
+
+    public void savePubapi(StringBuilder b) {
+        for (String l : pubapi) {
+            b.append("I "+l+"\n");
+        }
+    }
+
+    public static void savePackages(Map<String,Package> packages, StringBuilder b) {
+        List<String> sorted_packages = new ArrayList<String>();
+        for (String key : packages.keySet() ) {
+            sorted_packages.add(key);
+        }
+        Collections.sort(sorted_packages);
+        for (String s : sorted_packages) {
+            Package p = packages.get(s);
+            p.save(b);
+        }
+    }
+
+    public void addArtifact(String a) {
+        artifacts.put(a, new File(a));
+    }
+
+    public void addArtifact(File f) {
+        artifacts.put(f.getPath(), f);
+    }
+
+    public void addArtifacts(Set<URI> as) {
+        for (URI u : as) {
+            addArtifact(new File(u));
+        }
+    }
+
+    public void setArtifacts(Set<URI> as) {
+        assert(!artifacts.isEmpty());
+        artifacts = new HashMap<String,File>();
+        addArtifacts(as);
+    }
+
+    public void loadArtifact(String l) {
+        // Find next space after "A ".
+        int dp = l.indexOf(' ',2);
+        String fn = l.substring(2,dp);
+        long last_modified = Long.parseLong(l.substring(dp+1));
+        File f = new File(fn);
+        if (f.exists() && f.lastModified() != last_modified) {
+            // Hmm, the artifact on disk does not have the same last modified
+            // timestamp as the information from the build database.
+            // We no longer trust the artifact on disk. Delete it.
+            // The smart javac wrapper will then rebuild the artifact.
+            Log.debug("Removing "+f.getPath()+" since its timestamp does not match javac_state.");
+            f.delete();
+        }
+        artifacts.put(f.getPath(), f);
+    }
+
+    public void saveArtifacts(StringBuilder b) {
+        List<File> sorted_artifacts = new ArrayList<File>();
+        for (File f : artifacts.values()) {
+            sorted_artifacts.add(f);
+        }
+        Collections.sort(sorted_artifacts);
+        for (File f : sorted_artifacts) {
+            // The last modified information is only used
+            // to detect tampering with the output dir.
+            // If the outputdir has been modified, not by javac,
+            // then a mismatch will be detected in the last modified
+            // timestamps stored in the build database compared
+            // to the timestamps on disk and the artifact will be deleted.
+
+            b.append("A "+f.getPath()+" "+f.lastModified()+"\n");
+        }
+    }
+
+    /**
+     * Always clean out a tainted package before it is recompiled.
+     */
+    public void deleteArtifacts() {
+        for (File a : artifacts.values()) {
+            a.delete();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/ProblemException.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012, 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;
+
+/**
+ * Used to signal serious problems when running sjavac.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class ProblemException extends Exception {
+    static final long serialVersionUID = -3387516993124229949L;
+    public ProblemException(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Source.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.util.Set;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+
+/** A Source object maintains information about a source file.
+ * For example which package it belongs to and kind of source it is.
+ * The class also knows how to find source files (scanRoot) given include/exclude
+ * patterns and a root.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Source implements Comparable<Source> {
+    // The package the source belongs to.
+   private Package pkg;
+    // Name of this source file, relative its source root.
+    // For example: java/lang/Object.java
+    // Or if the source file is inside a module:
+    // jdk.base/java/lang/Object.java
+    private String name;
+    // What kind of file is this.
+    private String suffix;
+    // When this source file was last_modified
+    private long lastModified;
+    // The source File.
+    private File file;
+    // The source root under which file resides.
+    private File root;
+    // If the source is generated.
+    private boolean isGenerated;
+    // If the source is only linked to, not compiled.
+    private boolean linkedOnly;
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof Source) && name.equals(((Source)o).name);
+    }
+
+    @Override
+    public int compareTo(Source o) {
+        return name.compareTo(o.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    public Source(Module m, String n, File f, File r) {
+        name = n;
+        int dp = n.lastIndexOf(".");
+        if (dp != -1) {
+            suffix = n.substring(dp);
+        } else {
+            suffix = "";
+        }
+        file = f;
+        root = r;
+        lastModified = f.lastModified();
+        linkedOnly = false;
+    }
+
+    public Source(Package p, String n, long lm) {
+        pkg = p;
+        name = n;
+        int dp = n.lastIndexOf(".");
+        if (dp != -1) {
+            suffix = n.substring(dp);
+        } else {
+            suffix = "";
+        }
+        file = null;
+        root = null;
+        lastModified = lm;
+        linkedOnly = false;
+        int ls = n.lastIndexOf('/');
+    }
+
+    public String name() { return name; }
+    public String suffix() { return suffix; }
+    public Package pkg() { return pkg; }
+    public File   file() { return file; }
+    public File   root() { return root; }
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public void setPackage(Package p) {
+        pkg = p;
+    }
+
+    public void markAsGenerated() {
+        isGenerated = true;
+    }
+
+    public boolean isGenerated() {
+        return isGenerated;
+    }
+
+    public void markAsLinkedOnly() {
+        linkedOnly = true;
+    }
+
+    public boolean isLinkedOnly() {
+        return linkedOnly;
+    }
+
+    private void save(StringBuilder b) {
+        String CL = linkedOnly?"L":"C";
+        String GS = isGenerated?"G":"S";
+        b.append(GS+" "+CL+" "+name+" "+file.lastModified()+"\n");
+    }
+    // Parse a line that looks like this:
+    // S C /code/alfa/A.java 1357631228000
+    static public Source load(Package lastPackage, String l, boolean isGenerated) {
+        int sp = l.indexOf(' ',4);
+        if (sp == -1) return null;
+        String name = l.substring(4,sp);
+        long last_modified = Long.parseLong(l.substring(sp+1));
+
+        boolean isLinkedOnly = false;
+        if (l.charAt(2) == 'L') {
+            isLinkedOnly = true;
+        } else if (l.charAt(2) == 'C') {
+            isLinkedOnly = false;
+        } else return null;
+
+        Source s = new Source(lastPackage, name, last_modified);
+        s.file = new File(name);
+        if (isGenerated) s.markAsGenerated();
+        if (isLinkedOnly) s.markAsLinkedOnly();
+        return s;
+    }
+
+    public static void saveSources(Map<String,Source> sources, StringBuilder b) {
+        List<String> sorted_sources = new ArrayList<String>();
+        for (String key : sources.keySet()) {
+            sorted_sources.add(key);
+        }
+        Collections.sort(sorted_sources);
+        for (String key : sorted_sources) {
+            Source s = sources.get(key);
+            s.save(b);
+        }
+    }
+
+    /**
+     * Recurse into the directory root and find all files matchine the excl/incl/exclfiles/inclfiles rules.
+     * Detects the existence of module-info.java files and presumes that the directory it resides in
+     * is the name of the current module.
+     */
+    static public void scanRoot(File root,
+                                Set<String> suffixes,
+                                List<String> excludes, List<String> includes,
+                                List<String> excludeFiles, List<String> includeFiles,
+                                Map<String,Source> foundFiles,
+                                Map<String,Module> foundModules,
+                                Module currentModule,
+                                boolean permitSourcesWithoutPackage,
+                                boolean inGensrc,
+                                boolean inLinksrc)
+        throws ProblemException {
+
+        if (root == null) return;
+        int root_prefix = root.getPath().length()+1;
+        // This is the root source directory, it must not contain any Java sources files
+        // because we do not allow Java source files without a package.
+        // (Unless of course --permit-sources-without-package has been specified.)
+        // 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,
+                                       foundFiles, foundModules, currentModule,
+                                       inGensrc, inLinksrc);
+
+        File[] dirfiles = root.listFiles();
+        for (File d : dirfiles) {
+            if (d.isDirectory()) {
+                // Descend into the directory structure.
+                scanDirectory(d, root_prefix, root, suffixes,
+                              excludes, includes, excludeFiles, includeFiles,
+                              false, 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
+     */
+    static private boolean hasMatch(String path, List<String> patterns) {
+        for (String p : patterns) {
+            // Exact match
+            if (p.equals(path)) {
+                return true;
+            }
+            // Single dot the end matches this package and all its subpackages.
+            if (p.endsWith(".*")) {
+                // Remove the wildcard
+                String patprefix = p.substring(0,p.length()-2);
+                // Does the path start with the pattern prefix?
+                if (path.startsWith(patprefix)) {
+                    // If the path has the same length as the pattern prefix, then it is a match.
+                    // 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())=='.') {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Matches patterns with the asterisk first. */
+     // 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) {
+        path = Util.normalizeDriveLetter(path);
+        for (String p : patterns) {
+            // Exact match
+            if (p.equals(path)) {
+                return true;
+            }
+            // Single dot the end matches this package and all its subpackages.
+            if (p.startsWith("*")) {
+                // Remove the wildcard
+                String patsuffix = p.substring(1);
+                // Does the path start with the pattern prefix?
+                if (path.endsWith(patsuffix)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Add the files in the directory, assuming that the file has not been excluded.
+     * Returns a fresh Module object, if this was a dir with a module-info.java file.
+     */
+    static private Module addFilesInDir(File dir, int rootPrefix, File root,
+                                        Set<String> suffixes, boolean allow_javas,
+                                        List<String> excludeFiles, List<String> includeFiles, boolean all,
+                                        Map<String,Source> foundFiles,
+                                        Map<String,Module> foundModules,
+                                        Module currentModule,
+                                        boolean inGensrc,
+                                        boolean inLinksrc)
+        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);
+                        }
+                        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);
+                    }
+                }
+            }
+        }
+        return currentModule;
+    }
+
+    private static boolean gurka = false;
+
+    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,
+                                      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.
+        if (dir.getPath().length() > rootPrefix) {
+            pkg_name = dir.getPath().substring(rootPrefix).replace(File.separatorChar,'.');
+        }
+        // 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)))) {
+            // Add the source files.
+            currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles, all,
+                                          foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
+        }
+
+        for (File d : dir.listFiles()) {
+            if (d.isDirectory()) {
+                // Descend into the directory structure.
+                scanDirectory(d, rootPrefix, root, suffixes,
+                              excludes, includes, excludeFiles, includeFiles, all,
+                              foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Transformer.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.PrintStream;
+import java.net.URI;
+import java.util.Set;
+import java.util.Map;
+
+/**
+ * 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)
+ * but can also be an unpredictable number of generated source files (eg idl2java)
+ * or a single predictable output file (eg when copying,cleaning or compiling a properties file).
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public interface Transformer
+{
+    /**
+     * The transform method takes a set of package names, mapped to their source files and to the
+     * pubapis of the packages.
+     *
+     * The transform implementation must:
+     *    store the names of the generated artifacts for each package into package_artifacts
+     *    store found dependencies to other packages into the supplied set package_dependencies
+     *    store the public api for a package into the supplied set package_pubapis
+     *
+     * Any benign messages as a result of running the transform
+     * are written into stdout, and errors are written to stderr.
+     *
+     * The debug_level can be 0=silent (only warnings and errors) 1=normal 2=verbose 3 or greater=debug
+     * setExtra is used to set the extra information information that can be passed on
+     * the command line to the smart javac wrapper.
+     *
+     * If sjavac is building incrementally from an existing javac_state, the var incremental is true.
+     *
+     * The transformer will only be called if some source in the package (or dependency) has
+     * a modified timestamp. Thus the transformer might get called with many sources, of which
+     * only one has changed. The transformer is allowed to regenerate all artifacts but
+     * a better transformer will only write those artifacts that need updating.
+     *
+     * However the transformer must verify that the existing artifacts really are there!
+     * and it must always update package_artifacts, package_dependencies, and package_pubapis correctly.
+     * This means that at least for Java source, it will always have to recompile the sources.
+     *
+     * The transformer is allowed to put files anywhere in the dest_root.
+     * An example of this is, can be the META-INF transformer that copy files
+     * below META-INF directories to the single META-INF directory below dest_root.
+     *
+     * False is returned if there was an error that prevented the transform.
+     * I.e. something was printed on stderr.
+     *
+     * If num_cores is set to a non-zero value. The transform should attempt to use no more than these
+     * number of threads for heavy work.
+     */
+    boolean transform(Map<String,Set<URI>> pkgSrcs,
+                      Set<URI>             visibleSources,
+                      Map<URI,Set<String>> visibleClasses,
+                      Map<String,Set<String>> oldPackageDependencies,
+                      URI destRoot,
+                      Map<String,Set<URI>>    packageArtifacts,
+                      Map<String,Set<String>> packageDependencies,
+                      Map<String,String>      packagePublicApis,
+                      int debugLevel,
+                      boolean incremental,
+                      int numCores,
+                      PrintStream out,
+                      PrintStream err);
+
+    void setExtra(String e);
+    void setExtra(String[] args);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/Util.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * Utilities.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Util {
+
+    public static String toFileSystemPath(String pkgId) {
+        if (pkgId == null || pkgId.length()==0) return null;
+        String pn;
+        if (pkgId.charAt(0) == ':') {
+            // When the module is the default empty module.
+            // Do not prepend the module directory, because there is none.
+            // Thus :java.foo.bar translates to java/foo/bar (or \)
+            pn = pkgId.substring(1).replace('.',File.separatorChar);
+        } else {
+            // There is a module. Thus jdk.base:java.foo.bar translates
+            // into jdk.base/java/foo/bar
+            int cp = pkgId.indexOf(':');
+            String mn = pkgId.substring(0,cp);
+            pn = mn+File.separatorChar+pkgId.substring(cp+1).replace('.',File.separatorChar);
+        }
+        return pn;
+    }
+
+    public static String justPackageName(String pkgName) {
+        int c = pkgName.indexOf(":");
+        assert(c != -1);
+        return pkgName.substring(c+1);
+    }
+
+    public static String extractStringOption(String opName, String s) {
+        int p = s.indexOf(opName+"=");
+        if (p == -1) return null;
+        p+=opName.length()+1;
+        int pe = s.indexOf(',', p);
+        if (pe == -1) pe = s.length();
+        return s.substring(p, pe);
+    }
+
+    public static int extractIntOption(String opName, String s) {
+        int p = s.indexOf(opName+"=");
+        if (p == -1) return 0;
+        p+=opName.length()+1;
+        int pe = s.indexOf(',', p);
+        if (pe == -1) pe = s.length();
+        int v = 0;
+        try {
+            v = Integer.parseInt(s.substring(p, pe));
+        } catch (Exception e) {}
+        return v;
+    }
+
+    /**
+     * Clean out unwanted sub options supplied inside a primary option.
+     * For example to only had portfile remaining from:
+     *    settings="--server:id=foo,portfile=bar"
+     * 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) {
+        StringBuilder sb = new StringBuilder();
+        if (!s.startsWith(optionPrefix)) return "";
+        StringTokenizer st = new StringTokenizer(s.substring(optionPrefix.length()), ",");
+        while (st.hasMoreTokens()) {
+            String o = st.nextToken();
+            int p = o.indexOf('=');
+            if (p>0) {
+                String key = o.substring(0,p);
+                String val = o.substring(p+1);
+                if (allowedSubOptions.contains(key)) {
+                    if (sb.length() > 0) sb.append(',');
+                    sb.append(key+"="+val);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convenience method to create a set with strings.
+     */
+    public static Set<String> set(String... ss) {
+        Set<String> set = new HashSet<String>();
+        set.addAll(Arrays.asList(ss));
+        return set;
+    }
+
+    /**
+     * Normalize windows drive letter paths to upper case to enable string
+     * comparison.
+     *
+     * @param file File name to normalize
+     * @return The normalized string if file has a drive letter at the beginning,
+     *         otherwise the original string.
+     */
+    public static String normalizeDriveLetter(String file) {
+        if (file.length() > 2 && file.charAt(1) == ':') {
+            return Character.toUpperCase(file.charAt(0)) + file.substring(1);
+        } else if (file.length() > 3 && file.charAt(0) == '*'
+                   && file.charAt(2) == ':') {
+            // Handle a wildcard * at the beginning of the string.
+            return file.substring(0, 1) + Character.toUpperCase(file.charAt(1))
+                   + file.substring(2);
+        }
+        return file;
+    }
+
+    /**
+     * Locate the setting for the server properties.
+     */
+    public static String findServerSettings(String[] args) {
+        for (String s : args) {
+            if (s.startsWith("--server:")) {
+                return s;
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/Dependencies.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1999, 2011, 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.comp;
+
+import javax.lang.model.element.Element;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Name;
+
+/** Utility class containing dependency information between packages
+ *  and the pubapi for a package.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class Dependencies {
+    protected static final Context.Key<Dependencies> dependenciesKey =
+        new Context.Key<Dependencies>();
+
+    // The log to be used for error reporting.
+    protected Log log;
+    // Map from package name to packages that the package depends upon.
+    protected Map<Name,Set<Name>> deps;
+    // This is the set of all packages that are supplied
+    // through the java files at the command line.
+    protected Set<Name> explicitPackages;
+
+    // Map from a package name to its public api.
+    // Will the Name encode the module in the future?
+    // If not, this will have to change to map from Module+Name to public api.
+    protected Map<Name,StringBuffer> publicApiPerClass;
+
+    public static Dependencies instance(Context context) {
+        Dependencies instance = context.get(dependenciesKey);
+        if (instance == null)
+            instance = new Dependencies(context);
+        return instance;
+    }
+
+    private Dependencies(Context context) {
+        context.put(dependenciesKey, this);
+        log = Log.instance(context);
+    }
+
+    public void reset()
+    {
+        deps = new HashMap<Name, Set<Name>>();
+        explicitPackages = new HashSet<Name>();
+        publicApiPerClass = new HashMap<Name,StringBuffer>();
+    }
+
+    /**
+     * Fetch the set of dependencies that are relevant to the compile
+     * that has just been performed. I.e. we are only interested in
+     * dependencies for classes that were explicitly compiled.
+     * @return
+     */
+    public Map<String,Set<String>> getDependencies() {
+        Map<String,Set<String>> new_deps = new HashMap<String,Set<String>>();
+        if (explicitPackages == null) return new_deps;
+        for (Name pkg : explicitPackages) {
+            Set<Name> set = deps.get(pkg);
+            if (set != null) {
+                Set<String> new_set = new_deps.get(pkg.toString());
+                if (new_set == null) {
+                    new_set = new HashSet<String>();
+                    // Modules beware....
+                    new_deps.put(":"+pkg.toString(), new_set);
+                }
+                for (Name d : set) {
+                    new_set.add(":"+d.toString());
+                }
+            }
+        }
+        return new_deps;
+    }
+
+    class CompareNames implements Comparator<Name> {
+         public int compare(Name a, Name b) {
+             return a.toString().compareTo(b.toString());
+         }
+
+         public boolean equals(Object obj) {
+             return super.equals(obj);
+         }
+    }
+
+    /**
+     * Convert the map from class names to their pubapi to a map
+     * from package names to their pubapi (which is the sorted concatenation
+     * of all the class pubapis)
+     */
+    public Map<String,String> getPubapis() {
+        Map<String,String> publicApiPerPackage = new HashMap<String,String>();
+        if (publicApiPerClass == null) return publicApiPerPackage;
+        Name[] keys = publicApiPerClass.keySet().toArray(new Name[0]);
+        Arrays.sort(keys, new CompareNames());
+        StringBuffer newPublicApi = new StringBuffer();
+        int i=0;
+        String prevPkg = "";
+        for (Name k : keys) {
+            String cn = k.toString();
+            String pn = "";
+            int dp = cn.lastIndexOf('.');
+            if (dp != -1) {
+                pn = cn.substring(0,dp);
+            }
+            if (!pn.equals(prevPkg)) {
+                if (!prevPkg.equals("")) {
+                    // Add default module name ":"
+                    publicApiPerPackage.put(":"+prevPkg, newPublicApi.toString());
+                }
+                newPublicApi = new StringBuffer();
+                prevPkg = pn;
+            }
+            newPublicApi.append(publicApiPerClass.get(k));
+            i++;
+        }
+        if (!prevPkg.equals(""))
+            publicApiPerPackage.put(":"+prevPkg, newPublicApi.toString());
+        return publicApiPerPackage;
+    }
+
+    /**
+     * Visit the api of a class and construct a pubapi string and
+     * store it into the pubapi_perclass map.
+     */
+    public void visitPubapi(Element e) {
+        Name n = ((ClassSymbol)e).fullname;
+        Name p = ((ClassSymbol)e).packge().fullname;
+        StringBuffer sb = publicApiPerClass.get(n);
+        assert(sb == null);
+        sb = new StringBuffer();
+        PubapiVisitor v = new PubapiVisitor(sb);
+        v.visit(e);
+        if (sb.length()>0) {
+            publicApiPerClass.put(n, sb);
+        }
+        explicitPackages.add(p);
+     }
+
+    /**
+     * Collect a dependency. curr_pkg is marked as depending on dep_pkg.
+     */
+    public void collect(Name currPkg, Name depPkg) {
+        if (!currPkg.equals(depPkg)) {
+            Set<Name> theset = deps.get(currPkg);
+            if (theset==null) {
+                theset = new HashSet<Name>();
+                deps.put(currPkg, theset);
+            }
+            theset.add(depPkg);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012, 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.comp;
+
+import java.util.StringTokenizer;
+
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.sjavac.server.CompilerThread;
+import java.io.File;
+
+/** Subclass to Resolve that overrides collect.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class JavaCompilerWithDeps extends JavaCompiler {
+
+    /** The dependency database
+     */
+    protected Dependencies deps;
+    protected CompilerThread compilerThread;
+
+    public JavaCompilerWithDeps(Context context, CompilerThread t) {
+        super(context);
+        deps = Dependencies.instance(context);
+        compilerThread = t;
+        needRootClasses = true;
+    }
+
+    public static void preRegister(Context context, final CompilerThread t) {
+        context.put(compilerKey, new Context.Factory<JavaCompiler>() {
+            public JavaCompiler make(Context c) {
+                JavaCompiler instance = new JavaCompilerWithDeps(c, t);
+                c.put(JavaCompiler.class, instance);
+                return instance;
+            }
+        });
+    }
+
+    /** Collect the public apis of classes supplied explicitly for compilation.
+     * @param sym The class to visit.
+     */
+    @Override
+    public void reportPublicApi(ClassSymbol sym) {
+        // The next test will catch when source files are located in the wrong directory!
+        // This ought to be moved into javac as a new warning, or perhaps as part
+        // of the auxiliary class warning.
+
+        // For example if sun.swing.BeanInfoUtils
+        // is in fact stored in: /mybuild/jdk/gensrc/javax/swing/beaninfo/BeanInfoUtils.java
+
+        // We do not need to test that BeanInfoUtils is stored in a file named BeanInfoUtils
+        // since this is checked earlier.
+        if (sym.sourcefile != null) {
+            // Rewrite sun.swing.BeanInfoUtils into /sun/swing/
+            StringBuilder pathb = new StringBuilder();
+            StringTokenizer qn = new StringTokenizer(sym.packge().toString(), ".");
+            boolean first = true;
+            while (qn.hasMoreTokens()) {
+                String o = qn.nextToken();
+                pathb.append("/");
+                pathb.append(o);
+                first = false;
+            }
+            pathb.append("/");
+            String path = pathb.toString();
+
+            // Now cut the uri to be: file:///mybuild/jdk/gensrc/javax/swing/beaninfo/
+            String p = sym.sourcefile.toUri().getPath();
+            // Do not use File.separatorChar here, a URI always uses slashes /.
+            int i = p.lastIndexOf("/");
+            String pp = p.substring(0,i+1);
+
+            // Now check if the truncated uri ends with the path. (It does not == failure!)
+            if (path.length() > 0 && !path.equals("/unnamed package/") && !pp.endsWith(path)) {
+                compilerThread.logError("Error: The source file "+sym.sourcefile.getName()+
+                                        " is located in the wrong package directory, because it contains the class "+
+                                        sym.getQualifiedName());
+            }
+        }
+        deps.visitPubapi(sym);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/PubapiVisitor.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2011, 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.comp;
+
+import java.util.Iterator;
+import java.util.List;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementScanner6;
+
+/** Utility class that constructs a textual representation
+ * of the public api of a class.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class PubapiVisitor extends ElementScanner6<Void, Void> {
+
+    StringBuffer sb;
+    // Important that it is 1! Part of protocol over wire, silly yes.
+    // Fix please.
+    int indent = 1;
+
+    public PubapiVisitor(StringBuffer sb) {
+        this.sb = sb;
+    }
+
+    String depth(int l) {
+        return "                                              ".substring(0, l);
+    }
+
+    @Override
+    public Void visitType(TypeElement e, Void p) {
+        if (e.getModifiers().contains(Modifier.PUBLIC)
+            || e.getModifiers().contains(Modifier.PROTECTED))
+        {
+            sb.append(depth(indent) + "TYPE " + e.getQualifiedName() + "\n");
+            indent += 2;
+            Void v = super.visitType(e, p);
+            indent -= 2;
+            return v;
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitVariable(VariableElement e, Void p) {
+        if (e.getModifiers().contains(Modifier.PUBLIC)
+            || e.getModifiers().contains(Modifier.PROTECTED)) {
+            sb.append(depth(indent)).append("VAR ")
+                    .append(makeVariableString(e)).append("\n");
+        }
+        // Safe to not recurse here, because the only thing
+        // to visit here is the constructor of a variable declaration.
+        // If it happens to contain an anonymous inner class (which it might)
+        // then this class is never visible outside of the package anyway, so
+        // we are allowed to ignore it here.
+        return null;
+    }
+
+    @Override
+    public Void visitExecutable(ExecutableElement e, Void p) {
+        if (e.getModifiers().contains(Modifier.PUBLIC)
+            || e.getModifiers().contains(Modifier.PROTECTED)) {
+            sb.append(depth(indent)).append("METHOD ")
+                    .append(makeMethodString(e)).append("\n");
+        }
+        return null;
+    }
+
+    /**
+     * Creates a String representation of a method element with everything
+     * necessary to track all public aspects of it in an API.
+     * @param e Element to create String for.
+     * @return String representation of element.
+     */
+    protected String makeMethodString(ExecutableElement e) {
+        StringBuilder result = new StringBuilder();
+        for (Modifier modifier : e.getModifiers()) {
+            result.append(modifier.toString());
+            result.append(" ");
+        }
+        result.append(e.getReturnType().toString());
+        result.append(" ");
+        result.append(e.toString());
+
+        List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
+        if (!thrownTypes.isEmpty()) {
+            result.append(" throws ");
+            for (Iterator<? extends TypeMirror> iterator = thrownTypes
+                    .iterator(); iterator.hasNext();) {
+                TypeMirror typeMirror = iterator.next();
+                result.append(typeMirror.toString());
+                if (iterator.hasNext()) {
+                    result.append(", ");
+                }
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Creates a String representation of a variable element with everything
+     * necessary to track all public aspects of it in an API.
+     * @param e Element to create String for.
+     * @return String representation of element.
+     */
+    protected String makeVariableString(VariableElement e) {
+        StringBuilder result = new StringBuilder();
+        for (Modifier modifier : e.getModifiers()) {
+            result.append(modifier.toString());
+            result.append(" ");
+        }
+        result.append(e.asType().toString());
+        result.append(" ");
+        result.append(e.toString());
+        Object value = e.getConstantValue();
+        if (value != null) {
+            result.append(" = ");
+            if (e.asType().toString().equals("char")) {
+                int v = (int)value.toString().charAt(0);
+                result.append("'\\u"+Integer.toString(v,16)+"'");
+            } else {
+                result.append(value.toString());
+            }
+        }
+        return result.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/ResolveWithDeps.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 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.comp;
+
+import com.sun.tools.javac.comp.Resolve;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.code.Symbol;
+
+/** Subclass to Resolve that overrides collect.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class ResolveWithDeps extends Resolve {
+
+    /** The dependency database
+     */
+    protected Dependencies deps;
+
+    protected ResolveWithDeps(Context context) {
+        super(context);
+        deps = Dependencies.instance(context);
+    }
+
+    public static void preRegister(Context context) {
+        context.put(resolveKey, new Context.Factory<Resolve>() {
+            public Resolve make(Context c) {
+                Resolve instance = new ResolveWithDeps(c);
+                c.put(Resolve.class, instance);
+                return instance;
+            }
+        });
+    }
+    /** Collect dependencies in the enclosing class
+     * @param from The enclosing class sym
+     * @param to   The enclosing classes references this sym.
+     * */
+    @Override
+    public void reportDependence(Symbol from, Symbol to) {
+        // Capture dependencies between the packages.
+        deps.collect(from.packge().fullname, to.packge().fullname);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartFileManager.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2012, 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.comp;
+
+import com.sun.tools.javac.util.ListBuffer;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
+import javax.tools.*;
+import javax.tools.JavaFileObject.Kind;
+
+/**
+ * Intercepts reads and writes to the file system to gather
+ * information about what artifacts are generated.
+ *
+ * Traps writes to certain files, if the content written is identical
+ * to the existing file.
+ *
+ * Can also blind out the filemanager from seeing certain files in the file system.
+ * Necessary to prevent javac from seeing some sources where the source path points.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
+
+    // Set of sources that can be seen by javac.
+    Set<URI> visibleSources = new HashSet<URI>();
+    // Map from modulename:packagename to artifacts.
+    Map<String,Set<URI>> packageArtifacts = new HashMap<String,Set<URI>>();
+    // Where to print informational messages.
+    PrintWriter stdout;
+
+    public SmartFileManager(JavaFileManager fileManager) {
+        super(fileManager);
+    }
+
+    public void setVisibleSources(Set<URI> s) {
+        visibleSources = s;
+    }
+
+    public void cleanArtifacts() {
+        packageArtifacts = new HashMap<String,Set<URI>>();
+    }
+
+    public void setLog(PrintWriter pw) {
+        stdout = pw;
+    }
+
+    public Map<String,Set<URI>> getPackageArtifacts() {
+        return packageArtifacts;
+    }
+
+    @Override
+    public Iterable<JavaFileObject> list(Location location,
+                                         String packageName,
+                                         Set<Kind> kinds,
+                                         boolean recurse)
+        throws IOException
+    {
+        // Acquire the list of files.
+        Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
+        if (visibleSources.isEmpty()) {
+            return files;
+        }
+        // Now filter!
+        ListBuffer<JavaFileObject> filteredFiles = new ListBuffer<JavaFileObject>();
+        for (JavaFileObject f : files) {
+            URI uri = f.toUri();
+            String t = uri.toString();
+            if (t.startsWith("jar:")
+                || t.endsWith(".class")
+                || visibleSources.contains(uri))
+            {
+                filteredFiles.add(f);
+            }
+        }
+        return filteredFiles;
+    }
+
+    @Override
+    public boolean hasLocation(Location location) {
+        return super.hasLocation(location);
+    }
+
+    @Override
+    public JavaFileObject getJavaFileForInput(Location location,
+                                              String className,
+                                              Kind kind)
+        throws IOException
+    {
+        JavaFileObject file = super.getJavaFileForInput(location, className, kind);
+        if (file == null || visibleSources.isEmpty()) {
+            return file;
+        }
+
+        if (visibleSources.contains(file.toUri())) {
+            return file;
+        }
+        return null;
+    }
+
+    @Override
+    public JavaFileObject getJavaFileForOutput(Location location,
+                                               String className,
+                                               Kind kind,
+                                               FileObject sibling)
+        throws IOException
+    {
+        JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
+        if (file == null) return file;
+        int dp = className.lastIndexOf('.');
+        String pkg_name = "";
+        if (dp != -1) {
+            pkg_name = className.substring(0, dp);
+        }
+        // When modules are in use, then the mod_name might be something like "jdk_base"
+        String mod_name = "";
+        addArtifact(mod_name+":"+pkg_name, file.toUri());
+        return file;
+    }
+
+    @Override
+    public FileObject getFileForInput(Location location,
+                                      String packageName,
+                                      String relativeName)
+        throws IOException
+    {
+        FileObject file =  super.getFileForInput(location, packageName, relativeName);
+        if (file == null || visibleSources.isEmpty()) {
+            return file;
+        }
+
+        if (visibleSources.contains(file.toUri())) {
+            return file;
+        }
+        return null;
+    }
+
+    @Override
+    public FileObject getFileForOutput(Location location,
+                                       String packageName,
+                                       String relativeName,
+                                       FileObject sibling)
+        throws IOException
+    {
+        FileObject file = super.getFileForOutput(location, packageName, relativeName, sibling);
+        if (file == null) return file;
+        if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) &&
+                file instanceof JavaFileObject) {
+           file = new SmartFileObject((JavaFileObject)file, stdout);
+           packageName = ":" + packageNameFromFileName(relativeName);
+        }
+        if (packageName.equals("")) {
+            packageName = ":";
+        }
+        addArtifact(packageName, file.toUri());
+        return file;
+    }
+
+    private String packageNameFromFileName(String fn) {
+        StringBuilder sb = new StringBuilder();
+        int p = fn.indexOf('_'), pp = 0;
+        while (p != -1) {
+            if (sb.length() > 0) sb.append('.');
+            sb.append(fn.substring(pp,p));
+            if (p == fn.length()-1) break;
+            pp = p+1;
+            p = fn.indexOf('_',pp);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        super.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+    }
+
+    void addArtifact(String pkgName, URI art) {
+        Set<URI> s = packageArtifacts.get(pkgName);
+        if (s == null) {
+            s = new HashSet<URI>();
+            packageArtifacts.put(pkgName, s);
+        }
+        s.add(art);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartFileObject.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2012, 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.comp;
+
+import java.io.*;
+import java.net.URI;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.JavaFileObject;
+
+/**
+ * The SmartFileObject will return an outputstream that cache the written data
+ * and compare the new content with the old content on disk. Only if they differ,
+ * will the file be updated.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class SmartFileObject implements JavaFileObject {
+
+    JavaFileObject file;
+    PrintWriter stdout;
+
+    public SmartFileObject(JavaFileObject r, PrintWriter pw) {
+        file = r;
+        stdout = pw;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return file.equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        return file.hashCode();
+    }
+
+    public Kind getKind() {
+        return file.getKind();
+    }
+
+    public boolean isNameCompatible(String simpleName, Kind kind) {
+        return file.isNameCompatible(simpleName, kind);
+    }
+
+    public URI toUri() {
+        return file.toUri();
+    }
+
+    public String getName() {
+        return file.getName();
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return file.openInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return file.openOutputStream();
+    }
+
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+        return file.getCharContent(ignoreEncodingErrors);
+    }
+
+    static String lineseparator = System.getProperty("line.separator");
+
+    public Writer openWriter() throws IOException {
+        StringBuilder s = new StringBuilder();
+        try (BufferedReader r = new BufferedReader(file.openReader(true))) {
+            while (r.ready()) {
+                s.append(r.readLine()+lineseparator);
+            }
+        } catch (FileNotFoundException e) {
+            // Perfectly ok.
+        }
+        return new SmartWriter(file, s.toString(), file.getName(), stdout);
+    }
+
+    public long getLastModified() {
+        return file.getLastModified();
+    }
+
+    public boolean delete() {
+        return file.delete();
+    }
+
+    public Modifier getAccessLevel() {
+        return file.getAccessLevel();
+    }
+
+    public NestingKind getNestingKind() {
+        return file.getNestingKind();
+    }
+
+    public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+        return file.openReader(ignoreEncodingErrors);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartWriter.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012, 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.comp;
+
+import java.io.*;
+import javax.tools.JavaFileObject;
+
+/**
+ * The SmartWriter will cache the written data and when the writer is closed,
+ * then it will compare the cached data with the old_content string.
+ * If different, then it will write all the new content to the file.
+ * If not, the file is not touched.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class SmartWriter extends Writer {
+
+    String name;
+    JavaFileObject file;
+    String oldContent;
+    StringWriter newContent = new StringWriter();
+    PrintWriter stdout;
+    boolean closed;
+    public SmartWriter(JavaFileObject f, String s, String n, PrintWriter pw) {
+        name = n;
+        file = f;
+        oldContent = s;
+        newContent = new StringWriter();
+        stdout = pw;
+        closed = false;
+    }
+
+    public void write(char[] chars, int i, int i1)
+    {
+        newContent.write(chars, i, i1);
+    }
+
+    public void close() throws IOException {
+        if (closed) return;
+        closed = true;
+        String s = newContent.toString();
+        if (!oldContent.equals(s)) {
+            int p = file.getName().lastIndexOf(File.separatorChar);
+            try (Writer writer = file.openWriter()) {
+                writer.write(s);
+            }
+            stdout.println("Writing "+file.getName().substring(p+1));
+        }
+    }
+
+    public void flush() throws IOException {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2012, 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.server;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.Stack;
+import java.util.concurrent.Future;
+
+/** The compiler pool maintains compiler threads.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CompilerPool {
+    // The javac server that created this pool.
+    private JavacServer javacServer;
+    // A semaphore protecting the poolsize number of threads.
+    private Semaphore available;
+    // The stack of compiler threads.
+    private Stack<CompilerThread> compilers = new Stack<CompilerThread>();
+    // And the executor server to spawn threads.
+    private final ExecutorService executorPool;
+    // How many requests are active right now?
+    private int concurrentRequests = 0;
+    // When was the last request finished?
+    private long lastRequestFinished = 0;
+    // The total number of requests to this pool.
+    private int numRequests = 0;
+    // Protect access to the three above values.
+    private static final Object conc = new Object();
+
+    /**
+     * Return the javac server that this pool belongs to.
+     */
+    public JavacServer getJavacServer() {
+        return javacServer;
+    }
+
+    /**
+     * Return how many threads are running at this very moment.
+     */
+    public int numActiveRequests()
+    {
+        synchronized (conc) {
+            return concurrentRequests;
+        }
+    }
+
+    /**
+     * Return when the last request was finished.
+     * I.e. the pool has been idle since.
+     */
+    public long lastRequestFinished()
+    {
+        synchronized (conc) {
+            return lastRequestFinished;
+        }
+    }
+
+    /**
+     * Up the number of active requests.
+     */
+    public int startRequest() {
+        int n;
+        synchronized (conc) {
+            concurrentRequests++;
+            numRequests++;
+            n = numRequests;
+        }
+        return n;
+    }
+
+    /**
+     * Down the number of active requests. Return the current time.
+     */
+    public long stopRequest() {
+        synchronized (conc) {
+            concurrentRequests--;
+            lastRequestFinished = System.currentTimeMillis();
+        }
+        return lastRequestFinished;
+    }
+
+    /**
+     * Create a new compiler pool.
+     */
+    CompilerPool(int poolsize, JavacServer server) {
+        available = new Semaphore(poolsize, true);
+        javacServer = server;
+        executorPool = Executors.newFixedThreadPool(poolsize);
+        lastRequestFinished = System.currentTimeMillis();
+    }
+
+    /**
+     * Execute a compiler thread.
+     */
+    public void execute(CompilerThread ct) {
+        executorPool.execute(ct);
+    }
+
+    /**
+     * Execute a minor task, for example generating bytecodes and writing them to disk,
+     * that belong to a major compiler thread task.
+     */
+    public Future<?> executeSubtask(CompilerThread t, Runnable r) {
+        return executorPool.submit(r);
+    }
+
+    /**
+     * Shutdown the pool.
+     */
+    public void shutdown() {
+        executorPool.shutdown();
+    }
+
+    /**
+     * Acquire a compiler thread from the pool, or block until a thread is available.
+     * If the pools is empty, create a new thread, but never more than is "available".
+     */
+    public CompilerThread grabCompilerThread() throws InterruptedException {
+        available.acquire();
+        if (compilers.empty()) {
+            return new CompilerThread(this);
+        }
+        return compilers.pop();
+    }
+
+    /**
+     * Return the specified compiler thread to the pool.
+     */
+    public void returnCompilerThread(CompilerThread h) {
+        compilers.push(h);
+        available.release();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2012, 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.server;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+import java.util.concurrent.Future;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.BaseFileManager;
+import com.sun.tools.sjavac.comp.Dependencies;
+import com.sun.tools.sjavac.comp.JavaCompilerWithDeps;
+import com.sun.tools.sjavac.comp.SmartFileManager;
+import com.sun.tools.sjavac.comp.ResolveWithDeps;
+
+/**
+ * The compiler thread maintains a JavaCompiler instance and
+ * can receive a request from the client, perform the compilation
+ * requested and report back the results.
+ *
+ *  * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class CompilerThread implements Runnable {
+    private JavacServer javacServer;
+    private CompilerPool compilerPool;
+    private List<Future<?>> subTasks;
+
+    // Communicating over this socket.
+    private Socket socket;
+
+    // The necessary classes to do a compilation.
+    private com.sun.tools.javac.api.JavacTool compiler;
+    private StandardJavaFileManager fileManager;
+    private BaseFileManager fileManagerBase;
+    private SmartFileManager smartFileManager;
+    private Context context;
+
+    // If true, then this thread is serving a request.
+    private boolean inUse = false;
+
+    CompilerThread(CompilerPool cp) {
+        compilerPool = cp;
+        javacServer = cp.getJavacServer();
+    }
+
+    /**
+     * Execute a minor task, for example generating bytecodes and writing them to disk,
+     * that belong to a major compiler thread task.
+     */
+    public synchronized void executeSubtask(Runnable r) {
+        subTasks.add(compilerPool.executeSubtask(this, r));
+    }
+
+    /**
+     * Count the number of active sub tasks.
+     */
+    public synchronized int numActiveSubTasks() {
+        int c = 0;
+        for (Future<?> f : subTasks) {
+            if (!f.isDone() && !f.isCancelled()) {
+                c++;
+            }
+        }
+        return c;
+    }
+
+    /**
+     * Use this socket for the upcoming request.
+     */
+    public void setSocket(Socket s) {
+        socket = s;
+    }
+
+    /**
+     * Prepare the compiler thread for use. It is not yet started.
+     * It will be started by the executor service.
+     */
+    public synchronized void use() {
+        assert(!inUse);
+        inUse = true;
+        compiler = com.sun.tools.javac.api.JavacTool.create();
+        fileManager = compiler.getStandardFileManager(null, null, null);
+        fileManagerBase = (BaseFileManager)fileManager;
+        smartFileManager = new SmartFileManager(fileManager);
+        context = new Context();
+        context.put(JavaFileManager.class, smartFileManager);
+        ResolveWithDeps.preRegister(context);
+        JavaCompilerWithDeps.preRegister(context, this);
+        subTasks = new ArrayList<Future<?>>();
+    }
+
+    /**
+     * Prepare the compiler thread for idleness.
+     */
+    public synchronized void unuse() {
+        assert(inUse);
+        inUse = false;
+        compiler = null;
+        fileManager = null;
+        fileManagerBase = null;
+        smartFileManager = null;
+        context = null;
+        subTasks = null;
+    }
+
+    /**
+     * Expect this key on the next line read from the reader.
+     */
+    private static boolean expect(BufferedReader in, String key) throws IOException {
+        String s = in.readLine();
+        if (s != null && s.equals(key)) {
+            return true;
+        }
+        return false;
+    }
+
+    // The request identifier, for example GENERATE_NEWBYTECODE
+    String id = "";
+
+    public String currentRequestId() {
+        return id;
+    }
+
+    PrintWriter stdout;
+    PrintWriter stderr;
+    int forcedExitCode = 0;
+
+    public void logError(String msg) {
+        stderr.println(msg);
+        forcedExitCode = -1;
+    }
+
+    /**
+     * Invoked by the executor service.
+     */
+    public void run() {
+        // Unique nr that identifies this request.
+        int thisRequest = compilerPool.startRequest();
+        long start = System.currentTimeMillis();
+        int numClasses = 0;
+        StringBuilder compiledPkgs = new StringBuilder();
+        use();
+
+        PrintWriter out = null;
+        try {
+            javacServer.log("<"+thisRequest+"> Connect from "+socket.getRemoteSocketAddress()+" activethreads="+compilerPool.numActiveRequests());
+            BufferedReader in = new BufferedReader(new InputStreamReader(
+                                                       socket.getInputStream()));
+            out = new PrintWriter(new OutputStreamWriter(
+                                                  socket.getOutputStream()));
+            if (!expect(in, JavacServer.PROTOCOL_COOKIE_VERSION)) {
+                javacServer.log("<"+thisRequest+"> Bad protocol from ip "+socket.getRemoteSocketAddress());
+                return;
+            }
+
+            String cookie = in.readLine();
+            if (cookie == null || !cookie.equals(""+javacServer.getCookie())) {
+                javacServer.log("<"+thisRequest+"> Bad cookie from ip "+socket.getRemoteSocketAddress());
+                return;
+            }
+            if (!expect(in, JavacServer.PROTOCOL_CWD)) {
+                return;
+            }
+            String cwd = in.readLine();
+            if (cwd == null)
+                return;
+            if (!expect(in, JavacServer.PROTOCOL_ID)) {
+                return;
+            }
+            id = in.readLine();
+            if (id == null)
+                return;
+            if (!expect(in, JavacServer.PROTOCOL_ARGS)) {
+                return;
+            }
+            ArrayList<String> the_options = new ArrayList<String>();
+            ArrayList<File> the_classes = new ArrayList<File>();
+            Iterable<File> path = Arrays.<File> asList(new File(cwd));
+
+            for (;;) {
+                String l = in.readLine();
+                if (l == null)
+                    return;
+                if (l.equals(JavacServer.PROTOCOL_SOURCES_TO_COMPILE))
+                    break;
+                if (l.startsWith("--server:"))
+                    continue;
+                if (!l.startsWith("-") && l.endsWith(".java")) {
+                    the_classes.add(new File(l));
+                    numClasses++;
+                } else {
+                    the_options.add(l);
+                }
+                continue;
+            }
+
+            // Load sources to compile
+            Set<URI> sourcesToCompile = new HashSet<URI>();
+            for (;;) {
+                String l = in.readLine();
+                if (l == null)
+                    return;
+                if (l.equals(JavacServer.PROTOCOL_VISIBLE_SOURCES))
+                    break;
+                try {
+                    sourcesToCompile.add(new URI(l));
+                    numClasses++;
+                } catch (URISyntaxException e) {
+                    return;
+                }
+            }
+            // Load visible sources
+            Set<URI> visibleSources = new HashSet<URI>();
+            boolean fix_drive_letter_case = System.getProperty("os.name").toLowerCase().equals("windows");
+            for (;;) {
+                String l = in.readLine();
+                if (l == null)
+                    return;
+                if (l.equals(JavacServer.PROTOCOL_END))
+                    break;
+                try {
+                    URI u = new URI(l);
+                    if (fix_drive_letter_case) {
+                        // Make sure the driver letter is lower case.
+                        String s = u.toString();
+                        if (s.startsWith("file:/") &&
+                            Character.isUpperCase(s.charAt(6))) {
+                            u = new URI("file:/"+Character.toLowerCase(s.charAt(6))+s.substring(7));
+                        }
+                    }
+                    visibleSources.add(u);
+                } catch (URISyntaxException e) {
+                    return;
+                }
+            }
+
+            // A completed request has been received.
+
+            // Now setup the actual compilation....
+            // First deal with explicit source files on cmdline and in at file.
+            com.sun.tools.javac.util.ListBuffer<JavaFileObject> compilationUnits =
+                new com.sun.tools.javac.util.ListBuffer<JavaFileObject>();
+            for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(the_classes)) {
+                compilationUnits.append(i);
+            }
+            // Now deal with sources supplied as source_to_compile.
+            com.sun.tools.javac.util.ListBuffer<File> sourcesToCompileFiles =
+                new com.sun.tools.javac.util.ListBuffer<File>();
+            for (URI u : sourcesToCompile) {
+                sourcesToCompileFiles.append(new File(u));
+            }
+            for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
+                compilationUnits.append(i);
+            }
+            // Log the options to be used.
+            StringBuilder options = new StringBuilder();
+            for (String s : the_options) {
+                options.append(">").append(s).append("< ");
+            }
+            javacServer.log(id+" <"+thisRequest+"> options "+options.toString());
+
+            forcedExitCode = 0;
+            // Create a new logger.
+            StringWriter stdoutLog = new StringWriter();
+            StringWriter stderrLog = new StringWriter();
+            stdout = new PrintWriter(stdoutLog);
+            stderr = new PrintWriter(stderrLog);
+            com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
+            try {
+                if (compilationUnits.size() > 0) {
+                    // Bind the new logger to the existing context.
+                    context.put(Log.outKey, stderr);
+                    Log.instance(context).setWriter(Log.WriterKind.NOTICE, stdout);
+                    Log.instance(context).setWriter(Log.WriterKind.WARNING, stderr);
+                    Log.instance(context).setWriter(Log.WriterKind.ERROR, stderr);
+                    // Process the options.
+                    com.sun.tools.javac.api.JavacTool.processOptions(context, smartFileManager, the_options);
+                    fileManagerBase.setContext(context);
+                    smartFileManager.setVisibleSources(visibleSources);
+                    smartFileManager.cleanArtifacts();
+                    smartFileManager.setLog(stdout);
+                    Dependencies.instance(context).reset();
+
+                    com.sun.tools.javac.main.Main ccompiler = new com.sun.tools.javac.main.Main("javacTask", stderr);
+                    String[] aa = the_options.toArray(new String[0]);
+
+                    // Do the compilation!
+                    rc = ccompiler.compile(aa, context, compilationUnits.toList(), null);
+
+                    while (numActiveSubTasks()>0) {
+                        try { Thread.sleep(1000); } catch (InterruptedException e) { }
+                    }
+
+                    smartFileManager.flush();
+                }
+            } catch (Exception e) {
+                stderr.println(e.getMessage());
+                forcedExitCode = -1;
+            }
+
+            // Send the response..
+            out.println(JavacServer.PROTOCOL_STDOUT);
+            out.print(stdoutLog);
+            out.println(JavacServer.PROTOCOL_STDERR);
+            out.print(stderrLog);
+            // The compilation is complete! And errors will have already been printed on out!
+            out.println(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS);
+            Map<String,Set<URI>> pa = smartFileManager.getPackageArtifacts();
+            for (String aPkgName : pa.keySet()) {
+                out.println("+"+aPkgName);
+                Set<URI> as = pa.get(aPkgName);
+                for (URI a : as) {
+                    out.println(" "+a.toString());
+                }
+            }
+            Dependencies deps = Dependencies.instance(context);
+            out.println(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES);
+            Map<String,Set<String>> pd = deps.getDependencies();
+            for (String aPkgName : pd.keySet()) {
+                out.println("+"+aPkgName);
+                Set<String> ds = pd.get(aPkgName);
+                    // Everything depends on java.lang
+                    if (!ds.contains(":java.lang")) ds.add(":java.lang");
+                for (String d : ds) {
+                    out.println(" "+d);
+                }
+            }
+            out.println(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS);
+            Map<String,String> pp = deps.getPubapis();
+            for (String aPkgName : pp.keySet()) {
+                out.println("+"+aPkgName);
+                String ps = pp.get(aPkgName);
+                // getPubapis added a space to each line!
+                out.println(ps);
+                compiledPkgs.append(aPkgName+" ");
+            }
+            out.println(JavacServer.PROTOCOL_SYSINFO);
+            out.println("num_cores=" + Runtime.getRuntime().availableProcessors());
+            out.println("max_memory=" + Runtime.getRuntime().maxMemory());
+            out.println(JavacServer.PROTOCOL_RETURN_CODE);
+
+            // Errors from sjavac that affect compilation status!
+            int rcv = rc.exitCode;
+            if (rcv == 0 && forcedExitCode != 0) {
+                rcv = forcedExitCode;
+            }
+            out.println("" + rcv);
+            out.println(JavacServer.PROTOCOL_END);
+            out.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (out != null) out.close();
+                if (!socket.isClosed()) {
+                    socket.close();
+                }
+                socket = null;
+            } catch (Exception e) {
+                javacServer.log("ERROR "+e);
+                e.printStackTrace();
+            }
+            compilerPool.stopRequest();
+            long duration = System.currentTimeMillis()-start;
+            javacServer.addBuildTime(duration);
+            float classpersec = ((float)numClasses)*(((float)1000.0)/((float)duration));
+            javacServer.log(id+" <"+thisRequest+"> "+compiledPkgs+" duration " + duration+ " ms    num_classes="+numClasses+
+                             "     classpersec="+classpersec+" subtasks="+subTasks.size());
+            javacServer.flushLog();
+            unuse();
+            compilerPool.returnCompilerThread(this);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) 2011-2012, 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.server;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Random;
+
+import com.sun.tools.sjavac.Util;
+import com.sun.tools.sjavac.ProblemException;
+import java.io.*;
+import java.util.*;
+
+/**
+ * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
+ *
+ * <p><b>This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are
+ * subject to change or deletion without notice.</b></p>
+ */
+public class JavacServer {
+    // Responding to this tcp/ip port on localhost.
+
+    private final ServerSocket serverSocket;
+    // The secret cookie shared between server and client through the port file.
+    private final long myCookie;
+    // When the server was started.
+    private long serverStart;
+    // Accumulated build time for all requests, not counting idle time.
+    private long totalBuildTime;
+    // The javac server specific log file.
+    PrintWriter theLog;
+    // The compiler pool that maintains the compiler threads.
+    CompilerPool compilerPool;
+    // For the client, all port files fetched, one per started javac server.
+    // Though usually only one javac server is started by a client.
+    private static Map<String, PortFile> allPortFiles;
+    private static Map<String, Long> maxServerMemory;
+    final static int ERROR_FATAL = -1;
+    final static int ERROR_BUT_TRY_AGAIN = -4712;
+    final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
+    final static String PROTOCOL_CWD = "----THE-CWD----";
+    final static String PROTOCOL_ID = "----THE-ID----";
+    final static String PROTOCOL_ARGS = "----THE-ARGS----";
+    final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----";
+    final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----";
+    final static String PROTOCOL_END = "----THE-END----";
+    final static String PROTOCOL_STDOUT = "----THE-STDOUT----";
+    final static String PROTOCOL_STDERR = "----THE-STDERR----";
+    final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----";
+    final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----";
+    final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----";
+    final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----";
+    final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----";
+    // Check if the portfile is gone, every 5 seconds.
+    static int CHECK_PORTFILE_INTERVAL = 5;
+    // Wait 2 seconds for response, before giving up on javac server.
+    static int CONNECTION_TIMEOUT = 2;
+    static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1;
+    static int MAX_NUM_CONNECT_ATTEMPTS = 3;
+
+    /**
+     * Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
+     */
+    private static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
+        if (allPortFiles == null) {
+            allPortFiles = new HashMap<String, PortFile>();
+        }
+        PortFile pf = allPortFiles.get(filename);
+        if (pf == null) {
+            pf = new PortFile(filename);
+            allPortFiles.put(filename, pf);
+        }
+        return pf;
+    }
+
+    /**
+     * Get the cookie used for this server.
+     */
+    long getCookie() {
+        return myCookie;
+    }
+
+    /**
+     * Get the port used for this server.
+     */
+    int getPort() {
+        return serverSocket.getLocalPort();
+    }
+
+    /**
+     * Sum up the total build time for this javac server.
+     */
+    public void addBuildTime(long inc) {
+        totalBuildTime += inc;
+    }
+
+    /**
+     * Log this message.
+     */
+    public void log(String msg) {
+        if (theLog != null) {
+            theLog.println(msg);
+        } else {
+            System.err.println(msg);
+        }
+    }
+
+    /**
+     * Make sure the log is flushed.
+     */
+    public void flushLog() {
+        if (theLog != null) {
+            theLog.flush();
+        }
+    }
+
+    /**
+     * Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
+     * is sent as the settings parameter. Returns 0 on success, -1 on failure.
+     */
+    public static int startServer(String settings, PrintStream err) {
+        try {
+            String portfile = Util.extractStringOption("portfile", settings);
+            // The log file collects more javac server specific log information.
+            String logfile = Util.extractStringOption("logfile", settings);
+            // The stdouterr file collects all the System.out and System.err writes to disk.
+            String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
+            // We could perhaps use System.setOut and setErr here.
+            // But for the moment we rely on the client to spawn a shell where stdout
+            // and stderr are redirected already.
+            // The pool size is a limit the number of concurrent compiler threads used.
+            // The server might use less than these to avoid memory problems.
+            int poolsize = Util.extractIntOption("poolsize", settings);
+            if (poolsize <= 0) {
+                // If not set, default to the number of cores.
+                poolsize = Runtime.getRuntime().availableProcessors();
+            }
+
+            // How many seconds of inactivity will the server accept before quitting?
+            int keepalive = Util.extractIntOption("keepalive", settings);
+            if (keepalive <= 0) {
+                keepalive = 120;
+            }
+            // The port file is locked and the server port and cookie is written into it.
+            PortFile portFile = getPortFile(portfile);
+            JavacServer s;
+
+            synchronized (portFile) {
+                portFile.lock();
+                portFile.getValues();
+                if (portFile.containsPortInfo()) {
+                    err.println("Javac server not started because portfile exists!");
+                    portFile.unlock();
+                    return -1;
+                }
+                s = new JavacServer(poolsize, logfile);
+                portFile.setValues(s.getPort(), s.getCookie());
+                portFile.unlock();
+            }
+
+            // Run the server. Will delete the port file when shutting down.
+            // It will shut down automatically when no new requests have come in
+            // during the last 125 seconds.
+            s.run(portFile, err, keepalive);
+            // The run loop for the server has exited.
+            return 0;
+        } catch (Exception e) {
+            e.printStackTrace(err);
+            return -1;
+        }
+    }
+
+    /**
+     * Dispatch a compilation request to a javac server.
+     *
+     * @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac.
+     *
+     * The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk.
+     *
+     * @param sources_to_compile The sources to compile.
+     *
+     * @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see!
+     * (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath
+     *
+     * @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server
+     * can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar.
+     *
+     * The server return meta data about the build in the following parameters.
+     * @param package_artifacts, map from package name to set of created artifacts for that package.
+     * @param package_dependencies, map from package name to set of packages that it depends upon.
+     * @param package_pubapis, map from package name to unique string identifying its pub api.
+     */
+    public static int useServer(String settings, String[] args,
+            Set<URI> sourcesToCompile,
+            Set<URI> visibleSources,
+            Map<URI, Set<String>> visibleClasses,
+            Map<String, Set<URI>> packageArtifacts,
+            Map<String, Set<String>> packageDependencies,
+            Map<String, String> packagePubapis,
+            SysInfo sysinfo,
+            PrintStream out,
+            PrintStream err) {
+        try {
+            // The id can perhaps be used in the future by the javac server to reuse the
+            // JavaCompiler instance for several compiles using the same id.
+            String id = Util.extractStringOption("id", settings);
+            String portfile = Util.extractStringOption("portfile", settings);
+            String logfile = Util.extractStringOption("logfile", settings);
+            String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
+            String background = Util.extractStringOption("background", settings);
+            if (background == null || !background.equals("false")) {
+                background = "true";
+            }
+            // The sjavac option specifies how the server part of sjavac is spawned.
+            // If you have the experimental sjavac in your path, you are done. If not, you have
+            // to point to a com.sun.tools.sjavac.Main that supports --startserver
+            // for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
+            String sjavac = Util.extractStringOption("sjavac", settings);
+            int poolsize = Util.extractIntOption("poolsize", settings);
+            int keepalive = Util.extractIntOption("keepalive", settings);
+
+            if (keepalive <= 0) {
+                // Default keepalive for server is 120 seconds.
+                // I.e. it will accept 120 seconds of inactivity before quitting.
+                keepalive = 120;
+            }
+            if (portfile == null) {
+                err.println("No portfile was specified!");
+                return -1;
+            }
+            if (logfile == null) {
+                logfile = portfile + ".javaclog";
+            }
+            if (stdouterrfile == null) {
+                stdouterrfile = portfile + ".stdouterr";
+            }
+            // Default to sjavac and hope it is in the path.
+            if (sjavac == null) {
+                sjavac = "sjavac";
+            }
+
+            int attempts = 0;
+            int rc = -1;
+            do {
+                PortFile port_file = getPortFile(portfile);
+                synchronized (port_file) {
+                    port_file.lock();
+                    port_file.getValues();
+                    port_file.unlock();
+                }
+                if (!port_file.containsPortInfo()) {
+                    String cmd = fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, err, stdouterrfile, background);
+
+                    if (background.equals("true") && !port_file.waitForValidValues()) {
+                        // Ouch the server did not start! Lets print its stdouterrfile and the command used.
+                        printFailedAttempt(cmd, stdouterrfile, err);
+                        // And give up.
+                        return -1;
+                    }
+                }
+                rc = connectAndCompile(port_file, id, args, sourcesToCompile, visibleSources,
+                        packageArtifacts, packageDependencies, packagePubapis, sysinfo,
+                        out, err);
+                // Try again until we manage to connect. Any error after that
+                // will cause the compilation to fail.
+                if (rc == ERROR_BUT_TRY_AGAIN) {
+                    // We could not connect to the server. Try again.
+                    attempts++;
+                    try {
+                        Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
+                    } catch (InterruptedException e) {
+                    }
+                }
+            } while (rc == ERROR_BUT_TRY_AGAIN && attempts < MAX_NUM_CONNECT_ATTEMPTS);
+            return rc;
+        } catch (Exception e) {
+            e.printStackTrace(err);
+            return -1;
+        }
+    }
+
+    private static void printFailedAttempt(String cmd, String f, PrintStream err) {
+        err.println("---- Failed to start javac server with this command -----");
+        err.println(cmd);
+        try {
+            BufferedReader in = new BufferedReader(new FileReader(f));
+            err.println("---- stdout/stderr output from attempt to start javac server -----");
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    break;
+                }
+                err.println(l);
+            }
+            err.println("------------------------------------------------------------------");
+        } catch (Exception e) {
+            err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
+        }
+    }
+
+    /**
+     * Spawn the server instance.
+     */
+
+    private JavacServer(int poolSize, String logfile) throws IOException {
+        serverStart = System.currentTimeMillis();
+        // Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface.
+        // I.e only local processes can connect to this port.
+        serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null));
+        compilerPool = new CompilerPool(poolSize, this);
+        Random rnd = new Random();
+        myCookie = rnd.nextLong();
+        theLog = new PrintWriter(logfile);
+        log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize);
+        flushLog();
+    }
+
+    /**
+     * Fork a background process. Returns the command line used that can be printed if something failed.
+     */
+    private static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
+            final PrintStream err, String stdouterrfile, String background)
+            throws IOException, ProblemException {
+        if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
+            stdouterrfile = null;
+        }
+        final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
+
+        if (background.equals("true")) {
+            sjavac += "%20" + startserver;
+            sjavac = sjavac.replaceAll("%20", " ");
+            sjavac = sjavac.replaceAll("%2C", ",");
+            // If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
+            String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
+            if (!(new File("/bin/sh")).canExecute()) {
+                ArrayList<String> wincmd = new ArrayList<String>();
+                wincmd.add("cmd");
+                wincmd.add("/c");
+                wincmd.add("start");
+                wincmd.add("cmd");
+                wincmd.add("/c");
+                wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
+                cmd = wincmd.toArray(new String[wincmd.size()]);
+            }
+            Process pp = null;
+            try {
+                pp = Runtime.getRuntime().exec(cmd);
+            } catch (Exception e) {
+                e.printStackTrace(err);
+                e.printStackTrace(new PrintWriter(stdouterrfile));
+            }
+            StringBuilder rs = new StringBuilder();
+            for (String s : cmd) {
+                rs.append(s + " ");
+            }
+            return rs.toString();
+        }
+
+        // Do not spawn a background server, instead run it within the same JVM.
+        Thread t = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    JavacServer.startServer(startserver, err);
+                } catch (Throwable t) {
+                    t.printStackTrace(err);
+                }
+            }
+        };
+        t.start();
+        return "";
+    }
+
+    /**
+     * Expect this key on the next line read from the reader.
+     */
+    private static boolean expect(BufferedReader in, String key) throws IOException {
+        String s = in.readLine();
+        if (s != null && s.equals(key)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Make a request to the server only to get the maximum possible heap size to use for compilations.
+     *
+     * @param port_file The port file used to synchronize creation of this server.
+     * @param id The identify of the compilation.
+     * @param out Standard out information.
+     * @param err Standard err information.
+     * @return The maximum heap size in bytes.
+     */
+    public static SysInfo connectGetSysInfo(String serverSettings, PrintStream out, PrintStream err) {
+        SysInfo sysinfo = new SysInfo(-1, -1);
+        String id = Util.extractStringOption("id", serverSettings);
+        String portfile = Util.extractStringOption("portfile", serverSettings);
+        try {
+            PortFile pf = getPortFile(portfile);
+            useServer(serverSettings, new String[0],
+                    new HashSet<URI>(),
+                    new HashSet<URI>(),
+                    new HashMap<URI, Set<String>>(),
+                    new HashMap<String, Set<URI>>(),
+                    new HashMap<String, Set<String>>(),
+                    new HashMap<String, String>(),
+                    sysinfo, out, err);
+        } catch (Exception e) {
+            e.printStackTrace(err);
+        }
+        return sysinfo;
+    }
+
+    /**
+     * Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are
+     * supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis.
+     */
+    private static int connectAndCompile(PortFile portFile, String id, String[] args,
+            Set<URI> sourcesToCompile,
+            Set<URI> visibleSources,
+            Map<String, Set<URI>> packageArtifacts,
+            Map<String, Set<String>> packageDependencies,
+            Map<String, String> packagePublicApis,
+            SysInfo sysinfo,
+            PrintStream out,
+            PrintStream err) {
+        int rc = -3;
+        try {
+            int port = portFile.getPort();
+            if (port == 0) {
+                return ERROR_BUT_TRY_AGAIN;
+            }
+            long cookie = portFile.getCookie();
+
+            // Acquire the localhost/127.0.0.1 address.
+            InetAddress addr = InetAddress.getByName(null);
+            SocketAddress sockaddr = new InetSocketAddress(addr, port);
+            Socket sock = new Socket();
+            int timeoutMs = CONNECTION_TIMEOUT * 1000;
+            try {
+                sock.connect(sockaddr, timeoutMs);
+            } catch (java.net.ConnectException e) {
+                err.println("Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e);
+                return ERROR_BUT_TRY_AGAIN;
+            }
+            if (!sock.isConnected()) {
+                err.println("Could not connect to javac server found in portfile: " + portFile.getFilename());
+                return ERROR_BUT_TRY_AGAIN;
+            }
+            BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+            PrintWriter sockout = new PrintWriter(sock.getOutputStream());
+
+            sockout.println(PROTOCOL_COOKIE_VERSION);
+            sockout.println("" + cookie);
+            sockout.println(PROTOCOL_CWD);
+            sockout.println(System.getProperty("user.dir"));
+            sockout.println(PROTOCOL_ID);
+            sockout.println(id);
+            sockout.println(PROTOCOL_ARGS);
+            for (String s : args) {
+                StringBuffer buf = new StringBuffer();
+                String[] paths = s.split(File.pathSeparator);
+                int c = 0;
+                for (String path : paths) {
+                    File f = new File(path);
+                    if (f.isFile() || f.isDirectory()) {
+                        buf.append(f.getAbsolutePath());
+                        c++;
+                        if (c < paths.length) {
+                            buf.append(File.pathSeparator);
+                        }
+                    } else {
+                        buf = new StringBuffer(s);
+                        break;
+                    }
+                }
+                sockout.println(buf.toString());
+            }
+            sockout.println(PROTOCOL_SOURCES_TO_COMPILE);
+            for (URI uri : sourcesToCompile) {
+                sockout.println(uri.toString());
+            }
+            sockout.println(PROTOCOL_VISIBLE_SOURCES);
+            for (URI uri : visibleSources) {
+                sockout.println(uri.toString());
+            }
+            sockout.println(PROTOCOL_END);
+            sockout.flush();
+
+            StringBuffer stdout = new StringBuffer();
+            StringBuffer stderr = new StringBuffer();
+
+            if (!expect(in, PROTOCOL_STDOUT)) {
+                return ERROR_FATAL;
+            }
+            // Load stdout
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_STDERR)) {
+                    break;
+                }
+                stdout.append(l);
+                stdout.append('\n');
+            }
+            // Load stderr
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_PACKAGE_ARTIFACTS)) {
+                    break;
+                }
+                stderr.append(l);
+                stderr.append('\n');
+            }
+            // Load the package artifacts
+            Set<URI> lastUriSet = null;
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_PACKAGE_DEPENDENCIES)) {
+                    break;
+                }
+                if (l.length() > 1 && l.charAt(0) == '+') {
+                    String pkg = l.substring(1);
+                    lastUriSet = new HashSet<URI>();
+                    packageArtifacts.put(pkg, lastUriSet);
+                } else if (l.length() > 1 && lastUriSet != null) {
+                    lastUriSet.add(new URI(l.substring(1)));
+                }
+            }
+            // Load package dependencies
+            Set<String> lastPackageSet = null;
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_PACKAGE_PUBLIC_APIS)) {
+                    break;
+                }
+                if (l.length() > 1 && l.charAt(0) == '+') {
+                    String pkg = l.substring(1);
+                    lastPackageSet = new HashSet<String>();
+                    packageDependencies.put(pkg, lastPackageSet);
+                } else if (l.length() > 1 && lastPackageSet != null) {
+                    lastPackageSet.add(l.substring(1));
+                }
+            }
+            // Load package pubapis
+            Map<String, StringBuffer> tmp = new HashMap<String, StringBuffer>();
+            StringBuffer lastPublicApi = null;
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_SYSINFO)) {
+                    break;
+                }
+                if (l.length() > 1 && l.charAt(0) == '+') {
+                    String pkg = l.substring(1);
+                    lastPublicApi = new StringBuffer();
+                    tmp.put(pkg, lastPublicApi);
+                } else if (l.length() > 1 && lastPublicApi != null) {
+                    lastPublicApi.append(l.substring(1));
+                    lastPublicApi.append("\n");
+                }
+            }
+            for (String p : tmp.keySet()) {
+                assert (packagePublicApis.get(p) == null);
+                String api = tmp.get(p).toString();
+                packagePublicApis.put(p, api);
+            }
+            // Now reading the max memory possible.
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    return ERROR_FATAL;
+                }
+                if (l.equals(PROTOCOL_RETURN_CODE)) {
+                    break;
+                }
+                if (l.startsWith("num_cores=") && sysinfo != null) {
+                    sysinfo.numCores = Integer.parseInt(l.substring(10));
+                }
+                if (l.startsWith("max_memory=") && sysinfo != null) {
+                    sysinfo.maxMemory = Long.parseLong(l.substring(11));
+                }
+            }
+            String l = in.readLine();
+            if (l == null) {
+                err.println("No return value from the server!");
+                return ERROR_FATAL;
+            }
+            rc = Integer.parseInt(l);
+            out.print(stdout);
+            err.print(stderr);
+        } catch (Exception e) {
+            e.printStackTrace(err);
+        }
+        return rc;
+    }
+
+    /**
+     * Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other
+     * javac server.
+     */
+    private void run(PortFile portFile, PrintStream err, int keepalive) {
+        boolean fileDeleted = false;
+        long timeSinceLastCompile;
+        try {
+            // Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit
+            // Or if the last request was finished more than 125 seconds ago => quit
+            // 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval
+            serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000);
+            for (;;) {
+                try {
+                    Socket s = serverSocket.accept();
+                    CompilerThread ct = compilerPool.grabCompilerThread();
+                    ct.setSocket(s);
+                    compilerPool.execute(ct);
+                    flushLog();
+                } catch (java.net.SocketTimeoutException e) {
+                    if (compilerPool.numActiveRequests() > 0) {
+                        // Never quit while there are active requests!
+                        continue;
+                    }
+                    // If this is the timeout after the portfile
+                    // has been deleted by us. Then we truly stop.
+                    if (fileDeleted) {
+                        log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!");
+                        break;
+                    }
+                    // Check if the portfile is still there.
+                    if (!portFile.exists()) {
+                        // Time to quit because the portfile was deleted by another
+                        // process, probably by the makefile that is done building.
+                        log("Quitting because portfile was deleted!");
+                        flushLog();
+                        break;
+                    }
+                    // Check if portfile.stop is still there.
+                    if (portFile.markedForStop()) {
+                        // Time to quit because another process touched the file
+                        // server.port.stop to signal that the server should stop.
+                        // This is necessary on some operating systems that lock
+                        // the port file hard!
+                        log("Quitting because a portfile.stop file was found!");
+                        portFile.delete();
+                        flushLog();
+                        break;
+                    }
+                    // Does the portfile still point to me?
+                    if (!portFile.stillMyValues()) {
+                        // Time to quit because another build has started.
+                        log("Quitting because portfile is now owned by another javac server!");
+                        flushLog();
+                        break;
+                    }
+
+                    // Check how long since the last request finished.
+                    long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished();
+                    if (diff < keepalive * 1000) {
+                        // Do not quit if we have waited less than 120 seconds.
+                        continue;
+                    }
+                    // Ok, time to quit because of inactivity. Perhaps the build
+                    // was killed and the portfile not cleaned up properly.
+                    portFile.delete();
+                    fileDeleted = true;
+                    log("" + keepalive + " seconds of inactivity quitting in "
+                        + CHECK_PORTFILE_INTERVAL + " seconds!");
+                    flushLog();
+                    // Now we have a second 5 second grace
+                    // period where javac remote requests
+                    // that have loaded the data from the
+                    // recently deleted portfile can connect
+                    // and complete their requests.
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(err);
+            e.printStackTrace(theLog);
+            flushLog();
+        } finally {
+            compilerPool.shutdown();
+        }
+        long realTime = System.currentTimeMillis() - serverStart;
+        log("Shutting down.");
+        log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
+        flushLog();
+    }
+
+    public static void cleanup(String... args) {
+        String settings = Util.findServerSettings(args);
+        if (settings == null) return;
+        String portfile = Util.extractStringOption("portfile", settings);
+        String background = Util.extractStringOption("background", settings);
+        if (background != null && background.equals("false")) {
+            // If the server runs within this jvm, then delete the portfile,
+            // since this jvm is about to exit soon.
+            File f = new File(portfile);
+            f.delete();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/PortFile.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2012, 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.server;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.FileLockInterruptionException;
+import com.sun.tools.sjavac.Log;
+
+/**
+ * The PortFile class mediates access to a short binary file containing the tcp/ip port (for the localhost)
+ * and a cookie necessary for the server answering on that port. The file can be locked using file system
+ * primitives to avoid race conditions when several javac clients are started at the same. Note that file
+ * system locking is not always supported on a all operating systems and/or file systems.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+class PortFile {
+
+    // Port file format:
+    // byte ordering: high byte first = big endian
+    // Magic nr, 4 byte int, first in file.
+    private final static int magicNr = 0x1174;
+    // Followed by a 4 byte int, with the port nr.
+    // Followed by a 8 byte long, with cookie nr.
+
+    private String filename;
+    private File file;
+    private File stopFile;
+    private RandomAccessFile rwfile;
+    private FileChannel channel;
+    private FileLock lock;
+
+    private boolean containsPortInfo;
+    private int serverPort;
+    private long serverCookie;
+    private int myServerPort;
+    private long myServerCookie;
+
+    /**
+     * Create a new portfile.
+     * @param filename is the path to the file.
+     */
+    public PortFile(String fn) throws FileNotFoundException
+    {
+        filename = fn;
+        file = new File(filename);
+        stopFile = new File(filename+".stop");
+        rwfile = new RandomAccessFile(file, "rw");
+        // The rwfile should only be readable by the owner of the process
+        // and no other! How do we do that on a RandomAccessFile?
+        channel = rwfile.getChannel();
+        containsPortInfo = false;
+        lock = null;
+    }
+
+    /**
+     * Lock the port file.
+     */
+    void lock() throws IOException {
+        lock = channel.lock();
+    }
+
+    /**
+     * Read the values from the port file in the file system.
+     * Expects the port file to be locked.
+     */
+    public void getValues()  {
+        containsPortInfo = false;
+        if (lock == null) {
+            // Not locked, remain ignorant about port file contents.
+            return;
+        }
+        try {
+            if (rwfile.length()>0) {
+                rwfile.seek(0);
+                int nr = rwfile.readInt();
+                serverPort = rwfile.readInt();
+                serverCookie = rwfile.readLong();
+
+                if (nr == magicNr) {
+                    containsPortInfo = true;
+                } else {
+                    containsPortInfo = false;
+                }
+            }
+        } catch (Exception e) {
+            containsPortInfo = false;
+        }
+    }
+
+    /**
+     * Did the locking and getValues succeed?
+     */
+    public boolean containsPortInfo() {
+        return containsPortInfo;
+    }
+
+    /**
+     * If so, then we can acquire the tcp/ip port on localhost.
+     */
+    public int getPort() {
+        assert(containsPortInfo);
+        return serverPort;
+    }
+
+    /**
+     * If so, then we can acquire the server cookie.
+     */
+    public long getCookie() {
+        assert(containsPortInfo);
+        return serverCookie;
+    }
+
+    /**
+     * Store the values into the locked port file.
+     */
+    public void setValues(int port, long cookie) throws IOException {
+        assert(lock != null);
+        rwfile.seek(0);
+        // Write the magic nr that identifes a port file.
+        rwfile.writeInt(magicNr);
+        rwfile.writeInt(port);
+        rwfile.writeLong(cookie);
+        myServerPort = port;
+        myServerCookie = cookie;
+    }
+
+    /**
+     * Delete the port file.
+     */
+    public void delete() throws IOException {
+        // Access to file must be closed before deleting.
+        rwfile.close();
+        // Now delete.
+        file.delete();
+    }
+
+    /**
+     * Is the port file still there?
+     */
+    public boolean exists() throws IOException {
+        return file.exists();
+    }
+
+    /**
+     * Is a stop file there?
+     */
+    public boolean markedForStop() throws IOException {
+        if (stopFile.exists()) {
+            try {
+                stopFile.delete();
+            } catch (Exception e)
+            {}
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Unlock the port file.
+     */
+    public void unlock() throws IOException {
+        assert(lock != null);
+        lock.release();
+        lock = null;
+    }
+
+    /**
+     * Wait for the port file to contain values that look valid.
+     * Return true, if a-ok, false if the valid values did not materialize within 5 seconds.
+     */
+    public synchronized boolean waitForValidValues() throws IOException, FileNotFoundException {
+        for (int tries = 0; tries < 50; tries++) {
+            lock();
+            getValues();
+            unlock();
+            if (containsPortInfo) {
+                Log.debug("Found valid values in port file after waiting "+(tries*100)+"ms");
+                return true;
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e)
+            {}
+        }
+        Log.debug("Gave up waiting for valid values in port file");
+        return false;
+    }
+
+    /**
+     * Check if the portfile still contains my values, assuming that I am the server.
+     */
+    public synchronized boolean stillMyValues() throws IOException, FileNotFoundException {
+        for (;;) {
+            try {
+                lock();
+                getValues();
+                unlock();
+                if (containsPortInfo) {
+                    if (serverPort == myServerPort &&
+                        serverCookie == myServerCookie) {
+                        // Everything is ok.
+                        return true;
+                    }
+                    // Someone has overwritten the port file.
+                    // Probably another javac server, lets quit.
+                    return false;
+                }
+                // Something else is wrong with the portfile. Lets quit.
+                return false;
+            } catch (FileLockInterruptionException e) {
+                continue;
+            }
+            catch (ClosedChannelException e) {
+                // The channel has been closed since sjavac is exiting.
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Return the name of the port file.
+     */
+    public String getFilename() {
+        return filename;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/SysInfo.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/**
+ * A utility class used to report information about the system
+ * where the javac server is running.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+package com.sun.tools.sjavac.server;
+
+public class SysInfo {
+    public int numCores;
+    public long maxMemory;
+
+    public SysInfo(int nc, long mm) {
+        numCores = nc;
+        maxMemory = mm;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/sjavac/SJavac.java	Fri Jan 18 00:16:21 2013 +0100
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2013, 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
+ * @summary Test all aspects of sjavac.
+ *
+ * @bug 8004658
+ * @summary Add internal smart javac wrapper to solve JEP 139
+ *
+ * @run main SJavac
+ */
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.charset.*;
+
+import com.sun.tools.sjavac.Main;
+
+public
+class SJavac {
+
+    public static void main(String... args) throws Exception {
+        URL url = SJavac.class.getClassLoader().getResource("com/sun/tools/sjavac/Main.class");
+        if (url == null) {
+            // No sjavac in the classpath.
+            System.out.println("pass by default");
+            return;
+        }
+
+        SJavac s = new SJavac();
+        s.test();
+    }
+
+    FileSystem defaultfs = FileSystems.getDefault();
+
+    // Where to put generated sources that will
+    // test aspects of sjavac, ie JTWork/scratch/gensrc
+    Path gensrc;
+    // More gensrc dirs are used to test merging of serveral source roots.
+    Path gensrc2;
+    Path gensrc3;
+
+    // Where to put compiled classes.
+    Path bin;
+    // Where to put c-header files.
+    Path headers;
+
+    // The sjavac compiler.
+    Main main = new Main();
+
+    // Remember the previous bin and headers state here.
+    Map<String,Long> previous_bin_state;
+    Map<String,Long> previous_headers_state;
+
+    public void test() throws Exception {
+        gensrc = defaultfs.getPath("gensrc");
+        gensrc2 = defaultfs.getPath("gensrc2");
+        gensrc3 = defaultfs.getPath("gensrc3");
+        bin = defaultfs.getPath("bin");
+        headers = defaultfs.getPath("headers");
+
+        Files.createDirectory(gensrc);
+        Files.createDirectory(gensrc2);
+        Files.createDirectory(gensrc3);
+        Files.createDirectory(bin);
+        Files.createDirectory(headers);
+
+        initialCompile();
+        incrementalCompileNoChanges();
+        incrementalCompileDroppingClasses();
+        incrementalCompileWithChange();
+        incrementalCompileDropAllNatives();
+        incrementalCompileAddNative();
+        incrementalCompileChangeNative();
+        compileWithOverrideSource();
+        compileWithInvisibleSources();
+        compileCircularSources();
+
+        delete(gensrc);
+        delete(gensrc2);
+        delete(gensrc3);
+        delete(bin);
+    }
+
+    void initialCompile() throws Exception {
+        System.out.println("\nInitial compile of gensrc.");
+        System.out.println("----------------------------");
+        populate(gensrc,
+            "alfa/AINT.java",
+            "package alfa; public interface AINT { void aint(); }",
+
+            "alfa/A.java",
+            "package alfa; public class A implements AINT { "+
+                 "public final static int DEFINITION = 17; public void aint() { } }",
+
+            "alfa/AA.java",
+            "package alfa;"+
+            "// A package private class, not contributing to the public api.\n"+
+            "class AA {"+
+            "   // A properly nested static inner class.\n"+
+            "    static class AAA { }\n"+
+            "    // A properly nested inner class.\n"+
+            "    class AAAA { }\n"+
+            "    Runnable foo() {\n"+
+            "        // A proper anonymous class.\n"+
+            "        return new Runnable() { public void run() { } };\n"+
+            "    }\n"+
+            "    AAA aaa;\n"+
+            "    AAAA aaaa;\n"+
+            "    AAAAA aaaaa;\n"+
+            "}\n"+
+            "class AAAAA {\n"+
+            "    // A bad auxiliary class, but no one is referencing it\n"+
+            "    // from outside of this source file, therefore it is ok.\n"+
+            "}\n",
+
+            "beta/BINT.java",
+            "package beta;public interface BINT { void foo(); }",
+
+            "beta/B.java",
+            "package beta; import alfa.A; public class B {"+
+            "private int b() { return A.DEFINITION; } native void foo(); }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        previous_bin_state = collectState(bin);
+        previous_headers_state = collectState(headers);
+    }
+
+    void incrementalCompileNoChanges() throws Exception {
+        System.out.println("\nTesting that no change in sources implies no change in binaries.");
+        System.out.println("------------------------------------------------------------------");
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyEqual(new_bin_state, previous_bin_state);
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyEqual(previous_headers_state, new_headers_state);
+    }
+
+    void incrementalCompileDroppingClasses() throws Exception {
+        System.out.println("\nTesting that deleting AA.java deletes all");
+        System.out.println("generated inner class as well as AA.class");
+        System.out.println("-----------------------------------------");
+        removeFrom(gensrc, "alfa/AA.java");
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyThatFilesHaveBeenRemoved(previous_bin_state, new_bin_state,
+                                       "bin/alfa/AA$1.class",
+                                       "bin/alfa/AA$AAAA.class",
+                                       "bin/alfa/AA$AAA.class",
+                                       "bin/alfa/AAAAA.class",
+                                       "bin/alfa/AA.class");
+
+        previous_bin_state = new_bin_state;
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyEqual(previous_headers_state, new_headers_state);
+    }
+
+    void incrementalCompileWithChange() throws Exception {
+        System.out.println("\nNow update the A.java file with a new timestamps and");
+        System.out.println("new final static definition. This should trigger a recompile,");
+        System.out.println("not only of alfa, but also beta.");
+        System.out.println("But check that the generated native header was not updated!");
+        System.out.println("Since we did not modify the native api of B.");
+        System.out.println("-------------------------------------------------------------");
+
+        populate(gensrc,"alfa/A.java",
+                       "package alfa; public class A implements AINT { "+
+                 "public final static int DEFINITION = 18; public void aint() { } private void foo() { } }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+
+        verifyNewerFiles(previous_bin_state, new_bin_state,
+                         "bin/alfa/A.class",
+                         "bin/alfa/AINT.class",
+                         "bin/beta/B.class",
+                         "bin/beta/BINT.class",
+                         "bin/javac_state");
+        previous_bin_state = new_bin_state;
+
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyEqual(new_headers_state, previous_headers_state);
+    }
+
+    void incrementalCompileDropAllNatives() throws Exception {
+        System.out.println("\nNow update the B.java file with one less native method,");
+        System.out.println("ie it has no longer any methods!");
+        System.out.println("Verify that beta_B.h is removed!");
+        System.out.println("---------------------------------------------------------");
+
+        populate(gensrc,"beta/B.java",
+                       "package beta; import alfa.A; public class B {"+
+                       "private int b() { return A.DEFINITION; } }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyNewerFiles(previous_bin_state, new_bin_state,
+                         "bin/beta/B.class",
+                         "bin/beta/BINT.class",
+                         "bin/javac_state");
+        previous_bin_state = new_bin_state;
+
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyThatFilesHaveBeenRemoved(previous_headers_state, new_headers_state,
+                                       "headers/beta_B.h");
+        previous_headers_state = new_headers_state;
+    }
+
+    void incrementalCompileAddNative() throws Exception {
+        System.out.println("\nNow update the B.java file with a final static annotated with @Native.");
+        System.out.println("Verify that beta_B.h is added again!");
+        System.out.println("------------------------------------------------------------------------");
+
+        populate(gensrc,"beta/B.java",
+                       "package beta; import alfa.A; public class B {"+
+                       "private int b() { return A.DEFINITION; } "+
+                 "@java.lang.annotation.Native final static int alfa = 42; }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyNewerFiles(previous_bin_state, new_bin_state,
+                         "bin/beta/B.class",
+                         "bin/beta/BINT.class",
+                         "bin/javac_state");
+        previous_bin_state = new_bin_state;
+
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyThatFilesHaveBeenAdded(previous_headers_state, new_headers_state,
+                                     "headers/beta_B.h");
+        previous_headers_state = new_headers_state;
+    }
+
+    void incrementalCompileChangeNative() throws Exception {
+        System.out.println("\nNow update the B.java file with a new value for the final static"+
+                           " annotated with @Native.");
+        System.out.println("Verify that beta_B.h is rewritten again!");
+        System.out.println("-------------------------------------------------------------------");
+
+        populate(gensrc,"beta/B.java",
+                       "package beta; import alfa.A; public class B {"+
+                       "private int b() { return A.DEFINITION; } "+
+                 "@java.lang.annotation.Native final static int alfa = 43; }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false", "--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyNewerFiles(previous_bin_state, new_bin_state,
+                         "bin/beta/B.class",
+                         "bin/beta/BINT.class",
+                         "bin/javac_state");
+        previous_bin_state = new_bin_state;
+
+        Map<String,Long> new_headers_state = collectState(headers);
+        verifyNewerFiles(previous_headers_state, new_headers_state,
+                         "headers/beta_B.h");
+        previous_headers_state = new_headers_state;
+    }
+
+    void compileWithOverrideSource() throws Exception {
+        System.out.println("\nNow verify that we can override sources to be compiled.");
+        System.out.println("Compile gensrc and gensrc2. However do not compile broken beta.B in gensrc,");
+        System.out.println("only compile ok beta.B in gensrc2.");
+        System.out.println("---------------------------------------------------------------------------");
+
+        delete(gensrc);
+        delete(gensrc2);
+        delete(bin);
+        previous_bin_state = collectState(bin);
+
+        populate(gensrc,"alfa/A.java",
+                 "package alfa; import beta.B; import gamma.C; public class A { B b; C c; }",
+                 "beta/B.java",
+                 "package beta; public class B { broken",
+                 "gamma/C.java",
+                 "package gamma; public class C { }");
+
+        populate(gensrc2,
+                 "beta/B.java",
+                 "package beta; public class B { }");
+
+        compile("-x", "beta", "gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
+                                     "bin/alfa/A.class",
+                                     "bin/beta/B.class",
+                                     "bin/gamma/C.class",
+                                     "bin/javac_state");
+
+        System.out.println("----- Compile with exluded beta went well!");
+        delete(bin);
+        compileExpectFailure("gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
+                             "--server:portfile=testserver,background=false");
+
+        System.out.println("----- Compile without exluded beta failed, as expected! Good!");
+        delete(bin);
+    }
+
+    void compileWithInvisibleSources() throws Exception {
+        System.out.println("\nNow verify that we can make sources invisible to linking (sourcepath).");
+        System.out.println("Compile gensrc and link against gensrc2 and gensrc3, however");
+        System.out.println("gensrc2 contains broken code in beta.B, thus we must exclude that package");
+        System.out.println("fortunately gensrc3 contains a proper beta.B.");
+        System.out.println("------------------------------------------------------------------------");
+
+        // Start with a fresh gensrcs and bin.
+        delete(gensrc);
+        delete(gensrc2);
+        delete(gensrc3);
+        delete(bin);
+        previous_bin_state = collectState(bin);
+
+        populate(gensrc,"alfa/A.java",
+                 "package alfa; import beta.B; import gamma.C; public class A { B b; C c; }");
+        populate(gensrc2,"beta/B.java",
+                 "package beta; public class B { broken",
+                 "gamma/C.java",
+                 "package gamma; public class C { }");
+        populate(gensrc3, "beta/B.java",
+                 "package beta; public class B { }");
+
+        compile("gensrc", "-x", "beta", "-sourcepath", "gensrc2",
+                "-sourcepath", "gensrc3", "-d", "bin", "-h", "headers", "-j", "1",
+                "--server:portfile=testserver,background=false");
+
+        System.out.println("The first compile went well!");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
+                                     "bin/alfa/A.class",
+                                     "bin/javac_state");
+
+        System.out.println("----- Compile with exluded beta went well!");
+        delete(bin);
+        compileExpectFailure("gensrc", "-sourcepath", "gensrc2", "-sourcepath", "gensrc3",
+                             "-d", "bin", "-h", "headers", "-j", "1",
+                             "--server:portfile=testserver,background=false");
+
+        System.out.println("----- Compile without exluded beta failed, as expected! Good!");
+        delete(bin);
+    }
+
+    void compileCircularSources() throws Exception {
+        System.out.println("\nNow verify that circular sources split on multiple cores can be compiled.");
+        System.out.println("---------------------------------------------------------------------------");
+
+        // Start with a fresh gensrcs and bin.
+        delete(gensrc);
+        delete(gensrc2);
+        delete(gensrc3);
+        delete(bin);
+        previous_bin_state = collectState(bin);
+
+        populate(gensrc,"alfa/A.java",
+                 "package alfa; public class A { beta.B b; }",
+                 "beta/B.java",
+                 "package beta; public class B { gamma.C c; }",
+                 "gamma/C.java",
+                 "package gamma; public class C { alfa.A a; }");
+
+        compile("gensrc", "-d", "bin", "-h", "headers", "-j", "3",
+                "--server:portfile=testserver,background=false","--log=debug");
+        Map<String,Long> new_bin_state = collectState(bin);
+        verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
+                                     "bin/alfa/A.class",
+                                     "bin/beta/B.class",
+                                     "bin/gamma/C.class",
+                                     "bin/javac_state");
+        delete(bin);
+    }
+
+    void removeFrom(Path dir, String... args) throws IOException {
+        for (String filename : args) {
+            Path p = dir.resolve(filename);
+            Files.delete(p);
+        }
+    }
+
+    void populate(Path src, String... args) throws IOException {
+        if (!Files.exists(src)) {
+            Files.createDirectory(src);
+        }
+        String[] a = args;
+        for (int i = 0; i<a.length; i+=2) {
+            String filename = a[i];
+            String content = a[i+1];
+            Path p = src.resolve(filename);
+            Files.createDirectories(p.getParent());
+            PrintWriter out = new PrintWriter(Files.newBufferedWriter(p,
+                                                                      Charset.defaultCharset()));
+            out.println(content);
+            out.close();
+        }
+    }
+
+    void delete(Path root) throws IOException {
+        if (!Files.exists(root)) return;
+        Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
+                 @Override
+                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
+                 {
+                     Files.delete(file);
+                     return FileVisitResult.CONTINUE;
+                 }
+
+                 @Override
+                 public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException
+                 {
+                     if (e == null) {
+                         if (!dir.equals(root)) Files.delete(dir);
+                         return FileVisitResult.CONTINUE;
+                     } else {
+                         // directory iteration failed
+                         throw e;
+                     }
+                 }
+            });
+    }
+
+    void compile(String... args) throws Exception {
+        int rc = main.go(args, System.out, System.err);
+        if (rc != 0) throw new Exception("Error during compile!");
+
+        // Wait a second, to get around the (temporary) problem with
+        // second resolution in the Java file api. But do not do this
+        // on windows where the timestamps work.
+        long in_a_sec = System.currentTimeMillis()+1000;
+        while (in_a_sec > System.currentTimeMillis()) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    void compileExpectFailure(String... args) throws Exception {
+        int rc = main.go(args, System.out, System.err);
+        if (rc == 0) throw new Exception("Expected error during compile! Did not fail!");
+    }
+
+    Map<String,Long> collectState(Path dir) throws IOException
+    {
+        final Map<String,Long> files = new HashMap<>();
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+                 @Override
+                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                   throws IOException
+                 {
+                     files.put(file.toString(),new Long(Files.getLastModifiedTime(file).toMillis()));
+                     return FileVisitResult.CONTINUE;
+                 }
+            });
+        return files;
+    }
+
+    void verifyThatFilesHaveBeenRemoved(Map<String,Long> from,
+                                        Map<String,Long> to,
+                                        String... args) throws Exception {
+
+        Set<String> froms = from.keySet();
+        Set<String> tos = to.keySet();
+
+        if (froms.equals(tos)) {
+            throw new Exception("Expected new state to have fewer files than previous state!");
+        }
+
+        for (String t : tos) {
+            if (!froms.contains(t)) {
+                throw new Exception("Expected "+t+" to exist in previous state!");
+            }
+        }
+
+        for (String f : args) {
+            f = f.replace("/", File.separator);
+            if (!froms.contains(f)) {
+                throw new Exception("Expected "+f+" to exist in previous state!");
+            }
+            if (tos.contains(f)) {
+                throw new Exception("Expected "+f+" to have been removed from the new state!");
+            }
+        }
+
+        if (froms.size() - args.length != tos.size()) {
+            throw new Exception("There are more removed files than the expected list!");
+        }
+    }
+
+    void verifyThatFilesHaveBeenAdded(Map<String,Long> from,
+                                      Map<String,Long> to,
+                                      String... args) throws Exception {
+
+        Set<String> froms = from.keySet();
+        Set<String> tos = to.keySet();
+
+        if (froms.equals(tos)) {
+            throw new Exception("Expected new state to have more files than previous state!");
+        }
+
+        for (String t : froms) {
+            if (!tos.contains(t)) {
+                throw new Exception("Expected "+t+" to exist in new state!");
+            }
+        }
+
+        for (String f : args) {
+            f = f.replace("/", File.separator);
+            if (!tos.contains(f)) {
+                throw new Exception("Expected "+f+" to have been added to new state!");
+            }
+            if (froms.contains(f)) {
+                throw new Exception("Expected "+f+" to not exist in previous state!");
+            }
+        }
+
+        if (froms.size() + args.length != tos.size()) {
+            throw new Exception("There are more added files than the expected list!");
+        }
+    }
+
+    void verifyNewerFiles(Map<String,Long> from,
+                          Map<String,Long> to,
+                          String... args) throws Exception {
+        if (!from.keySet().equals(to.keySet())) {
+            throw new Exception("Expected the set of files to be identical!");
+        }
+        Set<String> files = new HashSet<String>();
+        for (String s : args) {
+            files.add(s.replace("/", File.separator));
+        }
+        for (String fn : from.keySet()) {
+            long f = from.get(fn);
+            long t = to.get(fn);
+            if (files.contains(fn)) {
+                if (t <= f) {
+                    throw new Exception("Expected "+fn+" to have a more recent timestamp!");
+                }
+            } else {
+                if (t != f) {
+                    throw new Exception("Expected "+fn+" to have the same timestamp!");
+                }
+            }
+        }
+    }
+
+    String print(Map<String,Long> m) {
+        StringBuilder b = new StringBuilder();
+        Set<String> keys = m.keySet();
+        for (String k : keys) {
+            b.append(k+" "+m.get(k)+"\n");
+        }
+        return b.toString();
+    }
+
+    void verifyEqual(Map<String,Long> from, Map<String,Long> to) throws Exception {
+        if (!from.equals(to)) {
+            System.out.println("FROM---"+print(from));
+            System.out.println("TO-----"+print(to));
+            throw new Exception("The dir should not differ! But it does!");
+        }
+    }
+}