You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by bo...@apache.org on 2016/03/21 12:21:14 UTC

[3/5] ant git commit: Added modulepath, modulesourcepath and upgrademodulepath into Javac task.

Added modulepath, modulesourcepath and upgrademodulepath into Javac task.


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/83eaf480
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/83eaf480
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/83eaf480

Branch: refs/heads/master
Commit: 83eaf4804522c3aaac033f224ec2c5b9f6a25e30
Parents: 038e194
Author: Tomas Zezula <to...@gmail.com>
Authored: Fri Mar 11 14:00:22 2016 +0100
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Mon Mar 21 12:02:59 2016 +0100

----------------------------------------------------------------------
 manual/Tasks/javac.html                         |  88 +++-
 .../org/apache/tools/ant/taskdefs/Javac.java    | 432 ++++++++++++++++++-
 .../compilers/DefaultCompilerAdapter.java       |  64 +++
 .../apache/tools/ant/taskdefs/JavacTest.java    |  85 ++++
 .../compilers/DefaultCompilerAdapterTest.java   | 163 +++++++
 5 files changed, 809 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/83eaf480/manual/Tasks/javac.html
----------------------------------------------------------------------
diff --git a/manual/Tasks/javac.html b/manual/Tasks/javac.html
index ce54246..cfddb58 100644
--- a/manual/Tasks/javac.html
+++ b/manual/Tasks/javac.html
@@ -135,7 +135,7 @@ invoking the compiler.</p>
     <td valign="top">srcdir</td>
     <td valign="top">Location of the java files. (See the
      <a href="#srcdirnote">note</a> below.)</td>
-    <td align="center" valign="top">Yes, unless nested <code>&lt;src&gt;</code> elements are present.</td>
+    <td align="center" valign="top">Yes, unless nested <code>&lt;src&gt;</code> elements or <code>modulesourcepath</code> attribute or elements are present.</td>
   </tr>
   <tr>
     <td valign="top">destdir</td>
@@ -459,6 +459,51 @@ invoking the compiler.</p>
     </td>
     <td align="center" valign="top">No - default is "true"</td>
   </tr>
+  <tr>
+    <td valign="top">modulepath</td>
+    <td valign="top">
+      Specify where to find application modules. A list of directories of modules, module files or exploded modules.
+      <em>since Ant 1.9.7</em>
+    </td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">modulepathref</td>
+    <td valign="top">
+      The modulepath to use, given as <a href="../using.html#references">reference</a> to a PATH defined elsewhere.
+      <em>since Ant 1.9.7</em></td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">modulesourcepath</td>
+    <td valign="top">
+      Specify where to find input source files for multiple module compilation.
+      <em>since Ant 1.9.7</em>
+    </td>
+    <td align="center" valign="top">Yes, unless <code>srcdir</code> attribute or nested <code>&lt;src&gt;</code> elements are present</td>
+  </tr>
+  <tr>
+    <td valign="top">modulesourcepathref</td>
+    <td valign="top">
+      The modulesourcepath to use, given as <a href="../using.html#references">reference</a> to a PATH defined elsewhere.
+      <em>since Ant 1.9.7</em></td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">upgrademodulepath</td>
+    <td valign="top">
+      Specify the location of modules that replace upgradeable modules in the runtime image.
+      <em>since Ant 1.9.7</em>
+    </td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">upgrademodulepathref</td>
+    <td valign="top">
+      The upgrademodulepath to use, given as <a href="../using.html#references">reference</a> to a PATH defined elsewhere.
+      <em>since Ant 1.9.7</em></td>
+    <td align="center" valign="top">No</td>
+  </tr>
 </table>
 
 <h3>Parameters specified as nested elements</h3>
@@ -468,17 +513,23 @@ supports most attributes of <code>&lt;fileset&gt;</code>
 <code>&lt;include&gt;</code>, <code>&lt;exclude&gt;</code> and
 <code>&lt;patternset&gt;</code> elements.</p>
 <h4><code>srcdir</code>, <code>classpath</code>, <code>sourcepath</code>,
-<code>bootclasspath</code> and <code>extdirs</code></h4>
+<code>bootclasspath</code>, <code>modulepath</code>, <code>modulesourcepath</code>,
+<code>upgrademodulepath</code> and <code>extdirs</code></h4>
 <p><code>&lt;javac&gt;</code>'s <code>srcdir</code>, <code>classpath</code>,
-<code>sourcepath</code>, <code>bootclasspath</code>, and
-<code>extdirs</code> attributes are
+<code>sourcepath</code>, <code>bootclasspath</code>,
+<code>extdirs</code>, <code>modulepath</code>, <code>modulesourcepath</code>,
+and <code>upgrademodulepath</code> attributes are
 <a href="../using.html#path">path-like structures</a>
 and can also be set via nested
 <code>&lt;src&gt;</code> (note the different name!),
 <code>&lt;classpath&gt;</code>,
 <code>&lt;sourcepath&gt;</code>,
-<code>&lt;bootclasspath&gt;</code> and
-<code>&lt;extdirs&gt;</code> elements, respectively.</p>
+<code>&lt;bootclasspath&gt;</code>,
+<code>&lt;extdirs&gt;</code>,
+<code>&lt;modulepath&gt;</code>,
+<code>&lt;modulesourcepath&gt;</code> and
+<code>&lt;upgrademodulepath&gt;</code>
+elements, respectively.</p>
 
 <h4>compilerarg</h4>
 
@@ -710,6 +761,31 @@ the <tt>&lt;compilerarg&gt;</tt> element:
 <p>in which case your compiler adapter can support attributes and
   nested elements of its own.</p>
 
+<pre>  &lt;javac srcdir=&quot;${src}&quot;
+         destdir=&quot;${build}&quot;
+         includeantruntime=&quot;false&quot;
+         modulepath=&quot;modules&quot;
+         source=&quot;9&quot;
+  /&gt;</pre>
+<p>compiles all <code>.java</code> files in a single module under the <code>${src}</code> directory,
+  and stores the <code>.class</code> files in the <code>${build}</code> directory. The compilation uses
+  application modules located in <code>modules</code> folder.The source level is <code>9</code> to enable modules.</p>
+
+<pre>  &lt;javac modulesourcepath=&quot;${src}/*/{gen,lin{32,64}}/classes&quot;
+         destdir=&quot;${build}&quot;
+         includeantruntime=&quot;false&quot;
+         modulepath=&quot;modules&quot;
+         source=&quot;9&quot;
+  /&gt;</pre>
+<p>compiles all <code>.java</code> files in <code>gen/classes</code>, <code>lin32/classes</code> and
+  <code>lin64/classes</code> in all source modules under the <code>${src}</code> directory.
+  Generates module directories in the <code>${build}</code> directory. Each generated module directory under
+  the <code>${build}</code> directory contains <code>.class</code> files from corresponding source module.
+  The <code>*</code> is a token representing the name of any of the modules in the compilation module set.
+  The <code>{ ... , ... }</code> express alternates for expansion.
+  The compilation uses application modules located in <code>modules</code> folder.The source level is
+  <code>9</code> to enable modules.</p>
+
 <h3>Jikes Notes</h3>
 
 <p>You need Jikes 1.15 or later.</p>

http://git-wip-us.apache.org/repos/asf/ant/blob/83eaf480/src/main/org/apache/tools/ant/taskdefs/Javac.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/Javac.java b/src/main/org/apache/tools/ant/taskdefs/Javac.java
index 3d77f7c..e5c96d6 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Javac.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Javac.java
@@ -19,12 +19,17 @@
 package org.apache.tools.ant.taskdefs;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.TreeMap;
 
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DirectoryScanner;
@@ -93,12 +98,20 @@ public class Javac extends MatchingTask {
     private static final String CLASSIC = "classic";
     private static final String EXTJAVAC = "extJavac";
 
+    private static final char GROUP_START_MARK = '{';   //modulesourcepath group start character
+    private static final char GROUP_END_MARK = '}';   //modulesourcepath group end character
+    private static final char GROUP_SEP_MARK = ',';   //modulesourcepath group element separator character
+    private static final String MODULE_MARKER = "*";    //modulesourcepath module name marker
+
     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
 
     private Path src;
     private File destDir;
     private Path compileClasspath;
+    private Path modulepath;
+    private Path upgrademodulepath;
     private Path compileSourcepath;
+    private Path moduleSourcepath;
     private String encoding;
     private boolean debug = false;
     private boolean optimize = false;
@@ -311,6 +324,49 @@ public class Javac extends MatchingTask {
     }
 
     /**
+     * Set the modulesourcepath to be used for this compilation.
+     * @param msp  the modulesourcepath
+     * @since 1.9.7
+     */
+    public void setModulesourcepath(final Path msp) {
+        if (moduleSourcepath == null) {
+            moduleSourcepath = msp;
+        } else {
+            moduleSourcepath.append(msp);
+        }
+    }
+
+    /**
+     * Gets the modulesourcepath to be used for this compilation.
+     * @return the modulesourcepath
+     * @since 1.9.7
+     */
+    public Path getModulesourcepath() {
+        return moduleSourcepath;
+    }
+
+    /**
+     * Adds a path to modulesourcepath.
+     * @return a modulesourcepath to be configured
+     * @since 1.9.7
+     */
+    public Path createModulesourcepath() {
+        if (moduleSourcepath == null) {
+            moduleSourcepath = new Path(getProject());
+        }
+        return moduleSourcepath.createPath();
+    }
+
+    /**
+     * Adds a reference to a modulesourcepath defined elsewhere.
+     * @param r a reference to a modulesourcepath
+     * @since 1.9.7
+     */
+    public void setModulesourcepathRef(final Reference r) {
+        createModulesourcepath().setRefid(r);
+    }
+
+    /**
      * Set the classpath to be used for this compilation.
      *
      * @param classpath an Ant Path object containing the compilation classpath.
@@ -351,6 +407,92 @@ public class Javac extends MatchingTask {
     }
 
     /**
+     * Set the modulepath to be used for this compilation.
+     * @param mp an Ant Path object containing the modulepath.
+     * @since 1.9.7
+     */
+    public void setModulepath(final Path mp) {
+        if (modulepath == null) {
+            modulepath = mp;
+        } else {
+            modulepath.append(mp);
+        }
+    }
+
+    /**
+     * Gets the modulepath to be used for this compilation.
+     * @return the modulepath
+     * @since 1.9.7
+     */
+    public Path getModulepath() {
+        return modulepath;
+    }
+
+    /**
+     * Adds a path to the modulepath.
+     * @return a modulepath to be configured
+     * @since 1.9.7
+     */
+    public Path createModulepath() {
+        if (modulepath == null) {
+            modulepath = new Path(getProject());
+        }
+        return modulepath.createPath();
+    }
+
+    /**
+     * Adds a reference to a modulepath defined elsewhere.
+     * @param r a reference to a modulepath
+     * @since 1.9.7
+     */
+    public void setModulepathRef(final Reference r) {
+        createModulepath().setRefid(r);
+    }
+
+    /**
+     * Set the upgrademodulepath to be used for this compilation.
+     * @param ump an Ant Path object containing the upgrademodulepath.
+     * @since 1.9.7
+     */
+    public void setUpgrademodulepath(final Path ump) {
+        if (upgrademodulepath == null) {
+            upgrademodulepath = ump;
+        } else {
+            upgrademodulepath.append(ump);
+        }
+    }
+
+    /**
+     * Gets the upgrademodulepath to be used for this compilation.
+     * @return the upgrademodulepath
+     * @since 1.9.7
+     */
+    public Path getUpgrademodulepath() {
+        return upgrademodulepath;
+    }
+
+    /**
+     * Adds a path to the upgrademodulepath.
+     * @return an upgrademodulepath to be configured
+     * @since 1.9.7
+     */
+    public Path createUpgrademodulepath() {
+        if (upgrademodulepath == null) {
+            upgrademodulepath = new Path(getProject());
+        }
+        return upgrademodulepath.createPath();
+    }
+
+    /**
+     * Adds a reference to the upgrademodulepath defined elsewhere.
+     * @param r a reference to an upgrademodulepath
+     * @since 1.9.7
+     */
+    public void setUpgrademodulepathRef(final Reference r) {
+        createUpgrademodulepath().setRefid(r);
+    }
+
+    /**
      * Sets the bootclasspath that will be used to compile the classes
      * against.
      * @param bootclasspath a path to use as a boot class path (may be more
@@ -918,19 +1060,45 @@ public class Javac extends MatchingTask {
 
         // scan source directories and dest directory to build up
         // compile lists
-        final String[] list = src.list();
-        for (int i = 0; i < list.length; i++) {
-            final File srcDir = getProject().resolveFile(list[i]);
-            if (!srcDir.exists()) {
-                throw new BuildException("srcdir \""
-                                         + srcDir.getPath()
-                                         + "\" does not exist!", getLocation());
-            }
+        // Both src and modulesourcepath can be passed to javac, in this case
+        // the src becomes a part of the unnamed module.
+        if (hasPath(src)) {
+            final String[] list = src.list();
+            for (int i = 0; i < list.length; i++) {
+                final File srcDir = getProject().resolveFile(list[i]);
+                if (!srcDir.exists()) {
+                    throw new BuildException("srcdir \""
+                                             + srcDir.getPath()
+                                             + "\" does not exist!", getLocation());
+                }
 
-            final DirectoryScanner ds = this.getDirectoryScanner(srcDir);
-            final String[] files = ds.getIncludedFiles();
+                final DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+                final String[] files = ds.getIncludedFiles();
 
-            scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+                scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+            }
+        } else {
+            assert hasPath(moduleSourcepath) : "Either srcDir or moduleSourcepath must be given";
+            final FileUtils fu = FileUtils.getFileUtils();
+            for (String pathElement : moduleSourcepath.list()) {
+                boolean valid = false;
+                for (Map.Entry<String,Collection<File>> modules : resolveModuleSourcePathElement(getProject().getBaseDir(), pathElement).entrySet()) {
+                    final String moduleName = modules.getKey();
+                    for (File srcDir : modules.getValue()) {
+                        if (srcDir.exists()) {
+                            valid = true;
+                            final DirectoryScanner ds = getDirectoryScanner(srcDir);
+                            final String[] files = ds.getIncludedFiles();
+                            scanDir(srcDir, fu.resolveFile(destDir, moduleName), files);
+                        }
+                    }
+                }
+                if (!valid) {
+                    throw new BuildException("modulesourcepath \""
+                                             + pathElement
+                                             + "\" does not exist!", getLocation());
+                }
+            }
         }
 
         compile();
@@ -1106,13 +1274,23 @@ public class Javac extends MatchingTask {
      * @exception BuildException if an error occurs
      */
     protected void checkParameters() throws BuildException {
-        if (src == null) {
-            throw new BuildException("srcdir attribute must be set!",
-                                     getLocation());
-        }
-        if (src.size() == 0) {
-            throw new BuildException("srcdir attribute must be set!",
+        if (hasPath(src)) {
+            if (hasPath(moduleSourcepath)) {
+                throw new BuildException("modulesourcepath cannot be combined with srcdir attribute!",
+                    getLocation());
+            }
+        } else if (hasPath(moduleSourcepath)) {
+            if (hasPath(src) || hasPath(compileSourcepath)) {
+                throw new BuildException("modulesourcepath cannot be combined with srcdir or sourcepath !",
+                    getLocation());
+            }
+            if (destDir == null) {
+                throw new BuildException("modulesourcepath requires destdir attribute to be set!",
                                      getLocation());
+            }
+        } else {
+            throw new BuildException("either srcdir or modulesourcepath attribute must be set!",
+                    getLocation());
         }
 
         if (destDir != null && !destDir.isDirectory()) {
@@ -1251,6 +1429,226 @@ public class Javac extends MatchingTask {
         }
     }
 
+    /**
+     * Checks if a path exists and is non empty.
+     * @param path to be checked
+     * @return true if the path is non <code>null</code> and non empty.
+     * @since 1.9.7
+     */
+    private static boolean hasPath(final Path path) {
+        return path != null && path.size() > 0;
+    }
+
+    /**
+     * Resolves the modulesourcepath element possibly containing groups
+     * and module marks to module names and source roots.
+     * @param projectDir the project directory
+     * @param element the modulesourcepath elemement
+     * @return a mapping from module name to module source roots
+     * @since 1.9.7
+     */
+    private static Map<String,Collection<File>> resolveModuleSourcePathElement(
+            final File projectDir,
+            final String element) {
+        final Map<String,Collection<File>> result = new TreeMap<String, Collection<File>>();
+        for (CharSequence resolvedElement : expandGroups(element)) {
+            findModules(projectDir, resolvedElement.toString(), result);
+        }
+        return result;
+    }
+
+    /**
+     * Expands the groups in the modulesourcepath entry to alternatives.
+     * <p>
+     * The <code>'*'</code> is a token representing the name of any of the modules in the compilation module set.
+     * The <code>'{' ... ',' ... '}'</code> express alternates for expansion.
+     * An example of the modulesourcepath entry is <code>src/&#42;/{linux,share}/classes</code>
+     * </p>
+     * @param element the entry to expand groups in
+     * @return the possible alternatives
+     * @since 1.9.7
+     */
+    private static Collection<? extends CharSequence> expandGroups(
+            final CharSequence element) {
+        List<StringBuilder> result = new ArrayList<StringBuilder>();
+        result.add(new StringBuilder());
+        StringBuilder resolved = new StringBuilder();
+        for (int i = 0; i < element.length(); i++) {
+            final char c = element.charAt(i);
+            switch (c) {
+                case GROUP_START_MARK:
+                    final int end = getGroupEndIndex(element, i);
+                    if (end < 0) {
+                        throw new BuildException(String.format(
+                                "Unclosed group %s, starting at: %d",
+                                element,
+                                i));
+                    }
+                    final Collection<? extends CharSequence> parts = resolveGroup(element.subSequence(i+1, end));
+                    switch (parts.size()) {
+                        case 0:
+                            break;
+                        case 1:
+                            resolved.append(parts.iterator().next());
+                            break;
+                        default:
+                            final List<StringBuilder> oldRes = result;
+                            result = new ArrayList<StringBuilder>(oldRes.size() * parts.size());
+                            for (CharSequence part : parts) {
+                                for (CharSequence prefix : oldRes) {
+                                    result.add(new StringBuilder(prefix).append(resolved).append(part));
+                                }
+                            }
+                            resolved = new StringBuilder();
+                    }
+                    i = end;
+                    break;
+                default:
+                    resolved.append(c);
+            }
+        }
+        for (StringBuilder prefix : result) {
+            prefix.append(resolved);
+        }
+        return result;
+    }
+
+    /**
+     * Resolves the group to alternatives.
+     * @param group the group to resolve
+     * @return the possible alternatives
+     * @since 1.9.7
+     */
+    private static Collection<? extends CharSequence> resolveGroup(final CharSequence group) {
+        final Collection<CharSequence> result = new ArrayList<CharSequence>();
+        int start = 0;
+        int depth = 0;
+        for (int i = 0; i < group.length(); i++) {
+            final char c = group.charAt(i);
+            switch (c) {
+                case GROUP_START_MARK:
+                    depth++;
+                    break;
+                case GROUP_END_MARK:
+                    depth--;
+                    break;
+                case GROUP_SEP_MARK:
+                    if (depth == 0) {
+                        result.addAll(expandGroups(group.subSequence(start, i)));
+                        start = i + 1;
+                    }
+                    break;
+            }
+        }
+        result.addAll(expandGroups(group.subSequence(start, group.length())));
+        return result;
+    }
+
+    /**
+     * Finds the index of an enclosing brace of the group.
+     * @param element the element to find the enclosing brace in
+     * @param start the index of the opening brace.
+     * @return return the index of an enclosing brace of the group or -1 if not found
+     * @since 1.9.7
+     */
+    private static int getGroupEndIndex(
+            final CharSequence element,
+            final int start) {
+        int depth = 0;
+        for (int i = start; i < element.length(); i++) {
+            final char c = element.charAt(i);
+            switch (c) {
+                case GROUP_START_MARK:
+                    depth++;
+                    break;
+                case GROUP_END_MARK:
+                    depth--;
+                    if (depth == 0) {
+                        return i;
+                    }
+                    break;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Finds modules in the expanded modulesourcepath entry.
+     * @param root the project root
+     * @param pattern the expanded modulesourcepath entry
+     * @param collector the map to put modules into
+     * @since 1.9.7
+     */
+    private static void findModules(
+            final File root,
+            String pattern,
+            final Map<String,Collection<File>> collector) {
+        pattern = pattern
+                .replace('/', File.separatorChar)
+                .replace('\\', File.separatorChar);
+        final int startIndex = pattern.indexOf(MODULE_MARKER);
+        if (startIndex == -1) {
+            findModules(root, pattern, null, collector);
+        } else {
+            if (startIndex == 0) {
+                throw new BuildException("The modulesourcepath entry must be a folder.");
+            }
+            final int endIndex = startIndex + MODULE_MARKER.length();
+            if (pattern.charAt(startIndex - 1) != File.separatorChar) {
+                    throw new BuildException("The module mark must be preceded by separator");
+            }
+            if (endIndex < pattern.length() && pattern.charAt(endIndex) != File.separatorChar) {
+                throw new BuildException("The module mark must be followed by separator");
+            }
+            if (pattern.indexOf(MODULE_MARKER, endIndex) != -1) {
+                throw new BuildException("The modulesourcepath entry must contain at most one module mark");
+            }
+            final String pathToModule = pattern.substring(0,startIndex);
+            final String pathInModule = endIndex == pattern.length() ?
+                    null :
+                    pattern.substring(endIndex+1);  //+1 the separator
+            findModules(root, pathToModule, pathInModule, collector);
+        }
+    }
+
+    /**
+     * Finds modules in the expanded modulesourcepath entry.
+     * @param root the project root
+     * @param pathToModule the path to modules folder
+     * @param pathInModule the path in module to source folder
+     * @param collector the map to put modules into
+     * @since 1.9.7
+     */
+    private static void findModules(
+        final File root,
+        final String pathToModule,
+        final String pathInModule,
+        final Map<String,Collection<File>> collector) {
+        final FileUtils fu = FileUtils.getFileUtils();
+        final File f = fu.resolveFile(root, pathToModule);
+        if (!f.isDirectory()) {
+            return;
+        }
+        final File[] modules = f.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File pathname) {
+                return pathname.isDirectory();
+            }
+        });
+        for (File module : modules) {
+            final String moduleName = module.getName();
+            final File moduleSourceRoot = pathInModule == null ?
+                    module :
+                    new File(module, pathInModule);
+            Collection<File> moduleRoots = collector.get(moduleName);
+            if (moduleRoots == null) {
+                moduleRoots = new ArrayList<File>();
+                collector.put(moduleName, moduleRoots);
+            }
+            moduleRoots.add(moduleSourceRoot);
+        }
+    }
+
     private static final byte[] PACKAGE_INFO_CLASS_HEADER = {
         (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, 0x00, 0x00, 0x00,
         0x31, 0x00, 0x07, 0x07, 0x00, 0x05, 0x07, 0x00, 0x06, 0x01, 0x00, 0x0a,

http://git-wip-us.apache.org/repos/asf/ant/blob/83eaf480/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java b/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
index 519163c..d1a9cb3 100644
--- a/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
+++ b/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
@@ -73,7 +73,10 @@ public abstract class DefaultCompilerAdapter
     protected Path bootclasspath;
     protected Path extdirs;
     protected Path compileClasspath;
+    protected Path modulepath;
+    protected Path upgrademodulepath;
     protected Path compileSourcepath;
+    protected Path moduleSourcepath;
     protected Project project;
     protected Location location;
     protected boolean includeAntRuntime;
@@ -112,13 +115,20 @@ public abstract class DefaultCompilerAdapter
         extdirs = attributes.getExtdirs();
         compileList = attributes.getFileList();
         compileClasspath = attributes.getClasspath();
+        modulepath = attributes.getModulepath();
+        upgrademodulepath = attributes.getUpgrademodulepath();
         compileSourcepath = attributes.getSourcepath();
+        moduleSourcepath = attributes.getModulesourcepath();
         project = attributes.getProject();
         location = attributes.getLocation();
         includeAntRuntime = attributes.getIncludeantruntime();
         includeJavaRuntime = attributes.getIncludejavaruntime();
         memoryInitialSize = attributes.getMemoryInitialSize();
         memoryMaximumSize = attributes.getMemoryMaximumSize();
+        if (moduleSourcepath != null && src == null && compileSourcepath == null) {
+            //Compatibility to prevent NPE from Jikes, Jvc, Kjc
+            compileSourcepath = new Path(getProject());
+        }
     }
 
     /**
@@ -183,6 +193,45 @@ public abstract class DefaultCompilerAdapter
     }
 
     /**
+     * Builds the modulepath.
+     * @return the modulepath
+     * @since 1.9.7
+     */
+    protected Path getModulepath() {
+        final Path mp = new Path(getProject());
+        if (modulepath != null) {
+            mp.addExisting(modulepath);
+        }
+        return mp;
+    }
+
+    /**
+     * Builds the upgrademodulepath.
+     * @return the upgrademodulepath
+     * @since 1.9.7
+     */
+    protected Path getUpgrademodulepath() {
+        final Path ump = new Path(getProject());
+        if (upgrademodulepath != null) {
+            ump.addExisting(upgrademodulepath);
+        }
+        return ump;
+    }
+
+    /**
+     * Builds the modulesourcepath for multi module compilation.
+     * @return the modulesourcepath
+     * @since 1.9.7
+     */
+    protected Path getModulesourcepath() {
+        final Path msp = new Path(getProject());
+        if (moduleSourcepath != null) {
+            msp.add(moduleSourcepath);
+        }
+        return msp;
+    }
+
+    /**
      * Get the command line arguments for the switches.
      * @param cmd the command line
      * @return the command line
@@ -350,6 +399,21 @@ public abstract class DefaultCompilerAdapter
                 setImplicitSourceSwitch(cmd, t, adjustSourceValue(t));
             }
         }
+        final Path msp = getModulesourcepath();
+        if (msp.size() > 0) {
+            cmd.createArgument().setValue("-modulesourcepath");
+            cmd.createArgument().setPath(msp);
+        }
+        final Path mp = getModulepath();
+        if (mp.size() > 0) {
+            cmd.createArgument().setValue("-modulepath");
+            cmd.createArgument().setPath(mp);
+        }
+        final Path ump = getUpgrademodulepath();
+        if (ump.size() > 0) {
+            cmd.createArgument().setValue("-upgrademodulepath");
+            cmd.createArgument().setPath(ump);
+        }
         return cmd;
     }
 

http://git-wip-us.apache.org/repos/asf/ant/blob/83eaf480/src/tests/junit/org/apache/tools/ant/taskdefs/JavacTest.java
----------------------------------------------------------------------
diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/JavacTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/JavacTest.java
index 12ceea2..67e31e9 100644
--- a/src/tests/junit/org/apache/tools/ant/taskdefs/JavacTest.java
+++ b/src/tests/junit/org/apache/tools/ant/taskdefs/JavacTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.tools.ant.taskdefs;
 
+import java.io.File;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
 import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
@@ -28,10 +29,13 @@ import org.junit.Before;
 import org.junit.Test;
 
 import static org.apache.tools.ant.AntAssert.assertContains;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * Testcase for <javac>.
@@ -244,4 +248,85 @@ public class JavacTest {
         javac.setTarget("1.5");
         assertEquals("1.5", javac.getTarget());
     }
+
+    @Test
+    public void testModulesourcepathOrSrcDirRequired() {
+        try {
+            javac.checkParameters();
+            fail("Build exception should have been thrown - neither srcDir nor modulesourcepath");
+        } catch (BuildException e) {
+            //pass
+        }
+    }
+
+    @Test
+    public void testModulesourcepathAndSrcDirForbidden() {
+        try {
+            javac.checkParameters();
+            final Path p = new Path(project);
+            p.setPath("src");
+            javac.setSrcdir(p);
+            final Path mp = new Path(project);
+            p.setPath("modsrc");
+            javac.setModulesourcepath(mp);
+            fail("Build exception should have been thrown - neither srcDir nor modulesourcepath");
+        } catch (BuildException e) {
+            //pass
+        }
+    }
+
+    @Test
+    public void testModulesourcepathAndSourcepathForbidden() {
+        try {
+            javac.checkParameters();
+            final Path p = new Path(project);
+            p.setPath("src");
+            javac.setSourcepath(p);
+            final Path mp = new Path(project);
+            p.setPath("modsrc");
+            javac.setModulesourcepath(mp);
+            fail("Build exception should have been thrown - neither srcDir nor modulesourcepath");
+        } catch (BuildException e) {
+            //pass
+        }
+    }
+
+    @Test
+    public void testSrcDir() {
+        final Path p = new Path(project);
+        p.setPath("src");
+        javac.setSrcdir(p);
+        javac.checkParameters();
+    }
+
+    @Test
+    public void testModulesourcepath() {
+        final File tmp = new File(System.getProperty("java.io.tmpdir"));   //NOI18N
+        final File destDir = new File(tmp, String.format("%stestMP%d",
+                getClass().getName(),
+                System.currentTimeMillis()/1000));
+        destDir.mkdirs();
+        try {
+            final Path p = new Path(project);
+            p.setPath("src");
+            javac.setModulesourcepath(p);
+            javac.setDestdir(destDir);
+            javac.checkParameters();
+        } finally {
+            destDir.delete();
+        }
+    }
+
+    @Test
+    public void testModulesourcepathRequiresDestdir() {
+        try {
+            final Path p = new Path(project);
+            p.setPath("src");
+            javac.setModulesourcepath(p);
+            javac.checkParameters();
+            fail("Build exception should have been thrown - modulesourcepath requires destdir");
+        } catch (BuildException e) {
+            //pass
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ant/blob/83eaf480/src/tests/junit/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapterTest.java
----------------------------------------------------------------------
diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapterTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapterTest.java
index 750098f..2972cdb 100644
--- a/src/tests/junit/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapterTest.java
+++ b/src/tests/junit/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapterTest.java
@@ -18,12 +18,23 @@
 
 package org.apache.tools.ant.taskdefs.compilers;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.taskdefs.Javac;
 import org.apache.tools.ant.types.Commandline;
 import org.junit.Test;
 
 import static org.apache.tools.ant.AntAssert.assertContains;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.junit.Assert;
 import static org.junit.Assert.assertEquals;
 
 public class DefaultCompilerAdapterTest {
@@ -174,6 +185,127 @@ public class DefaultCompilerAdapterTest {
         testSource(null, "javac1.9", "", "9");
     }
 
+    @Test
+    public void testSingleModuleCompilation() throws IOException {
+        final File workDir = createWorkDir("testSMC");
+        try {
+            final File src = new File(workDir, "src");
+            src.mkdir();
+            final File java1 = createFile(src,"org/apache/ant/tests/J1.java");
+            final File java2 = createFile(src,"org/apache/ant/tests/J2.java");
+            final File modules = new File(workDir, "modules");
+            modules.mkdir();
+            final Project prj = new Project();
+            prj.setBaseDir(workDir);
+            final LogCapturingJavac javac = new LogCapturingJavac();
+            javac.setProject(prj);
+            final Commandline[] cmd = new Commandline[1];
+            final DefaultCompilerAdapter impl = new DefaultCompilerAdapter() {
+                @Override
+                public boolean execute() throws BuildException {
+                    cmd[0] = setupModernJavacCommand();
+                    return true;
+                }
+            };
+            final Path srcPath = new Path(prj);
+            srcPath.setLocation(src);
+            javac.setSrcdir(srcPath);
+            javac.createModulepath().setLocation(modules);
+            javac.setSource("9");
+            javac.setTarget("9");
+            javac.setIncludeantruntime(false);
+            javac.add(impl);
+            javac.execute();
+            Assert.assertNotNull(cmd[0]);
+            final List<String> cmdLine = Arrays.asList(cmd[0].getCommandline());
+            //No modulesourcepath
+            assertEquals(-1, cmdLine.indexOf("-modulesourcepath"));
+            //The -sourcepath has to be followed by src
+            int index = cmdLine.indexOf("-sourcepath");
+            Assert.assertTrue(index != -1 && index < cmdLine.size() - 1);
+            assertEquals(src.getAbsolutePath(), cmdLine.get(index + 1));
+            //The -modulepath has to be followed by modules
+            index = cmdLine.indexOf("-modulepath");
+            Assert.assertTrue(index != -1 && index < cmdLine.size() - 1);
+            assertEquals(modules.getAbsolutePath(), cmdLine.get(index + 1));
+            //J1.java & J2.java has to be in files list
+            final Set<String> expected = new TreeSet<String>();
+            Collections.addAll(expected, java1.getAbsolutePath(), java2.getAbsolutePath());
+            final Set<String> actual = new TreeSet<String>();
+            actual.addAll(cmdLine.subList(cmdLine.size() - 2, cmdLine.size()));
+            assertEquals(expected, actual);
+        } finally {
+            delete(workDir);
+        }
+    }
+
+    @Test
+    public void testMultiModuleCompilation() throws IOException {
+        final File workDir = createWorkDir("testMMC");
+        try {
+            final File src = new File(workDir, "src");
+            src.mkdir();
+            final File java1 = createFile(src,"main/m1/lin64/classes/org/apache/ant/tests/J1.java");
+            final File java2 = createFile(src,"main/m2/lin32/classes/org/apache/ant/tests/J2.java");
+            final File java3 = createFile(src,"main/m3/sol/classes/org/apache/ant/tests/J3.java");
+            final File modules = new File(workDir, "modules");
+            modules.mkdir();
+            final File build = new File(workDir, "build");
+            build.mkdirs();
+            final Project prj = new Project();
+            prj.setBaseDir(workDir);
+            final LogCapturingJavac javac = new LogCapturingJavac();
+            javac.setProject(prj);
+            final Commandline[] cmd = new Commandline[1];
+            final DefaultCompilerAdapter impl = new DefaultCompilerAdapter() {
+                @Override
+                public boolean execute() throws BuildException {
+                    cmd[0] = setupModernJavacCommand();
+                    return true;
+                }
+            };
+            final String moduleSrcPathStr = "src/main/*/{lin{32,64},sol}/classes";
+            final Path moduleSourcePath = new Path(prj);
+            moduleSourcePath.setPath(moduleSrcPathStr);
+            javac.setModulesourcepath(moduleSourcePath);
+            javac.createModulepath().setLocation(modules);
+            javac.setSource("9");
+            javac.setTarget("9");
+            javac.setDestdir(build);
+            javac.setIncludeantruntime(false);
+            javac.add(impl);
+            javac.execute();
+            Assert.assertNotNull(cmd[0]);
+            final List<String> cmdLine = Arrays.asList(cmd[0].getCommandline());
+            //No sourcepath
+            assertEquals(-1, cmdLine.indexOf("-sourcepath"));
+            //The -modulesourcepath has to be followed by the pattern
+            int index = cmdLine.indexOf("-modulesourcepath");
+            Assert.assertTrue(index != -1 && index < cmdLine.size() - 1);
+            String expectedModSrcPath = String.format("%s/%s",
+                    workDir.getAbsolutePath(),
+                    moduleSrcPathStr)
+                    .replace('/', File.separatorChar)
+                    .replace('\\', File.separatorChar);
+            assertEquals(expectedModSrcPath, cmdLine.get(index + 1));
+            //The -modulepath has to be followed by modules
+            index = cmdLine.indexOf("-modulepath");
+            Assert.assertTrue(index != -1 && index < cmdLine.size() - 1);
+            assertEquals(modules.getAbsolutePath(), cmdLine.get(index + 1));
+            //J1.java, J2.java & J3.java has to be in files list
+            final Set<String> expectedFiles = new TreeSet<String>();
+            Collections.addAll(expectedFiles,
+                    java1.getAbsolutePath(),
+                    java2.getAbsolutePath(),
+                    java3.getAbsolutePath());
+            final Set<String> actualFiles = new TreeSet<String>();
+            actualFiles.addAll(cmdLine.subList(cmdLine.size() - 3, cmdLine.size()));
+            assertEquals(expectedFiles, actualFiles);
+        } finally {
+            delete(workDir);
+        }
+    }
+
     private void commonSourceDowngrades(String javaVersion) {
         testSource("1.3", javaVersion,
                    "If you specify -target 1.1 you now must also specify"
@@ -220,4 +352,35 @@ public class DefaultCompilerAdapterTest {
             assertEquals(expectedSource, args[1]);
         }
     }
+
+    private File createWorkDir(String testName) {
+        final File tmp = new File(System.getProperty("java.io.tmpdir"));   //NOI18N
+        final File destDir = new File(tmp, String.format("%s%s%d",
+                getClass().getName(),
+                testName,
+                System.currentTimeMillis()/1000));
+        destDir.mkdirs();
+        return destDir;
+    }
+
+    private File createFile(File folder, String relativePath) throws IOException {
+        final File file = new File(
+                folder,
+                relativePath.replace('/', File.separatorChar).replace('\\', File.separatorChar));
+        FileUtils.getFileUtils().createNewFile(file, true);
+        return file;
+    }
+
+    private void delete(File f) {
+        if (f.isDirectory()) {
+            final File[] clds = f.listFiles();
+            if (clds != null) {
+                for (File cld : clds) {
+                    delete(cld);
+                }
+            }
+        }
+        f.delete();
+    }
+
 }