| @@ -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(); | |||||
| } | |||||
| } | } | ||||