diff --git a/manual/Tasks/javac.html b/manual/Tasks/javac.html
index ce542463d..cfddb5821 100644
--- a/manual/Tasks/javac.html
+++ b/manual/Tasks/javac.html
@@ -135,7 +135,7 @@ invoking the compiler.
srcdir |
Location of the java files. (See the
note below.) |
- Yes, unless nested <src> elements are present. |
+ Yes, unless nested <src> elements or modulesourcepath attribute or elements are present. |
destdir |
@@ -459,6 +459,51 @@ invoking the compiler.
No - default is "true" |
+
+ modulepath |
+
+ Specify where to find application modules. A list of directories of modules, module files or exploded modules.
+ since Ant 1.9.7
+ |
+ No |
+
+
+ modulepathref |
+
+ The modulepath to use, given as reference to a PATH defined elsewhere.
+ since Ant 1.9.7 |
+ No |
+
+
+ modulesourcepath |
+
+ Specify where to find input source files for multiple module compilation.
+ since Ant 1.9.7
+ |
+ Yes, unless srcdir attribute or nested <src> elements are present |
+
+
+ modulesourcepathref |
+
+ The modulesourcepath to use, given as reference to a PATH defined elsewhere.
+ since Ant 1.9.7 |
+ No |
+
+
+ upgrademodulepath |
+
+ Specify the location of modules that replace upgradeable modules in the runtime image.
+ since Ant 1.9.7
+ |
+ No |
+
+
+ upgrademodulepathref |
+
+ The upgrademodulepath to use, given as reference to a PATH defined elsewhere.
+ since Ant 1.9.7 |
+ No |
+
Parameters specified as nested elements
@@ -468,17 +513,23 @@ supports most attributes of <fileset>
<include>
, <exclude>
and
<patternset>
elements.
srcdir
, classpath
, sourcepath
,
-bootclasspath
and extdirs
+bootclasspath
, modulepath
, modulesourcepath
,
+upgrademodulepath
and extdirs
<javac>
's srcdir
, classpath
,
-sourcepath
, bootclasspath
, and
-extdirs
attributes are
+sourcepath
, bootclasspath
,
+extdirs
, modulepath
, modulesourcepath
,
+and upgrademodulepath
attributes are
path-like structures
and can also be set via nested
<src>
(note the different name!),
<classpath>
,
<sourcepath>
,
-<bootclasspath>
and
-<extdirs>
elements, respectively.
+<bootclasspath>
,
+<extdirs>
,
+<modulepath>
,
+<modulesourcepath>
and
+<upgrademodulepath>
+elements, respectively.
compilerarg
@@ -710,6 +761,31 @@ the <compilerarg> element:
in which case your compiler adapter can support attributes and
nested elements of its own.
+ <javac srcdir="${src}"
+ destdir="${build}"
+ includeantruntime="false"
+ modulepath="modules"
+ source="9"
+ />
+compiles all .java
files in a single module under the ${src}
directory,
+ and stores the .class
files in the ${build}
directory. The compilation uses
+ application modules located in modules
folder.The source level is 9
to enable modules.
+
+ <javac modulesourcepath="${src}/*/{gen,lin{32,64}}/classes"
+ destdir="${build}"
+ includeantruntime="false"
+ modulepath="modules"
+ source="9"
+ />
+compiles all .java
files in gen/classes
, lin32/classes
and
+ lin64/classes
in all source modules under the ${src}
directory.
+ Generates module directories in the ${build}
directory. Each generated module directory under
+ the ${build}
directory contains .class
files from corresponding source module.
+ The *
is a token representing the name of any of the modules in the compilation module set.
+ The { ... , ... }
express alternates for expansion.
+ The compilation uses application modules located in modules
folder.The source level is
+ 9
to enable modules.
+
Jikes Notes
You need Jikes 1.15 or later.
diff --git a/src/main/org/apache/tools/ant/taskdefs/Javac.java b/src/main/org/apache/tools/ant/taskdefs/Javac.java
index 3d77f7c40..e5c96d630 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;
@@ -310,6 +323,49 @@ public class Javac extends MatchingTask {
createSourcepath().setRefid(r);
}
+ /**
+ * 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.
*
@@ -350,6 +406,92 @@ public class Javac extends MatchingTask {
createClasspath().setRefid(r);
}
+ /**
+ * 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.
@@ -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> 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 null
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> resolveModuleSourcePathElement(
+ final File projectDir,
+ final String element) {
+ final Map> result = new TreeMap>();
+ for (CharSequence resolvedElement : expandGroups(element)) {
+ findModules(projectDir, resolvedElement.toString(), result);
+ }
+ return result;
+ }
+
+ /**
+ * Expands the groups in the modulesourcepath entry to alternatives.
+ *
+ * The '*'
is a token representing the name of any of the modules in the compilation module set.
+ * The '{' ... ',' ... '}'
express alternates for expansion.
+ * An example of the modulesourcepath entry is src/*/{linux,share}/classes
+ *
+ * @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 result = new ArrayList();
+ 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 oldRes = result;
+ result = new ArrayList(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 result = new ArrayList();
+ 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> 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> 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 moduleRoots = collector.get(moduleName);
+ if (moduleRoots == null) {
+ moduleRoots = new ArrayList();
+ 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,
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 519163cc7..d1a9cb3cc 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());
+ }
}
/**
@@ -182,6 +192,45 @@ public abstract class DefaultCompilerAdapter
return classpath;
}
+ /**
+ * 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
@@ -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;
}
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 12ceea286..67e31e9e8 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 .
@@ -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
+ }
+ }
}
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 750098f99..2972cdb3c 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 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 expected = new TreeSet();
+ Collections.addAll(expected, java1.getAbsolutePath(), java2.getAbsolutePath());
+ final Set actual = new TreeSet();
+ 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 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 expectedFiles = new TreeSet();
+ Collections.addAll(expectedFiles,
+ java1.getAbsolutePath(),
+ java2.getAbsolutePath(),
+ java3.getAbsolutePath());
+ final Set actualFiles = new TreeSet();
+ 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();
+ }
+
}