Copyright (c) 2000, 2013 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.core; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Javac; import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Commandline.Argument; import org.apache.tools.ant.util.JavaEnvUtils; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.antadapter.AntAdapterMessages; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.compiler.util.Util;
Ant 1.5 compiler adapter for the Eclipse Java compiler. This adapter permits the Eclipse Java compiler to be used with the javac task in Ant scripts. In order to use it, just set the property build.compiler as follows:

<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>

For more information on Ant check out the website at http://jakarta.apache.org/ant/ .

Since:2.0
/** * Ant 1.5 compiler adapter for the Eclipse Java compiler. This adapter permits the * Eclipse Java compiler to be used with the <code>javac</code> task in Ant scripts. In order * to use it, just set the property <code>build.compiler</code> as follows: * <p> * <code>&lt;property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/&gt;</code> * </p> * <p> * For more information on Ant check out the website at http://jakarta.apache.org/ant/ . * </p> * * @since 2.0 */
@SuppressWarnings({ "rawtypes", "unchecked" }) public class JDTCompilerAdapter extends DefaultCompilerAdapter { private static final char[] SEPARATOR_CHARS = new char[] { '/', '\\' }; private static final char[] ADAPTER_PREFIX = "#ADAPTER#".toCharArray(); //$NON-NLS-1$ private static final char[] ADAPTER_ENCODING = "ENCODING#".toCharArray(); //$NON-NLS-1$ private static final char[] ADAPTER_ACCESS = "ACCESS#".toCharArray(); //$NON-NLS-1$ private static String compilerClass = "org.eclipse.jdt.internal.compiler.batch.Main"; //$NON-NLS-1$ String logFileName; Map customDefaultOptions; private Map fileEncodings = null; private Map dirEncodings = null; private List accessRules = null;
Performs a compile using the JDT batch compiler
Throws:
  • BuildException – if anything wrong happen during the compilation
Returns:boolean true if the compilation is ok, false otherwise
/** * Performs a compile using the JDT batch compiler * @throws BuildException if anything wrong happen during the compilation * @return boolean true if the compilation is ok, false otherwise */
@Override public boolean execute() throws BuildException { this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.info.usingJDTCompiler"), Project.MSG_VERBOSE); //$NON-NLS-1$ Commandline cmd = setupJavacCommand(); try { Class c = Class.forName(compilerClass); Constructor batchCompilerConstructor = c.getConstructor(new Class[] { PrintWriter.class, PrintWriter.class, Boolean.TYPE, Map.class}); Object batchCompilerInstance = batchCompilerConstructor.newInstance(new Object[] {new PrintWriter(System.out), new PrintWriter(System.err), Boolean.TRUE, this.customDefaultOptions}); Method compile = c.getMethod("compile", new Class[] {String[].class}); //$NON-NLS-1$ Object result = compile.invoke(batchCompilerInstance, new Object[] { cmd.getArguments()}); final boolean resultValue = ((Boolean) result).booleanValue(); if (!resultValue && this.logFileName != null) { this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.error.compilationFailed", this.logFileName)); //$NON-NLS-1$ } return resultValue; } catch (ClassNotFoundException cnfe) { throw new BuildException(AntAdapterMessages.getString("ant.jdtadapter.error.cannotFindJDTCompiler"), cnfe); //$NON-NLS-1$ } catch (Exception ex) { throw new BuildException(ex); } } @Override protected Commandline setupJavacCommand() throws BuildException { Commandline cmd = new Commandline(); this.customDefaultOptions = new CompilerOptions().getMap(); Class javacClass = Javac.class; /* * Read in the compiler arguments first since we might need to modify * the classpath if any access rules were specified */ String [] compilerArgs = processCompilerArguments(javacClass); /* * This option is used to never exit at the end of the ant task. */ cmd.createArgument().setValue("-noExit"); //$NON-NLS-1$ if (this.bootclasspath != null) { cmd.createArgument().setValue("-bootclasspath"); //$NON-NLS-1$ if (this.bootclasspath.size() != 0) { /* * Set the bootclasspath for the Eclipse compiler. */ cmd.createArgument().setPath(this.bootclasspath); } else { cmd.createArgument().setValue(Util.EMPTY_STRING); } } /* * Eclipse compiler doesn't support -extdirs. * It is emulated using the classpath. We add extdirs entries after the * bootclasspath. */ if (this.extdirs != null) { cmd.createArgument().setValue("-extdirs"); //$NON-NLS-1$ cmd.createArgument().setPath(this.extdirs); } Path classpath = new Path(this.project); /* * The java runtime is already handled, so we simply want to retrieve the * ant runtime and the compile classpath. */ classpath.append(getCompileClasspath()); /* * Set the classpath for the Eclipse compiler. */ cmd.createArgument().setValue("-classpath"); //$NON-NLS-1$ createClasspathArgument(cmd, classpath); // For -sourcepath, use the "sourcepath" value if present. // Otherwise default to the "srcdir" value. Path sourcepath = null; // retrieve the method getSourcepath() using reflect // This is done to improve the compatibility to ant 1.5 Method getSourcepathMethod = null; try { getSourcepathMethod = javacClass.getMethod("getSourcepath", (Class[]) null); //$NON-NLS-1$ } catch(NoSuchMethodException e) { // if not found, then we cannot use this method (ant 1.5) } Path compileSourcePath = null; if (getSourcepathMethod != null) { try { compileSourcePath = (Path) getSourcepathMethod.invoke(this.attributes, (Object[]) null); } catch (IllegalAccessException | InvocationTargetException e) { // should never happen } } if (compileSourcePath != null) { sourcepath = compileSourcePath; } else { sourcepath = this.src; } cmd.createArgument().setValue("-sourcepath"); //$NON-NLS-1$ createClasspathArgument(cmd, sourcepath); final String javaVersion = JavaEnvUtils.getJavaVersion(); String memoryParameterPrefix = javaVersion.equals(JavaEnvUtils.JAVA_1_1) ? "-J-" : "-J-X";//$NON-NLS-1$//$NON-NLS-2$ if (this.memoryInitialSize != null) { if (!this.attributes.isForkedJavac()) { this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.info.ignoringMemoryInitialSize"), Project.MSG_WARN); //$NON-NLS-1$ } else { cmd.createArgument().setValue(memoryParameterPrefix + "ms" + this.memoryInitialSize); //$NON-NLS-1$ } } if (this.memoryMaximumSize != null) { if (!this.attributes.isForkedJavac()) { this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.info.ignoringMemoryMaximumSize"), Project.MSG_WARN); //$NON-NLS-1$ } else { cmd.createArgument().setValue(memoryParameterPrefix + "mx" + this.memoryMaximumSize); //$NON-NLS-1$ } } if (this.debug) { // retrieve the method getSourcepath() using reflect // This is done to improve the compatibility to ant 1.5 Method getDebugLevelMethod = null; try { getDebugLevelMethod = javacClass.getMethod("getDebugLevel", (Class[]) null); //$NON-NLS-1$ } catch(NoSuchMethodException e) { // if not found, then we cannot use this method (ant 1.5) // debug level is only available with ant 1.5.x } String debugLevel = null; if (getDebugLevelMethod != null) { try { debugLevel = (String) getDebugLevelMethod.invoke(this.attributes, (Object[]) null); } catch (IllegalAccessException | InvocationTargetException e) { // should never happen } } if (debugLevel != null) { this.customDefaultOptions.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_SourceFileAttribute , CompilerOptions.DO_NOT_GENERATE); if (debugLevel.length() != 0) { if (debugLevel.indexOf("vars") != -1) {//$NON-NLS-1$ this.customDefaultOptions.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE); } if (debugLevel.indexOf("lines") != -1) {//$NON-NLS-1$ this.customDefaultOptions.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); } if (debugLevel.indexOf("source") != -1) {//$NON-NLS-1$ this.customDefaultOptions.put(CompilerOptions.OPTION_SourceFileAttribute , CompilerOptions.GENERATE); } } } else { this.customDefaultOptions.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_SourceFileAttribute , CompilerOptions.GENERATE); } } else { this.customDefaultOptions.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE); this.customDefaultOptions.put(CompilerOptions.OPTION_SourceFileAttribute , CompilerOptions.DO_NOT_GENERATE); } /* * Handle the nowarn option. If none, then we generate all warnings. */ if (this.attributes.getNowarn()) { // disable all warnings Object[] entries = this.customDefaultOptions.entrySet().toArray(); for (int i = 0, max = entries.length; i < max; i++) { Map.Entry entry = (Map.Entry) entries[i]; if (!(entry.getKey() instanceof String)) continue; if (!(entry.getValue() instanceof String)) continue; if (((String) entry.getValue()).equals(CompilerOptions.WARNING)) { this.customDefaultOptions.put(entry.getKey(), CompilerOptions.IGNORE); } } this.customDefaultOptions.put(CompilerOptions.OPTION_TaskTags, Util.EMPTY_STRING); if (this.deprecation) { this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.WARNING); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationInDeprecatedCode, CompilerOptions.ENABLED); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationWhenOverridingDeprecatedMethod, CompilerOptions.ENABLED); } } else if (this.deprecation) { this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.WARNING); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationInDeprecatedCode, CompilerOptions.ENABLED); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationWhenOverridingDeprecatedMethod, CompilerOptions.ENABLED); } else { this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationInDeprecatedCode, CompilerOptions.DISABLED); this.customDefaultOptions.put(CompilerOptions.OPTION_ReportDeprecationWhenOverridingDeprecatedMethod, CompilerOptions.DISABLED); } /* * destDir option. */ if (this.destDir != null) { cmd.createArgument().setValue("-d"); //$NON-NLS-1$ cmd.createArgument().setFile(this.destDir.getAbsoluteFile()); } /* * verbose option */ if (this.verbose) { cmd.createArgument().setValue("-verbose"); //$NON-NLS-1$ } /* * failnoerror option */ if (!this.attributes.getFailonerror()) { cmd.createArgument().setValue("-proceedOnError"); //$NON-NLS-1$ } /* * target option. */ if (this.target != null) { this.customDefaultOptions.put(CompilerOptions.OPTION_TargetPlatform, this.target); } /* * source option */ String source = this.attributes.getSource(); if (source != null) { this.customDefaultOptions.put(CompilerOptions.OPTION_Source, source); this.customDefaultOptions.put(CompilerOptions.OPTION_Compliance, source); } if (compilerArgs != null) { /* * Add extra argument on the command line */ final int length = compilerArgs.length; if (length != 0) { for (int i = 0, max = length; i < max; i++) { String arg = compilerArgs[i]; if (this.logFileName == null && "-log".equals(arg) && ((i + 1) < max)) { //$NON-NLS-1$ this.logFileName = compilerArgs[i + 1]; } cmd.createArgument().setValue(arg); } } } /* * encoding option. javac task encoding property must be the last encoding on the command * line as compiler arg might also specify an encoding. */ if (this.encoding != null) { cmd.createArgument().setValue("-encoding"); //$NON-NLS-1$ cmd.createArgument().setValue(this.encoding); } /* * Eclipse compiler doesn't have a -sourcepath option. This is * handled through the javac task that collects all source files in * srcdir option. */ logAndAddFilesToCompile(cmd); return cmd; }
Get the compiler arguments
Params:
  • javacClass –
Returns:String[] the array of arguments
/** * Get the compiler arguments * @param javacClass * @return String[] the array of arguments */
private String[] processCompilerArguments(Class javacClass) { // retrieve the method getCurrentCompilerArgs() using reflect // This is done to improve the compatibility to ant 1.5 Method getCurrentCompilerArgsMethod = null; try { getCurrentCompilerArgsMethod = javacClass.getMethod("getCurrentCompilerArgs", (Class[]) null); //$NON-NLS-1$ } catch (NoSuchMethodException e) { // if not found, then we cannot use this method (ant 1.5) // debug level is only available with ant 1.5.x } String[] compilerArgs = null; if (getCurrentCompilerArgsMethod != null) { try { compilerArgs = (String[]) getCurrentCompilerArgsMethod.invoke(this.attributes, (Object[]) null); } catch (IllegalAccessException | InvocationTargetException e) { // should never happen } } //check the compiler arguments for anything requiring extra processing if (compilerArgs != null) checkCompilerArgs(compilerArgs); return compilerArgs; }
check the compiler arguments. Extract from files specified using @, lines marked with ADAPTER_PREFIX These lines specify information that needs to be interpreted by us.
Params:
  • args – compiler arguments to process
/** * check the compiler arguments. * Extract from files specified using @, lines marked with ADAPTER_PREFIX * These lines specify information that needs to be interpreted by us. * @param args compiler arguments to process */
private void checkCompilerArgs(String[] args) { for (int i = 0; i < args.length; i++) { if (args[i].charAt(0) == '@') { try { char[] content = Util.getFileCharContent(new File(args[i].substring(1)), null); int offset = 0; int prefixLength = ADAPTER_PREFIX.length; while ((offset = CharOperation.indexOf(ADAPTER_PREFIX, content, true, offset)) > -1) { int start = offset + prefixLength; int end = CharOperation.indexOf('\n', content, start); if (end == -1) end = content.length; while (CharOperation.isWhitespace(content[end])) { end--; } // end is inclusive, but in the API end is exclusive if (CharOperation.equals(ADAPTER_ENCODING, content, start, start + ADAPTER_ENCODING.length)) { CharOperation.replace(content, SEPARATOR_CHARS, File.separatorChar, start, end + 1); // file or folder level custom encoding start += ADAPTER_ENCODING.length; int encodeStart = CharOperation.lastIndexOf('[', content, start, end); if (start < encodeStart && encodeStart < end) { boolean isFile = CharOperation.equals(SuffixConstants.SUFFIX_java, content, encodeStart - 5, encodeStart, false); String str = String.valueOf(content, start, encodeStart - start); String enc = String.valueOf(content, encodeStart, end - encodeStart + 1); if (isFile) { if (this.fileEncodings == null) this.fileEncodings = new HashMap(); //use File to translate the string into a path with the correct File.seperator this.fileEncodings.put(str, enc); } else { if (this.dirEncodings == null) this.dirEncodings = new HashMap(); this.dirEncodings.put(str, enc); } } } else if (CharOperation.equals(ADAPTER_ACCESS, content, start, start + ADAPTER_ACCESS.length)) { // access rules for the classpath start += ADAPTER_ACCESS.length; int accessStart = CharOperation.indexOf('[', content, start, end); CharOperation.replace(content, SEPARATOR_CHARS, File.separatorChar, start, accessStart); if (start < accessStart && accessStart < end) { String path = String.valueOf(content, start, accessStart - start); String access = String.valueOf(content, accessStart, end - accessStart + 1); if (this.accessRules == null) this.accessRules = new ArrayList(); this.accessRules.add(path); this.accessRules.add(access); } } offset = end; } } catch (IOException e) { //ignore } } } }
Copy the classpath to the command line with access rules included.
Params:
  • cmd – the given command line
  • classpath – the given classpath entry
/** * Copy the classpath to the command line with access rules included. * @param cmd the given command line * @param classpath the given classpath entry */
private void createClasspathArgument(Commandline cmd, Path classpath) { Argument arg = cmd.createArgument(); final String[] pathElements = classpath.list(); // empty path return empty string if (pathElements.length == 0) { arg.setValue(Util.EMPTY_STRING); return; } // no access rules, can set the path directly if (this.accessRules == null) { arg.setPath(classpath); return; } int rulesLength = this.accessRules.size(); String[] rules = (String[]) this.accessRules.toArray(new String[rulesLength]); int nextRule = 0; final StringBuffer result = new StringBuffer(); //access rules are expected in the same order as the classpath, but there could //be elements in the classpath not in the access rules or access rules not in the classpath for (int i = 0, max = pathElements.length; i < max; i++) { if (i > 0) result.append(File.pathSeparatorChar); String pathElement = pathElements[i]; result.append(pathElement); //the rules list is [path, rule, path, rule, ...] for (int j = nextRule; j < rulesLength; j += 2) { String rule = rules[j]; if (pathElement.endsWith(rule)) { result.append(rules[j + 1]); nextRule = j + 2; break; } // if the path doesn't match, it could be due to a trailing file separatorChar in the rule if (rule.endsWith(File.separator)) { // rule ends with the File.separator, but pathElement might not // otherwise it would match on the first endsWith int ruleLength = rule.length(); if (pathElement.regionMatches(false, pathElement.length() - ruleLength + 1, rule, 0, ruleLength - 1)) { result.append(rules[j + 1]); nextRule = j + 2; break; } } else if (pathElement.endsWith(File.separator)) { // rule doesn't end with the File.separator, but pathElement might int ruleLength = rule.length(); if (pathElement.regionMatches(false, pathElement.length() - ruleLength - 1, rule, 0, ruleLength)) { result.append(rules[j + 1]); nextRule = j + 2; break; } } } } arg.setValue(result.toString()); }
Modified from base class, Logs the compilation parameters, adds the files to compile and logs the "niceSourceList" Appends encoding information at the end of arguments
Params:
  • cmd – the given command line
/** * Modified from base class, Logs the compilation parameters, adds the files * to compile and logs the &quot;niceSourceList&quot; * Appends encoding information at the end of arguments * * @param cmd the given command line */
@Override protected void logAndAddFilesToCompile(Commandline cmd) { this.attributes.log("Compilation " + cmd.describeArguments(), //$NON-NLS-1$ Project.MSG_VERBOSE); StringBuffer niceSourceList = new StringBuffer("File"); //$NON-NLS-1$ if (this.compileList.length != 1) { niceSourceList.append("s"); //$NON-NLS-1$ } niceSourceList.append(" to be compiled:"); //$NON-NLS-1$ niceSourceList.append(System.lineSeparator()); String[] encodedFiles = null, encodedDirs = null; int encodedFilesLength = 0, encodedDirsLength = 0; if (this.fileEncodings != null) { encodedFilesLength = this.fileEncodings.size(); encodedFiles = new String[encodedFilesLength]; this.fileEncodings.keySet().toArray(encodedFiles); } if (this.dirEncodings != null) { encodedDirsLength = this.dirEncodings.size(); encodedDirs = new String[encodedDirsLength]; this.dirEncodings.keySet().toArray(encodedDirs); //we need the directories sorted, longest first,since sub directories can //override encodings for their parent directories Comparator comparator = new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String) o2).length() - ((String) o1).length(); } }; Arrays.sort(encodedDirs, comparator); } for (int i = 0; i < this.compileList.length; i++) { String arg = this.compileList[i].getAbsolutePath(); boolean encoded = false; if (encodedFiles != null) { //check for file level custom encoding for (int j = 0; j < encodedFilesLength; j++) { if (arg.endsWith(encodedFiles[j])) { //found encoding, remove it from the list to speed things up next time around arg = arg + (String) this.fileEncodings.get(encodedFiles[j]); if (j < encodedFilesLength - 1) { System.arraycopy(encodedFiles, j + 1, encodedFiles, j, encodedFilesLength - j - 1); } encodedFiles[--encodedFilesLength] = null; encoded = true; break; } } } if (!encoded && encodedDirs != null) { //check folder level custom encoding for (int j = 0; j < encodedDirsLength; j++) { if (arg.lastIndexOf(encodedDirs[j]) != -1) { arg = arg + (String) this.dirEncodings.get(encodedDirs[j]); break; } } } cmd.createArgument().setValue(arg); niceSourceList.append(" " + arg + System.lineSeparator()); //$NON-NLS-1$ } this.attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE); } }