@@ -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><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> | |||
<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><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> | |||
<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><patternset></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><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> | |||
and can also be set via nested | |||
<code><src></code> (note the different name!), | |||
<code><classpath></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> | |||
@@ -710,6 +761,31 @@ the <tt><compilerarg></tt> element: | |||
<p>in which case your compiler adapter can support attributes and | |||
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> | |||
<p>You need Jikes 1.15 or later.</p> | |||
@@ -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<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/*/{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, | |||
@@ -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; | |||
} | |||
@@ -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 | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |