@@ -135,7 +135,7 @@ invoking the compiler.</p> | |||||
<td valign="top">srcdir</td> | <td valign="top">srcdir</td> | ||||
<td valign="top">Location of the java files. (See the | <td valign="top">Location of the java files. (See the | ||||
<a href="#srcdirnote">note</a> below.)</td> | <a href="#srcdirnote">note</a> below.)</td> | ||||
<td align="center" valign="top">Yes, unless nested <code><src></code> elements are present.</td> | |||||
<td align="center" valign="top">Yes, unless nested <code><src></code> elements or <code>modulesourcepath</code> attribute or elements are present.</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td valign="top">destdir</td> | <td valign="top">destdir</td> | ||||
@@ -459,6 +459,51 @@ invoking the compiler.</p> | |||||
</td> | </td> | ||||
<td align="center" valign="top">No - default is "true"</td> | <td align="center" valign="top">No - default is "true"</td> | ||||
</tr> | </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><src></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> | </table> | ||||
<h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
@@ -468,17 +513,23 @@ supports most attributes of <code><fileset></code> | |||||
<code><include></code>, <code><exclude></code> and | <code><include></code>, <code><exclude></code> and | ||||
<code><patternset></code> elements.</p> | <code><patternset></code> elements.</p> | ||||
<h4><code>srcdir</code>, <code>classpath</code>, <code>sourcepath</code>, | <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><javac></code>'s <code>srcdir</code>, <code>classpath</code>, | <p><code><javac></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> | <a href="../using.html#path">path-like structures</a> | ||||
and can also be set via nested | and can also be set via nested | ||||
<code><src></code> (note the different name!), | <code><src></code> (note the different name!), | ||||
<code><classpath></code>, | <code><classpath></code>, | ||||
<code><sourcepath></code>, | <code><sourcepath></code>, | ||||
<code><bootclasspath></code> and | |||||
<code><extdirs></code> elements, respectively.</p> | |||||
<code><bootclasspath></code>, | |||||
<code><extdirs></code>, | |||||
<code><modulepath></code>, | |||||
<code><modulesourcepath></code> and | |||||
<code><upgrademodulepath></code> | |||||
elements, respectively.</p> | |||||
<h4>compilerarg</h4> | <h4>compilerarg</h4> | ||||
@@ -710,6 +761,31 @@ the <tt><compilerarg></tt> element: | |||||
<p>in which case your compiler adapter can support attributes and | <p>in which case your compiler adapter can support attributes and | ||||
nested elements of its own.</p> | nested elements of its own.</p> | ||||
<pre> <javac srcdir="${src}" | |||||
destdir="${build}" | |||||
includeantruntime="false" | |||||
modulepath="modules" | |||||
source="9" | |||||
/></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> <javac modulesourcepath="${src}/*/{gen,lin{32,64}}/classes" | |||||
destdir="${build}" | |||||
includeantruntime="false" | |||||
modulepath="modules" | |||||
source="9" | |||||
/></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> | <h3>Jikes Notes</h3> | ||||
<p>You need Jikes 1.15 or later.</p> | <p>You need Jikes 1.15 or later.</p> | ||||
@@ -19,12 +19,17 @@ | |||||
package org.apache.tools.ant.taskdefs; | package org.apache.tools.ant.taskdefs; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileFilter; | |||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Map.Entry; | import java.util.Map.Entry; | ||||
import java.util.TreeMap; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.DirectoryScanner; | 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 CLASSIC = "classic"; | ||||
private static final String EXTJAVAC = "extJavac"; | 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 static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); | ||||
private Path src; | private Path src; | ||||
private File destDir; | private File destDir; | ||||
private Path compileClasspath; | private Path compileClasspath; | ||||
private Path modulepath; | |||||
private Path upgrademodulepath; | |||||
private Path compileSourcepath; | private Path compileSourcepath; | ||||
private Path moduleSourcepath; | |||||
private String encoding; | private String encoding; | ||||
private boolean debug = false; | private boolean debug = false; | ||||
private boolean optimize = false; | private boolean optimize = false; | ||||
@@ -310,6 +323,49 @@ public class Javac extends MatchingTask { | |||||
createSourcepath().setRefid(r); | 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. | * Set the classpath to be used for this compilation. | ||||
* | * | ||||
@@ -350,6 +406,92 @@ public class Javac extends MatchingTask { | |||||
createClasspath().setRefid(r); | 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 | * Sets the bootclasspath that will be used to compile the classes | ||||
* against. | * against. | ||||
@@ -918,19 +1060,45 @@ public class Javac extends MatchingTask { | |||||
// scan source directories and dest directory to build up | // scan source directories and dest directory to build up | ||||
// compile lists | // 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(); | compile(); | ||||
@@ -1106,13 +1274,23 @@ public class Javac extends MatchingTask { | |||||
* @exception BuildException if an error occurs | * @exception BuildException if an error occurs | ||||
*/ | */ | ||||
protected void checkParameters() throws BuildException { | 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()); | getLocation()); | ||||
} | |||||
} else { | |||||
throw new BuildException("either srcdir or modulesourcepath attribute must be set!", | |||||
getLocation()); | |||||
} | } | ||||
if (destDir != null && !destDir.isDirectory()) { | 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/*/{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 = { | private static final byte[] PACKAGE_INFO_CLASS_HEADER = { | ||||
(byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, 0x00, 0x00, 0x00, | (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, 0x00, 0x00, 0x00, | ||||
0x31, 0x00, 0x07, 0x07, 0x00, 0x05, 0x07, 0x00, 0x06, 0x01, 0x00, 0x0a, | 0x31, 0x00, 0x07, 0x07, 0x00, 0x05, 0x07, 0x00, 0x06, 0x01, 0x00, 0x0a, | ||||
@@ -73,7 +73,10 @@ public abstract class DefaultCompilerAdapter | |||||
protected Path bootclasspath; | protected Path bootclasspath; | ||||
protected Path extdirs; | protected Path extdirs; | ||||
protected Path compileClasspath; | protected Path compileClasspath; | ||||
protected Path modulepath; | |||||
protected Path upgrademodulepath; | |||||
protected Path compileSourcepath; | protected Path compileSourcepath; | ||||
protected Path moduleSourcepath; | |||||
protected Project project; | protected Project project; | ||||
protected Location location; | protected Location location; | ||||
protected boolean includeAntRuntime; | protected boolean includeAntRuntime; | ||||
@@ -112,13 +115,20 @@ public abstract class DefaultCompilerAdapter | |||||
extdirs = attributes.getExtdirs(); | extdirs = attributes.getExtdirs(); | ||||
compileList = attributes.getFileList(); | compileList = attributes.getFileList(); | ||||
compileClasspath = attributes.getClasspath(); | compileClasspath = attributes.getClasspath(); | ||||
modulepath = attributes.getModulepath(); | |||||
upgrademodulepath = attributes.getUpgrademodulepath(); | |||||
compileSourcepath = attributes.getSourcepath(); | compileSourcepath = attributes.getSourcepath(); | ||||
moduleSourcepath = attributes.getModulesourcepath(); | |||||
project = attributes.getProject(); | project = attributes.getProject(); | ||||
location = attributes.getLocation(); | location = attributes.getLocation(); | ||||
includeAntRuntime = attributes.getIncludeantruntime(); | includeAntRuntime = attributes.getIncludeantruntime(); | ||||
includeJavaRuntime = attributes.getIncludejavaruntime(); | includeJavaRuntime = attributes.getIncludejavaruntime(); | ||||
memoryInitialSize = attributes.getMemoryInitialSize(); | memoryInitialSize = attributes.getMemoryInitialSize(); | ||||
memoryMaximumSize = attributes.getMemoryMaximumSize(); | 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; | 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. | * Get the command line arguments for the switches. | ||||
* @param cmd the command line | * @param cmd the command line | ||||
@@ -350,6 +399,21 @@ public abstract class DefaultCompilerAdapter | |||||
setImplicitSourceSwitch(cmd, t, adjustSourceValue(t)); | 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; | return cmd; | ||||
} | } | ||||
@@ -18,6 +18,7 @@ | |||||
package org.apache.tools.ant.taskdefs; | package org.apache.tools.ant.taskdefs; | ||||
import java.io.File; | |||||
import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter; | import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter; | ||||
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory; | import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory; | ||||
@@ -28,10 +29,13 @@ import org.junit.Before; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import static org.apache.tools.ant.AntAssert.assertContains; | 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.assertEquals; | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | |||||
/** | /** | ||||
* Testcase for <javac>. | * Testcase for <javac>. | ||||
@@ -244,4 +248,85 @@ public class JavacTest { | |||||
javac.setTarget("1.5"); | javac.setTarget("1.5"); | ||||
assertEquals("1.5", javac.getTarget()); | 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 | |||||
} | |||||
} | |||||
} | } |
@@ -18,12 +18,23 @@ | |||||
package org.apache.tools.ant.taskdefs.compilers; | 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.Project; | ||||
import org.apache.tools.ant.taskdefs.Javac; | import org.apache.tools.ant.taskdefs.Javac; | ||||
import org.apache.tools.ant.types.Commandline; | import org.apache.tools.ant.types.Commandline; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import static org.apache.tools.ant.AntAssert.assertContains; | 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; | import static org.junit.Assert.assertEquals; | ||||
public class DefaultCompilerAdapterTest { | public class DefaultCompilerAdapterTest { | ||||
@@ -174,6 +185,127 @@ public class DefaultCompilerAdapterTest { | |||||
testSource(null, "javac1.9", "", "9"); | 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) { | private void commonSourceDowngrades(String javaVersion) { | ||||
testSource("1.3", javaVersion, | testSource("1.3", javaVersion, | ||||
"If you specify -target 1.1 you now must also specify" | "If you specify -target 1.1 you now must also specify" | ||||
@@ -220,4 +352,35 @@ public class DefaultCompilerAdapterTest { | |||||
assertEquals(expectedSource, args[1]); | 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(); | |||||
} | |||||
} | } |