| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| 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; |
| import org.apache.tools.ant.MagicNames; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter; |
| import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterExtension; |
| import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.Reference; |
| import org.apache.tools.ant.util.FileUtils; |
| import org.apache.tools.ant.util.GlobPatternMapper; |
| import org.apache.tools.ant.util.JavaEnvUtils; |
| import org.apache.tools.ant.util.SourceFileScanner; |
| import org.apache.tools.ant.util.facade.FacadeTaskHelper; |
| |
| /** |
| * Compiles Java source files. This task can take the following |
| * arguments: |
| * <ul> |
| * <li>sourcedir |
| * <li>destdir |
| * <li>deprecation |
| * <li>classpath |
| * <li>bootclasspath |
| * <li>extdirs |
| * <li>optimize |
| * <li>debug |
| * <li>encoding |
| * <li>target |
| * <li>depend |
| * <li>verbose |
| * <li>failonerror |
| * <li>includeantruntime |
| * <li>includejavaruntime |
| * <li>source |
| * <li>compiler |
| * </ul> |
| * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required. |
| * <p> |
| * When this task executes, it will recursively scan the sourcedir and |
| * destdir looking for Java source files to compile. This task makes its |
| * compile decision based on timestamp. |
| * |
| * |
| * @since Ant 1.1 |
| * |
| * @ant.task category="java" |
| */ |
| |
| public class Javac extends MatchingTask { |
| |
| private static final String FAIL_MSG |
| = "Compile failed; see the compiler error output for details."; |
| |
| private static final String JAVAC9 = "javac9"; |
| private static final String JAVAC19 = "javac1.9"; |
| private static final String JAVAC18 = "javac1.8"; |
| private static final String JAVAC17 = "javac1.7"; |
| private static final String JAVAC16 = "javac1.6"; |
| private static final String JAVAC15 = "javac1.5"; |
| private static final String JAVAC14 = "javac1.4"; |
| private static final String JAVAC13 = "javac1.3"; |
| private static final String JAVAC12 = "javac1.2"; |
| private static final String JAVAC11 = "javac1.1"; |
| private static final String MODERN = "modern"; |
| 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 File nativeHeaderDir; |
| 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; |
| private boolean deprecation = false; |
| private boolean depend = false; |
| private boolean verbose = false; |
| private String targetAttribute; |
| private Path bootclasspath; |
| private Path extdirs; |
| private Boolean includeAntRuntime; |
| private boolean includeJavaRuntime = false; |
| private boolean fork = false; |
| private String forkedExecutable = null; |
| private boolean nowarn = false; |
| private String memoryInitialSize; |
| private String memoryMaximumSize; |
| private FacadeTaskHelper facade = null; |
| |
| // CheckStyle:VisibilityModifier OFF - bc |
| protected boolean failOnError = true; |
| protected boolean listFiles = false; |
| protected File[] compileList = new File[0]; |
| private Map<String, Long> packageInfos = new HashMap<String, Long>(); |
| // CheckStyle:VisibilityModifier ON |
| |
| private String source; |
| private String debugLevel; |
| private File tmpDir; |
| private String updatedProperty; |
| private String errorProperty; |
| private boolean taskSuccess = true; // assume the best |
| private boolean includeDestClasses = true; |
| private CompilerAdapter nestedAdapter = null; |
| |
| private boolean createMissingPackageInfoClass = true; |
| |
| /** |
| * Javac task for compilation of Java files. |
| */ |
| public Javac() { |
| facade = new FacadeTaskHelper(assumedJavaVersion()); |
| } |
| |
| private String assumedJavaVersion() { |
| if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4)) { |
| return JAVAC14; |
| } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5)) { |
| return JAVAC15; |
| } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6)) { |
| return JAVAC16; |
| } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_7)) { |
| return JAVAC17; |
| } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_8)) { |
| return JAVAC18; |
| } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_9)) { |
| return JAVAC9; |
| } else { |
| return CLASSIC; |
| } |
| } |
| |
| /** |
| * Get the value of debugLevel. |
| * @return value of debugLevel. |
| */ |
| public String getDebugLevel() { |
| return debugLevel; |
| } |
| |
| /** |
| * Keyword list to be appended to the -g command-line switch. |
| * |
| * This will be ignored by all implementations except modern |
| * and classic(ver >= 1.2). Legal values are none or a |
| * comma-separated list of the following keywords: lines, vars, |
| * and source. If debuglevel is not specified, by default, :none |
| * will be appended to -g. If debug is not turned on, this attribute |
| * will be ignored. |
| * |
| * @param v Value to assign to debugLevel. |
| */ |
| public void setDebugLevel(final String v) { |
| this.debugLevel = v; |
| } |
| |
| /** |
| * Get the value of source. |
| * @return value of source. |
| */ |
| public String getSource() { |
| return source != null |
| ? source : getProject().getProperty(MagicNames.BUILD_JAVAC_SOURCE); |
| } |
| |
| /** |
| * Value of the -source command-line switch; will be ignored by |
| * all implementations except modern, jikes and gcj (gcj uses |
| * -fsource). |
| * |
| * <p>If you use this attribute together with jikes or gcj, you |
| * must make sure that your version of jikes supports the -source |
| * switch.</p> |
| * |
| * <p>Legal values are 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, and 5, 6, 7, 8 and 9 |
| * - by default, no -source argument will be used at all.</p> |
| * |
| * @param v Value to assign to source. |
| */ |
| public void setSource(final String v) { |
| this.source = v; |
| } |
| |
| /** |
| * Adds a path for source compilation. |
| * |
| * @return a nested src element. |
| */ |
| public Path createSrc() { |
| if (src == null) { |
| src = new Path(getProject()); |
| } |
| return src.createPath(); |
| } |
| |
| /** |
| * Recreate src. |
| * |
| * @return a nested src element. |
| */ |
| protected Path recreateSrc() { |
| src = null; |
| return createSrc(); |
| } |
| |
| /** |
| * Set the source directories to find the source Java files. |
| * @param srcDir the source directories as a path |
| */ |
| public void setSrcdir(final Path srcDir) { |
| if (src == null) { |
| src = srcDir; |
| } else { |
| src.append(srcDir); |
| } |
| } |
| |
| /** |
| * Gets the source dirs to find the source java files. |
| * @return the source directories as a path |
| */ |
| public Path getSrcdir() { |
| return src; |
| } |
| |
| /** |
| * Set the destination directory into which the Java source |
| * files should be compiled. |
| * @param destDir the destination director |
| */ |
| public void setDestdir(final File destDir) { |
| this.destDir = destDir; |
| } |
| |
| /** |
| * Gets the destination directory into which the java source files |
| * should be compiled. |
| * @return the destination directory |
| */ |
| public File getDestdir() { |
| return destDir; |
| } |
| |
| /** |
| * Set the destination directory into which the generated native |
| * header files should be placed. |
| * @param nhDir where to place generated native header files |
| * @since Ant 1.9.8 |
| */ |
| public void setNativeHeaderDir(final File nhDir) { |
| this.nativeHeaderDir = nhDir; |
| } |
| |
| /** |
| * Gets the destination directory into which the generated native |
| * header files should be placed. |
| * @return where to place generated native header files |
| * @since Ant 1.9.8 |
| */ |
| public File getNativeHeaderDir() { |
| return nativeHeaderDir; |
| } |
| |
| /** |
| * Set the sourcepath to be used for this compilation. |
| * @param sourcepath the source path |
| */ |
| public void setSourcepath(final Path sourcepath) { |
| if (compileSourcepath == null) { |
| compileSourcepath = sourcepath; |
| } else { |
| compileSourcepath.append(sourcepath); |
| } |
| } |
| |
| /** |
| * Gets the sourcepath to be used for this compilation. |
| * @return the source path |
| */ |
| public Path getSourcepath() { |
| return compileSourcepath; |
| } |
| |
| /** |
| * Adds a path to sourcepath. |
| * @return a sourcepath to be configured |
| */ |
| public Path createSourcepath() { |
| if (compileSourcepath == null) { |
| compileSourcepath = new Path(getProject()); |
| } |
| return compileSourcepath.createPath(); |
| } |
| |
| /** |
| * Adds a reference to a source path defined elsewhere. |
| * @param r a reference to a source path |
| */ |
| public void setSourcepathRef(final Reference 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. |
| * |
| * @param classpath an Ant Path object containing the compilation classpath. |
| */ |
| public void setClasspath(final Path classpath) { |
| if (compileClasspath == null) { |
| compileClasspath = classpath; |
| } else { |
| compileClasspath.append(classpath); |
| } |
| } |
| |
| /** |
| * Gets the classpath to be used for this compilation. |
| * @return the class path |
| */ |
| public Path getClasspath() { |
| return compileClasspath; |
| } |
| |
| /** |
| * Adds a path to the classpath. |
| * @return a class path to be configured |
| */ |
| public Path createClasspath() { |
| if (compileClasspath == null) { |
| compileClasspath = new Path(getProject()); |
| } |
| return compileClasspath.createPath(); |
| } |
| |
| /** |
| * Adds a reference to a classpath defined elsewhere. |
| * @param r a reference to a classpath |
| */ |
| public void setClasspathRef(final Reference 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 |
| * against. |
| * @param bootclasspath a path to use as a boot class path (may be more |
| * than one) |
| */ |
| public void setBootclasspath(final Path bootclasspath) { |
| if (this.bootclasspath == null) { |
| this.bootclasspath = bootclasspath; |
| } else { |
| this.bootclasspath.append(bootclasspath); |
| } |
| } |
| |
| /** |
| * Gets the bootclasspath that will be used to compile the classes |
| * against. |
| * @return the boot path |
| */ |
| public Path getBootclasspath() { |
| return bootclasspath; |
| } |
| |
| /** |
| * Adds a path to the bootclasspath. |
| * @return a path to be configured |
| */ |
| public Path createBootclasspath() { |
| if (bootclasspath == null) { |
| bootclasspath = new Path(getProject()); |
| } |
| return bootclasspath.createPath(); |
| } |
| |
| /** |
| * Adds a reference to a classpath defined elsewhere. |
| * @param r a reference to a classpath |
| */ |
| public void setBootClasspathRef(final Reference r) { |
| createBootclasspath().setRefid(r); |
| } |
| |
| /** |
| * Sets the extension directories that will be used during the |
| * compilation. |
| * @param extdirs a path |
| */ |
| public void setExtdirs(final Path extdirs) { |
| if (this.extdirs == null) { |
| this.extdirs = extdirs; |
| } else { |
| this.extdirs.append(extdirs); |
| } |
| } |
| |
| /** |
| * Gets the extension directories that will be used during the |
| * compilation. |
| * @return the extension directories as a path |
| */ |
| public Path getExtdirs() { |
| return extdirs; |
| } |
| |
| /** |
| * Adds a path to extdirs. |
| * @return a path to be configured |
| */ |
| public Path createExtdirs() { |
| if (extdirs == null) { |
| extdirs = new Path(getProject()); |
| } |
| return extdirs.createPath(); |
| } |
| |
| /** |
| * If true, list the source files being handed off to the compiler. |
| * @param list if true list the source files |
| */ |
| public void setListfiles(final boolean list) { |
| listFiles = list; |
| } |
| |
| /** |
| * Get the listfiles flag. |
| * @return the listfiles flag |
| */ |
| public boolean getListfiles() { |
| return listFiles; |
| } |
| |
| /** |
| * Indicates whether the build will continue |
| * even if there are compilation errors; defaults to true. |
| * @param fail if true halt the build on failure |
| */ |
| public void setFailonerror(final boolean fail) { |
| failOnError = fail; |
| } |
| |
| /** |
| * @ant.attribute ignore="true" |
| * @param proceed inverse of failoferror |
| */ |
| public void setProceed(final boolean proceed) { |
| failOnError = !proceed; |
| } |
| |
| /** |
| * Gets the failonerror flag. |
| * @return the failonerror flag |
| */ |
| public boolean getFailonerror() { |
| return failOnError; |
| } |
| |
| /** |
| * Indicates whether source should be |
| * compiled with deprecation information; defaults to off. |
| * @param deprecation if true turn on deprecation information |
| */ |
| public void setDeprecation(final boolean deprecation) { |
| this.deprecation = deprecation; |
| } |
| |
| /** |
| * Gets the deprecation flag. |
| * @return the deprecation flag |
| */ |
| public boolean getDeprecation() { |
| return deprecation; |
| } |
| |
| /** |
| * The initial size of the memory for the underlying VM |
| * if javac is run externally; ignored otherwise. |
| * Defaults to the standard VM memory setting. |
| * (Examples: 83886080, 81920k, or 80m) |
| * @param memoryInitialSize string to pass to VM |
| */ |
| public void setMemoryInitialSize(final String memoryInitialSize) { |
| this.memoryInitialSize = memoryInitialSize; |
| } |
| |
| /** |
| * Gets the memoryInitialSize flag. |
| * @return the memoryInitialSize flag |
| */ |
| public String getMemoryInitialSize() { |
| return memoryInitialSize; |
| } |
| |
| /** |
| * The maximum size of the memory for the underlying VM |
| * if javac is run externally; ignored otherwise. |
| * Defaults to the standard VM memory setting. |
| * (Examples: 83886080, 81920k, or 80m) |
| * @param memoryMaximumSize string to pass to VM |
| */ |
| public void setMemoryMaximumSize(final String memoryMaximumSize) { |
| this.memoryMaximumSize = memoryMaximumSize; |
| } |
| |
| /** |
| * Gets the memoryMaximumSize flag. |
| * @return the memoryMaximumSize flag |
| */ |
| public String getMemoryMaximumSize() { |
| return memoryMaximumSize; |
| } |
| |
| /** |
| * Set the Java source file encoding name. |
| * @param encoding the source file encoding |
| */ |
| public void setEncoding(final String encoding) { |
| this.encoding = encoding; |
| } |
| |
| /** |
| * Gets the java source file encoding name. |
| * @return the source file encoding name |
| */ |
| public String getEncoding() { |
| return encoding; |
| } |
| |
| /** |
| * Indicates whether source should be compiled |
| * with debug information; defaults to off. |
| * @param debug if true compile with debug information |
| */ |
| public void setDebug(final boolean debug) { |
| this.debug = debug; |
| } |
| |
| /** |
| * Gets the debug flag. |
| * @return the debug flag |
| */ |
| public boolean getDebug() { |
| return debug; |
| } |
| |
| /** |
| * If true, compiles with optimization enabled. |
| * @param optimize if true compile with optimization enabled |
| */ |
| public void setOptimize(final boolean optimize) { |
| this.optimize = optimize; |
| } |
| |
| /** |
| * Gets the optimize flag. |
| * @return the optimize flag |
| */ |
| public boolean getOptimize() { |
| return optimize; |
| } |
| |
| /** |
| * Enables dependency-tracking for compilers |
| * that support this (jikes and classic). |
| * @param depend if true enable dependency-tracking |
| */ |
| public void setDepend(final boolean depend) { |
| this.depend = depend; |
| } |
| |
| /** |
| * Gets the depend flag. |
| * @return the depend flag |
| */ |
| public boolean getDepend() { |
| return depend; |
| } |
| |
| /** |
| * If true, asks the compiler for verbose output. |
| * @param verbose if true, asks the compiler for verbose output |
| */ |
| public void setVerbose(final boolean verbose) { |
| this.verbose = verbose; |
| } |
| |
| /** |
| * Gets the verbose flag. |
| * @return the verbose flag |
| */ |
| public boolean getVerbose() { |
| return verbose; |
| } |
| |
| /** |
| * Sets the target VM that the classes will be compiled for. Valid |
| * values depend on the compiler, for jdk 1.4 the valid values are |
| * "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "5", "6", "7", "8", "9". |
| * @param target the target VM |
| */ |
| public void setTarget(final String target) { |
| this.targetAttribute = target; |
| } |
| |
| /** |
| * Gets the target VM that the classes will be compiled for. |
| * @return the target VM |
| */ |
| public String getTarget() { |
| return targetAttribute != null |
| ? targetAttribute |
| : getProject().getProperty(MagicNames.BUILD_JAVAC_TARGET); |
| } |
| |
| /** |
| * If true, includes Ant's own classpath in the classpath. |
| * @param include if true, includes Ant's own classpath in the classpath |
| */ |
| public void setIncludeantruntime(final boolean include) { |
| includeAntRuntime = Boolean.valueOf(include); |
| } |
| |
| /** |
| * Gets whether or not the ant classpath is to be included in the classpath. |
| * @return whether or not the ant classpath is to be included in the classpath |
| */ |
| public boolean getIncludeantruntime() { |
| return includeAntRuntime != null ? includeAntRuntime.booleanValue() : true; |
| } |
| |
| /** |
| * If true, includes the Java runtime libraries in the classpath. |
| * @param include if true, includes the Java runtime libraries in the classpath |
| */ |
| public void setIncludejavaruntime(final boolean include) { |
| includeJavaRuntime = include; |
| } |
| |
| /** |
| * Gets whether or not the java runtime should be included in this |
| * task's classpath. |
| * @return the includejavaruntime attribute |
| */ |
| public boolean getIncludejavaruntime() { |
| return includeJavaRuntime; |
| } |
| |
| /** |
| * If true, forks the javac compiler. |
| * |
| * @param f "true|false|on|off|yes|no" |
| */ |
| public void setFork(final boolean f) { |
| fork = f; |
| } |
| |
| /** |
| * Sets the name of the javac executable. |
| * |
| * <p>Ignored unless fork is true or extJavac has been specified |
| * as the compiler.</p> |
| * @param forkExec the name of the executable |
| */ |
| public void setExecutable(final String forkExec) { |
| forkedExecutable = forkExec; |
| } |
| |
| /** |
| * The value of the executable attribute, if any. |
| * |
| * @since Ant 1.6 |
| * @return the name of the java executable |
| */ |
| public String getExecutable() { |
| return forkedExecutable; |
| } |
| |
| /** |
| * Is this a forked invocation of JDK's javac? |
| * @return true if this is a forked invocation |
| */ |
| public boolean isForkedJavac() { |
| return fork || EXTJAVAC.equalsIgnoreCase(getCompiler()); |
| } |
| |
| /** |
| * The name of the javac executable to use in fork-mode. |
| * |
| * <p>This is either the name specified with the executable |
| * attribute or the full path of the javac compiler of the VM Ant |
| * is currently running in - guessed by Ant.</p> |
| * |
| * <p>You should <strong>not</strong> invoke this method if you |
| * want to get the value of the executable command - use {@link |
| * #getExecutable getExecutable} for this.</p> |
| * @return the name of the javac executable |
| */ |
| public String getJavacExecutable() { |
| if (forkedExecutable == null && isForkedJavac()) { |
| forkedExecutable = getSystemJavac(); |
| } else if (forkedExecutable != null && !isForkedJavac()) { |
| forkedExecutable = null; |
| } |
| return forkedExecutable; |
| } |
| |
| /** |
| * If true, enables the -nowarn option. |
| * @param flag if true, enable the -nowarn option |
| */ |
| public void setNowarn(final boolean flag) { |
| this.nowarn = flag; |
| } |
| |
| /** |
| * Should the -nowarn option be used. |
| * @return true if the -nowarn option should be used |
| */ |
| public boolean getNowarn() { |
| return nowarn; |
| } |
| |
| /** |
| * Adds an implementation specific command-line argument. |
| * @return a ImplementationSpecificArgument to be configured |
| */ |
| public ImplementationSpecificArgument createCompilerArg() { |
| final ImplementationSpecificArgument arg = |
| new ImplementationSpecificArgument(); |
| facade.addImplementationArgument(arg); |
| return arg; |
| } |
| |
| /** |
| * Get the additional implementation specific command line arguments. |
| * @return array of command line arguments, guaranteed to be non-null. |
| */ |
| public String[] getCurrentCompilerArgs() { |
| final String chosen = facade.getExplicitChoice(); |
| try { |
| // make sure facade knows about magic properties and fork setting |
| final String appliedCompiler = getCompiler(); |
| facade.setImplementation(appliedCompiler); |
| |
| String[] result = facade.getArgs(); |
| |
| final String altCompilerName = getAltCompilerName(facade.getImplementation()); |
| |
| if (result.length == 0 && altCompilerName != null) { |
| facade.setImplementation(altCompilerName); |
| result = facade.getArgs(); |
| } |
| |
| return result; |
| |
| } finally { |
| facade.setImplementation(chosen); |
| } |
| } |
| |
| private String getAltCompilerName(final String anImplementation) { |
| if (JAVAC9.equalsIgnoreCase(anImplementation) |
| || JAVAC19.equalsIgnoreCase(anImplementation) |
| || JAVAC18.equalsIgnoreCase(anImplementation) |
| || JAVAC17.equalsIgnoreCase(anImplementation) |
| || JAVAC16.equalsIgnoreCase(anImplementation) |
| || JAVAC15.equalsIgnoreCase(anImplementation) |
| || JAVAC14.equalsIgnoreCase(anImplementation) |
| || JAVAC13.equalsIgnoreCase(anImplementation)) { |
| return MODERN; |
| } |
| if (JAVAC12.equalsIgnoreCase(anImplementation) |
| || JAVAC11.equalsIgnoreCase(anImplementation)) { |
| return CLASSIC; |
| } |
| if (MODERN.equalsIgnoreCase(anImplementation)) { |
| final String nextSelected = assumedJavaVersion(); |
| if (JAVAC9.equalsIgnoreCase(nextSelected) |
| || JAVAC18.equalsIgnoreCase(nextSelected) |
| || JAVAC17.equalsIgnoreCase(nextSelected) |
| || JAVAC16.equalsIgnoreCase(nextSelected) |
| || JAVAC15.equalsIgnoreCase(nextSelected) |
| || JAVAC14.equalsIgnoreCase(nextSelected) |
| || JAVAC13.equalsIgnoreCase(nextSelected)) { |
| return nextSelected; |
| } |
| } |
| if (CLASSIC.equalsIgnoreCase(anImplementation)) { |
| return assumedJavaVersion(); |
| } |
| if (EXTJAVAC.equalsIgnoreCase(anImplementation)) { |
| return assumedJavaVersion(); |
| } |
| return null; |
| } |
| |
| /** |
| * Where Ant should place temporary files. |
| * |
| * @since Ant 1.6 |
| * @param tmpDir the temporary directory |
| */ |
| public void setTempdir(final File tmpDir) { |
| this.tmpDir = tmpDir; |
| } |
| |
| /** |
| * Where Ant should place temporary files. |
| * |
| * @since Ant 1.6 |
| * @return the temporary directory |
| */ |
| public File getTempdir() { |
| return tmpDir; |
| } |
| |
| /** |
| * The property to set on compilation success. |
| * This property will not be set if the compilation |
| * fails, or if there are no files to compile. |
| * @param updatedProperty the property name to use. |
| * @since Ant 1.7.1. |
| */ |
| public void setUpdatedProperty(final String updatedProperty) { |
| this.updatedProperty = updatedProperty; |
| } |
| |
| /** |
| * The property to set on compilation failure. |
| * This property will be set if the compilation |
| * fails. |
| * @param errorProperty the property name to use. |
| * @since Ant 1.7.1. |
| */ |
| public void setErrorProperty(final String errorProperty) { |
| this.errorProperty = errorProperty; |
| } |
| |
| /** |
| * This property controls whether to include the |
| * destination classes directory in the classpath |
| * given to the compiler. |
| * The default value is "true". |
| * @param includeDestClasses the value to use. |
| */ |
| public void setIncludeDestClasses(final boolean includeDestClasses) { |
| this.includeDestClasses = includeDestClasses; |
| } |
| |
| /** |
| * Get the value of the includeDestClasses property. |
| * @return the value. |
| */ |
| public boolean isIncludeDestClasses() { |
| return includeDestClasses; |
| } |
| |
| /** |
| * Get the result of the javac task (success or failure). |
| * @return true if compilation succeeded, or |
| * was not necessary, false if the compilation failed. |
| */ |
| public boolean getTaskSuccess() { |
| return taskSuccess; |
| } |
| |
| /** |
| * The classpath to use when loading the compiler implementation |
| * if it is not a built-in one. |
| * |
| * @since Ant 1.8.0 |
| */ |
| public Path createCompilerClasspath() { |
| return facade.getImplementationClasspath(getProject()); |
| } |
| |
| /** |
| * Set the compiler adapter explicitly. |
| * @since Ant 1.8.0 |
| */ |
| public void add(final CompilerAdapter adapter) { |
| if (nestedAdapter != null) { |
| throw new BuildException("Can't have more than one compiler" |
| + " adapter"); |
| } |
| nestedAdapter = adapter; |
| } |
| |
| /** |
| * Whether package-info.class files will be created by Ant |
| * matching package-info.java files that have been compiled but |
| * didn't create class files themselves. |
| * |
| * @since Ant 1.8.3 |
| */ |
| public void setCreateMissingPackageInfoClass(final boolean b) { |
| createMissingPackageInfoClass = b; |
| } |
| |
| /** |
| * Executes the task. |
| * @exception BuildException if an error occurs |
| */ |
| @Override |
| public void execute() throws BuildException { |
| checkParameters(); |
| resetFileLists(); |
| |
| // scan source directories and dest directory to build up |
| // compile list |
| if (hasPath(src)) { |
| collectFileListFromSourcePath(); |
| } else { |
| assert hasPath(moduleSourcepath) : "Either srcDir or moduleSourcepath must be given"; |
| collectFileListFromModulePath(); |
| } |
| |
| compile(); |
| if (updatedProperty != null |
| && taskSuccess |
| && compileList.length != 0) { |
| getProject().setNewProperty(updatedProperty, "true"); |
| } |
| } |
| |
| /** |
| * Clear the list of files to be compiled and copied.. |
| */ |
| protected void resetFileLists() { |
| compileList = new File[0]; |
| packageInfos = new HashMap<String, Long>(); |
| } |
| |
| /** |
| * Scans the directory looking for source files to be compiled. |
| * The results are returned in the class variable compileList |
| * |
| * @param srcDir The source directory |
| * @param destDir The destination directory |
| * @param files An array of filenames |
| */ |
| protected void scanDir(final File srcDir, final File destDir, final String[] files) { |
| final GlobPatternMapper m = new GlobPatternMapper(); |
| final String[] extensions = findSupportedFileExtensions(); |
| |
| for (int i = 0; i < extensions.length; i++) { |
| m.setFrom(extensions[i]); |
| m.setTo("*.class"); |
| final SourceFileScanner sfs = new SourceFileScanner(this); |
| final File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m); |
| |
| if (newFiles.length > 0) { |
| lookForPackageInfos(srcDir, newFiles); |
| final File[] newCompileList |
| = new File[compileList.length + newFiles.length]; |
| System.arraycopy(compileList, 0, newCompileList, 0, |
| compileList.length); |
| System.arraycopy(newFiles, 0, newCompileList, |
| compileList.length, newFiles.length); |
| compileList = newCompileList; |
| } |
| } |
| } |
| |
| private void collectFileListFromSourcePath() { |
| 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(); |
| |
| scanDir(srcDir, destDir != null ? destDir : srcDir, files); |
| } |
| } |
| |
| private void collectFileListFromModulePath() { |
| 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()); |
| } |
| } |
| } |
| |
| private String[] findSupportedFileExtensions() { |
| final String compilerImpl = getCompiler(); |
| final CompilerAdapter adapter = |
| nestedAdapter != null ? nestedAdapter : |
| CompilerAdapterFactory.getCompiler(compilerImpl, this, |
| createCompilerClasspath()); |
| String[] extensions = null; |
| if (adapter instanceof CompilerAdapterExtension) { |
| extensions = |
| ((CompilerAdapterExtension) adapter).getSupportedFileExtensions(); |
| } |
| |
| if (extensions == null) { |
| extensions = new String[] {"java"}; |
| } |
| |
| // now process the extensions to ensure that they are the |
| // right format |
| for (int i = 0; i < extensions.length; i++) { |
| if (!extensions[i].startsWith("*.")) { |
| extensions[i] = "*." + extensions[i]; |
| } |
| } |
| return extensions; |
| } |
| |
| /** |
| * Gets the list of files to be compiled. |
| * @return the list of files as an array |
| */ |
| public File[] getFileList() { |
| return compileList; |
| } |
| |
| /** |
| * Is the compiler implementation a jdk compiler |
| * |
| * @param compilerImpl the name of the compiler implementation |
| * @return true if compilerImpl is "modern", "classic", |
| * "javac1.1", "javac1.2", "javac1.3", "javac1.4", "javac1.5", |
| * "javac1.6", "javac1.7", "javac1.8", "javac1.9" or "javac9". |
| */ |
| protected boolean isJdkCompiler(final String compilerImpl) { |
| return MODERN.equals(compilerImpl) |
| || CLASSIC.equals(compilerImpl) |
| || JAVAC9.equals(compilerImpl) |
| || JAVAC18.equals(compilerImpl) |
| || JAVAC17.equals(compilerImpl) |
| || JAVAC16.equals(compilerImpl) |
| || JAVAC15.equals(compilerImpl) |
| || JAVAC14.equals(compilerImpl) |
| || JAVAC13.equals(compilerImpl) |
| || JAVAC12.equals(compilerImpl) |
| || JAVAC11.equals(compilerImpl); |
| } |
| |
| /** |
| * @return the executable name of the java compiler |
| */ |
| protected String getSystemJavac() { |
| return JavaEnvUtils.getJdkExecutable("javac"); |
| } |
| |
| /** |
| * Choose the implementation for this particular task. |
| * @param compiler the name of the compiler |
| * @since Ant 1.5 |
| */ |
| public void setCompiler(final String compiler) { |
| facade.setImplementation(compiler); |
| } |
| |
| /** |
| * The implementation for this particular task. |
| * |
| * <p>Defaults to the build.compiler property but can be overridden |
| * via the compiler and fork attributes.</p> |
| * |
| * <p>If fork has been set to true, the result will be extJavac |
| * and not classic or java1.2 - no matter what the compiler |
| * attribute looks like.</p> |
| * |
| * @see #getCompilerVersion |
| * @return the compiler. |
| * @since Ant 1.5 |
| */ |
| public String getCompiler() { |
| String compilerImpl = getCompilerVersion(); |
| if (fork) { |
| if (isJdkCompiler(compilerImpl)) { |
| compilerImpl = EXTJAVAC; |
| } else { |
| log("Since compiler setting isn't classic or modern, " |
| + "ignoring fork setting.", Project.MSG_WARN); |
| } |
| } |
| return compilerImpl; |
| } |
| |
| /** |
| * The implementation for this particular task. |
| * |
| * <p>Defaults to the build.compiler property but can be overridden |
| * via the compiler attribute.</p> |
| * |
| * <p>This method does not take the fork attribute into |
| * account.</p> |
| * |
| * @see #getCompiler |
| * @return the compiler. |
| * |
| * @since Ant 1.5 |
| */ |
| public String getCompilerVersion() { |
| facade.setMagicValue(getProject().getProperty("build.compiler")); |
| return facade.getImplementation(); |
| } |
| |
| /** |
| * Check that all required attributes have been set and nothing |
| * silly has been entered. |
| * |
| * @since Ant 1.5 |
| * @exception BuildException if an error occurs |
| */ |
| protected void checkParameters() throws BuildException { |
| 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()) { |
| throw new BuildException("destination directory \"" |
| + destDir |
| + "\" does not exist " |
| + "or is not a directory", getLocation()); |
| } |
| if (includeAntRuntime == null && getProject().getProperty("build.sysclasspath") == null) { |
| log(getLocation() + "warning: 'includeantruntime' was not set, " + |
| "defaulting to build.sysclasspath=last; set to false for repeatable builds", |
| Project.MSG_WARN); |
| } |
| } |
| |
| /** |
| * Perform the compilation. |
| * |
| * @since Ant 1.5 |
| */ |
| protected void compile() { |
| final String compilerImpl = getCompiler(); |
| |
| if (compileList.length > 0) { |
| log("Compiling " + compileList.length + " source file" |
| + (compileList.length == 1 ? "" : "s") |
| + (destDir != null ? " to " + destDir : "")); |
| |
| if (listFiles) { |
| for (int i = 0; i < compileList.length; i++) { |
| final String filename = compileList[i].getAbsolutePath(); |
| log(filename); |
| } |
| } |
| |
| final CompilerAdapter adapter = |
| nestedAdapter != null ? nestedAdapter : |
| CompilerAdapterFactory.getCompiler(compilerImpl, this, |
| createCompilerClasspath()); |
| |
| // now we need to populate the compiler adapter |
| adapter.setJavac(this); |
| |
| // finally, lets execute the compiler!! |
| if (adapter.execute()) { |
| // Success |
| if (createMissingPackageInfoClass) { |
| try { |
| generateMissingPackageInfoClasses(destDir != null |
| ? destDir |
| : getProject() |
| .resolveFile(src.list()[0])); |
| } catch (final IOException x) { |
| // Should this be made a nonfatal warning? |
| throw new BuildException(x, getLocation()); |
| } |
| } |
| } else { |
| // Fail path |
| this.taskSuccess = false; |
| if (errorProperty != null) { |
| getProject().setNewProperty( |
| errorProperty, "true"); |
| } |
| if (failOnError) { |
| throw new BuildException(FAIL_MSG, getLocation()); |
| } else { |
| log(FAIL_MSG, Project.MSG_ERR); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Adds an "compiler" attribute to Commandline$Attribute used to |
| * filter command line attributes based on the current |
| * implementation. |
| */ |
| public class ImplementationSpecificArgument extends |
| org.apache.tools.ant.util.facade.ImplementationSpecificArgument { |
| |
| /** |
| * @param impl the name of the compiler |
| */ |
| public void setCompiler(final String impl) { |
| super.setImplementation(impl); |
| } |
| } |
| |
| private void lookForPackageInfos(final File srcDir, final File[] newFiles) { |
| for (int i = 0; i < newFiles.length; i++) { |
| final File f = newFiles[i]; |
| if (!f.getName().equals("package-info.java")) { |
| continue; |
| } |
| final String path = FILE_UTILS.removeLeadingPath(srcDir, f). |
| replace(File.separatorChar, '/'); |
| final String suffix = "/package-info.java"; |
| if (!path.endsWith(suffix)) { |
| log("anomalous package-info.java path: " + path, Project.MSG_WARN); |
| continue; |
| } |
| final String pkg = path.substring(0, path.length() - suffix.length()); |
| packageInfos.put(pkg, new Long(f.lastModified())); |
| } |
| } |
| |
| /** |
| * Ensure that every {@code package-info.java} produced a {@code package-info.class}. |
| * Otherwise this task's up-to-date tracking mechanisms do not work. |
| * @see <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=43114">Bug #43114</a> |
| */ |
| private void generateMissingPackageInfoClasses(final File dest) throws IOException { |
| for (final Entry<String, Long> entry : packageInfos.entrySet()) { |
| final String pkg = entry.getKey(); |
| final Long sourceLastMod = entry.getValue(); |
| final File pkgBinDir = new File(dest, pkg.replace('/', File.separatorChar)); |
| pkgBinDir.mkdirs(); |
| final File pkgInfoClass = new File(pkgBinDir, "package-info.class"); |
| if (pkgInfoClass.isFile() && pkgInfoClass.lastModified() >= sourceLastMod.longValue()) { |
| continue; |
| } |
| log("Creating empty " + pkgInfoClass); |
| final OutputStream os = new FileOutputStream(pkgInfoClass); |
| try { |
| os.write(PACKAGE_INFO_CLASS_HEADER); |
| final byte[] name = pkg.getBytes("UTF-8"); |
| final int length = name.length + /* "/package-info" */ 13; |
| os.write((byte) length / 256); |
| os.write((byte) length % 256); |
| os.write(name); |
| os.write(PACKAGE_INFO_CLASS_FOOTER); |
| } finally { |
| os.close(); |
| } |
| } |
| } |
| |
| /** |
| * 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() { |
| 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, |
| 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x01, 0x00, |
| 0x11, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x6e, 0x66, |
| 0x6f, 0x2e, 0x6a, 0x61, 0x76, 0x61, 0x01 |
| }; |
| |
| private static final byte[] PACKAGE_INFO_CLASS_FOOTER = { |
| 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x6e, 0x66, |
| 0x6f, 0x01, 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, |
| 0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x02, 0x00, 0x00, 0x01, |
| 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, |
| 0x00, 0x00, 0x00, 0x02, 0x00, 0x04 |
| }; |
| |
| } |