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