summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2005-09-30 17:26:05 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2005-09-30 17:26:05 +0000
commit43b0ce3c5dbae86c308652625ad2f61e225d6c12 (patch)
tree915fc502dc1fd89f27ffd4bdc52474b9e62c69d0
parent9cbbfa3ae375a1d119787304da4ef2c3bd39076b (diff)
downloadscala-43b0ce3c5dbae86c308652625ad2f61e225d6c12.tar.gz
scala-43b0ce3c5dbae86c308652625ad2f61e225d6c12.tar.bz2
scala-43b0ce3c5dbae86c308652625ad2f61e225d6c12.zip
The new Ant compile task.
-rw-r--r--sources/scala/tools/scalac/ant/Scalac.java493
1 files changed, 493 insertions, 0 deletions
diff --git a/sources/scala/tools/scalac/ant/Scalac.java b/sources/scala/tools/scalac/ant/Scalac.java
new file mode 100644
index 0000000000..b4a7a5d412
--- /dev/null
+++ b/sources/scala/tools/scalac/ant/Scalac.java
@@ -0,0 +1,493 @@
+/* ____ ____ ____ ____ ______ *\
+ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+ ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+ ** /_____/\____/\___/\____/____/ **
+ ** **
+ ** $Id: $
+\* */
+
+package scala.tools.scalac.ant;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Vector;
+import org.apache.tools.ant.AntClassLoader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.types.Reference;
+
+import scala.tools.util.ConsoleReporter;
+import scala.tools.util.Reporter;
+import scala.tools.util.Timer;
+import scala.tools.util.debug.AbortError;
+import scala.tools.scalac.CompilerPhases$class;
+import scala.tools.scalac.Global$class;
+import scalac.CompilationUnit;
+import scalac.CompilerCommand;
+
+/**
+ * An Ant task to compile with the old Scala compiler.
+ * This task can take the following parameters as attributes:<ul>
+ * <li>srcdir (mandatory),</li>
+ * <li>srcref,</li>
+ * <li>destdir,</li>
+ * <li>classpath,</li>
+ * <li>classpathref,</li>
+ * <li>sourcepath,</li>
+ * <li>sourcepathref,</li>
+ * <li>bootclasspath,</li>
+ * <li>bootclasspathref,</li>
+ * <li>extdirs,</li>
+ * <li>extdirsref,</li>
+ * <li>encoding,</li>
+ * <li>verbose,</li>
+ * <li>debug,</li>
+ * <li>usepredefs,</li>
+ * <li>useimports,</li>
+ * <li>force.</li>
+ * </ul>
+ * It also takes the following parameters as nested elements:<ul>
+ * <li>src (for srcdir),</li>
+ * <li>classpath,</li>
+ * <li>sourcepath,</li>
+ * <li>bootclasspath,</li>
+ * <li>extdirs.</li>
+ * </ul>
+ */
+public class Scalac extends MatchingTask {
+
+ private String SCALA_PRODUCT =
+ System.getProperty("scala.product", "Scalac Ant compiler");
+ private String SCALA_VERSION =
+ System.getProperty("scala.version", "Unknown version");
+
+ // ###################################################################
+ // ##### Ant Properties #####
+ // ###################################################################
+
+ /** The directories that contain source files to compile. */
+ private Path origin = null;
+ /** The directory to put the compiled files in. */
+ private File destination = null;
+
+ /** The class path to use for this compilation. */
+ private Path classpath = null;
+ /** The source path to use for this compilation. */
+ private Path sourcepath = null;
+ /** The boot class path to use for this compilation. */
+ private Path bootclasspath = null;
+ /** The external extensions path to use for this compilation. */
+ private Path extpath = null;
+
+ /** The text encoding of the files to compile. */
+ private String encoding = null;
+
+ /** Whether to use verbose output or not. */
+ private boolean verbose = false;
+ /** Whether to print-out some additional ant compilation information. */
+ private boolean debug = false;
+ /** Whether to use implicit predefined values or not. */
+ private boolean usepredefs = true;
+ /** Whether to implicitly import or not. */
+ private boolean useimports = true;
+ /** Whether to force compilation of all files or not. */
+ private boolean force = false;
+
+ // ###################################################################
+ // ##### Properties setters #####
+ // ###################################################################
+
+ /**
+ * A setter for the srcdir attribute. Used by Ant.
+ * @param input The value of <code>origin</code>.
+ */
+ public void setSrcdir(Path input) {
+ if (origin == null) {
+ origin = input;
+ } else {
+ origin.append(input);
+ }
+ }
+
+ /**
+ * A setter of the <code>origin</code> as a nested src Ant parameter.
+ * @return An origin path to be configured.
+ */
+ public Path createSrc() {
+ if (origin == null) {
+ origin = new Path(getProject());
+ }
+ return origin.createPath();
+ }
+
+ /**
+ * A setter of the <code>origin</code> as an external reference Ant parameter.
+ * @param input A reference to an origin path.
+ */
+ public void setSrcref(Reference input) {
+ createSrc().setRefid(input);
+ }
+
+ /**
+ * A setter for the destdir attribute. Used by Ant.
+ * @param input The value of <code>destination</code>.
+ */
+ public void setDestdir(File input) {
+ destination = input;
+ }
+
+ /**
+ * A setter for the classpath attribute. Used by Ant.
+ * @param input The value of <code>classpath</code>.
+ */
+ public void setClasspath(Path input) {
+ if (classpath == null) {
+ classpath = input;
+ } else {
+ classpath.append(input);
+ }
+ }
+
+ /**
+ * A setter of the <code>classpath</code> as a nested classpath Ant parameter.
+ * @return A class path to be configured.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * A setter of the <code>classpath</code> as an external reference Ant parameter.
+ * @param input A reference to a class path.
+ */
+ public void setClasspathref(Reference input) {
+ createClasspath().setRefid(input);
+ }
+
+ /**
+ * A setter for the sourcepath attribute. Used by Ant.
+ * @param input The value of <code>sourcepath</code>.
+ */
+ public void setSourcepath(Path input) {
+ if (sourcepath == null) {
+ sourcepath = input;
+ } else {
+ sourcepath.append(input);
+ }
+ }
+
+ /**
+ * A setter of the <code>sourcepath</code> as a nested sourcepath Ant parameter.
+ * @return A source path to be configured.
+ */
+ public Path createSourcepath() {
+ if (sourcepath == null) {
+ sourcepath = new Path(getProject());
+ }
+ return sourcepath.createPath();
+ }
+
+ /**
+ * A setter of the <code>sourcepath</code> as an external reference Ant parameter.
+ * @param input A reference to a source path.
+ */
+ public void setSourcepathref(Reference input) {
+ createSourcepath().setRefid(input);
+ }
+
+ /**
+ * A setter for the boot classpath attribute. Used by Ant.
+ * @param input The value of <code>bootclasspath</code>.
+ */
+ public void setBootclasspath(Path input) {
+ if (bootclasspath == null) {
+ bootclasspath = input;
+ } else {
+ bootclasspath.append(input);
+ }
+ }
+
+ /**
+ * A setter of the <code>bootclasspath</code> as a nested sourcepath Ant parameter.
+ * @return A source path to be configured.
+ */
+ public Path createBootclasspath() {
+ if (bootclasspath == null) {
+ bootclasspath = new Path(getProject());
+ }
+ return bootclasspath.createPath();
+ }
+
+ /**
+ * A setter of the <code>bootclasspath</code> as an external reference Ant parameter.
+ * @param input A reference to a source path.
+ */
+ public void setBootclasspathref(Reference input) {
+ createBootclasspath().setRefid(input);
+ }
+
+ /**
+ * A setter for the external extensions path attribute. Used by Ant.
+ * @param input The value of <code>extpath</code>.
+ */
+ public void setExtdirs(Path input) {
+ if (extpath == null) {
+ extpath = input;
+ } else {
+ extpath.append(input);
+ }
+ }
+
+ /**
+ * A setter of the <code>extpath</code> as a nested sourcepath Ant parameter.
+ * @return A source path to be configured.
+ */
+ public Path createExtdirs() {
+ if (extpath == null) {
+ extpath = new Path(getProject());
+ }
+ return extpath.createPath();
+ }
+
+ /**
+ * A setter of the <code>extpath</code> as an external reference Ant parameter.
+ * @param input A reference to a source path.
+ */
+ public void setExtdirsref(Reference input) {
+ createExtdirs().setRefid(input);
+ }
+
+ /**
+ * A setter for the encoding attribute. Used by Ant.
+ * @param input The value of <code>encoding</code>.
+ */
+ public void setEncoding(String input) {
+ encoding = input;
+ }
+
+ /**
+ * A setter for the verbose attribute. Used by Ant.
+ * @param input The value for <code>verbose</code>.
+ */
+ public void setVerbose(boolean input) {
+ verbose = input;
+ }
+
+ /**
+ * A setter for the debug attribute. Used by Ant.
+ * @param input The value for <code>debug</code>.
+ */
+ public void setDebug(boolean input) {
+ debug = input;
+ }
+
+ /**
+ * A setter for the use predefs attribute. Used by Ant.
+ * @param input The value for <code>usepredefs</code>.
+ */
+ public void setUsepredefs(boolean input) {
+ usepredefs = input;
+ }
+
+ /**
+ * A setter for the use imports attribute. Used by Ant.
+ * @param input The value for <code>useimport</code>.
+ */
+ public void setUseimports(boolean input) {
+ useimports = input;
+ }
+
+ /**
+ * A setter for the force attribute. Used by Ant.
+ * @param input The value for <code>force</code>.
+ */
+ public void setForce(boolean input) {
+ force = input;
+ }
+
+ // ###################################################################
+ // ##### Compilation and support methods #####
+ // ###################################################################
+
+ /**
+ * Generates a build error. Error location will be the current task in the ant file.
+ * @param message The message of the error. This message should be end-user readable.
+ * @throws org.apache.tools.ant.BuildException The build error exception. Will be thrown in all conditions.
+ */
+ private void error(String message) throws BuildException {
+ throw new BuildException(message, getLocation());
+ }
+
+ /**
+ * Performs the compilation.
+ */
+ public void execute() throws BuildException {
+
+ // Tests if all mandatory attributes are set and valid.
+ if (origin == null) error("Attribute 'srcdir' is not set.");
+ if (origin.size() == 0) error("Attribute 'srcdir' is not set.");
+ if (destination != null && !destination.isDirectory())
+ error("Attribute 'destdir' does not refer to an existing directory.");
+
+ Vector sourceFilesList = new Vector();
+ FileUtils fileUtils = FileUtils.newFileUtils();
+
+ // Scans source directories to build up a compile lists.
+ // If force is false, only files were the .class file in destination is newer than
+ // the .suffix file will be used.
+ String[] originList = origin.list();
+ for (int i = 0; i < originList.length; i++) {
+ File originDir = getProject().resolveFile(originList[i]);
+ if (!originDir.exists()) {
+ log("Element '" + originDir.getPath() + "' in attribute 'srcdir' does not refer to an existing directory.", Project.MSG_WARN);
+ break;
+ }
+ DirectoryScanner originDirScanner = this.getDirectoryScanner(originDir);
+ String[] files = originDirScanner.getIncludedFiles();
+
+ if (force) {
+ for (int j = 0; j < files.length; j++) {
+ String sourceFile = fileUtils.resolveFile(originDir, files[j]).toString();
+ log(sourceFile, Project.MSG_VERBOSE);
+ sourceFilesList.add(sourceFile);
+ }
+ } else {
+ GlobPatternMapper mapper = new GlobPatternMapper();
+ mapper.setTo("*.class");
+ mapper.setFrom("*.scala");
+ SourceFileScanner scanner = new SourceFileScanner(this);
+ String[] newFiles = scanner.restrict(files, originDir, destination, mapper);
+ for (int k = 0; k < newFiles.length; k++) {
+ String sourceFile = fileUtils.resolveFile(originDir, newFiles[k]).toString();
+ log(sourceFile, Project.MSG_VERBOSE);
+ sourceFilesList.add(sourceFile);
+ }
+ }
+ }
+
+ log("Compiling " + sourceFilesList.size() + " source file" + (sourceFilesList.size() == 1 ? "" : "s") + (destination != null ? " to " + destination.toString() : ""));
+
+ // Builds-up the compilation settings for Scalac with the existing Ant parameters.
+ Reporter reporter = new ConsoleReporter();
+ CompilerCommand command = new CompilerCommand(SCALA_PRODUCT, SCALA_VERSION, reporter, new CompilerPhases$class());
+ if (destination != null) command.outpath.value = destination.getAbsolutePath();
+ if (classpath != null) command.classpath.value = makeAbsolutePath(classpath, "classpath");
+ if (sourcepath != null) {
+ command.sourcepath.value = makeAbsolutePath(sourcepath, "sourcepath");
+ } else {
+ command.sourcepath.value = destination.getAbsolutePath();
+ }
+ // The bootclasspath needs to be treated specially.
+ // When no bootclasspath is provided, the classpath of the current classloader is used:
+ // This is where the scala classes should be.
+ // Furthermore, the source files for the library must be available in the bootclasspath too.
+ // To do that, an element ending in 'lib/scala.jar' or 'scala/classes' is looked-up in the bootclasspath.
+ // From there, the sources should be in '../src' or '../sources' respectively.
+ // This means any other configuration will fail at runtime. In this case, the bootclasspath should be set explicitly.
+ // Notice also how the bootclasspath must finish with a ":" for it to work.
+ String[] baseBootclasspath;
+ if (bootclasspath != null) {
+ baseBootclasspath = bootclasspath.list();
+ } else {
+ baseBootclasspath = getClassLoaderClasspath(this.getClass().getClassLoader()).list();
+ }
+ bootclasspath = new Path(getProject());
+ for (int i = 0; i < baseBootclasspath.length; i++) {
+ bootclasspath.append(new Path(getProject(), baseBootclasspath[i]));
+ int scalaJarIndex = baseBootclasspath[i].lastIndexOf("lib" + File.separator + "scala.jar");
+ int scalaClassesIndex = baseBootclasspath[i].lastIndexOf("scala" + File.separator + "classes");
+ if (scalaJarIndex > -1) {
+ String libPath = baseBootclasspath[i].substring(0, scalaJarIndex) + "src";
+ bootclasspath.append(new Path(getProject(), libPath));
+ System.setProperty("scala.library.class.path", baseBootclasspath[i]);
+ System.setProperty("scala.library.source.path", libPath);
+ } else if (scalaClassesIndex > -1) {
+ String libPath = baseBootclasspath[i].substring(0, scalaClassesIndex + 6) + "sources";
+ bootclasspath.append(new Path(getProject(), libPath));
+ System.setProperty("scala.library.class.path", baseBootclasspath[i]);
+ System.setProperty("scala.library.source.path", libPath);
+ }
+ }
+ command.bootclasspath.value = makeAbsolutePath(bootclasspath, "bootclasspath") + ":";
+ if (extpath != null) command.extdirs.value = makeAbsolutePath(extpath, "extpath");
+ if (encoding != null) command.encoding.value = encoding;
+ command.verbose.value = verbose;
+ command.debug.value = debug;
+ command.noimports.value = !useimports;
+ command.nopredefs.value = !usepredefs;
+ Timer timer = scalac.Global.getTimer(reporter);
+
+ // Compiles the actual code
+ Global$class compiler = new Global$class(command, timer, false);
+
+ String[] stringArray = new String[sourceFilesList.size()];
+ try {
+ timer.start();
+ CompilationUnit[] classes = compiler.compile((String[])sourceFilesList.toArray(stringArray), false);
+ if (reporter.errors() > 0) {
+ error("Compile failed with " + reporter.errors() + " error" + (reporter.errors() == 1 ? "" : "s") + "; see the compiler error output for details.");
+ }
+ compiler.dump(classes);
+ } catch (AbortError exception) {
+ if (debug) exception.printStackTrace();
+ error("Compile failed because of an internal compiler error; see the error output for details.");
+ } finally {
+ timer.stop("Compile time");
+ }
+ if (reporter.warnings() > 0) {
+ log("Compile suceeded with " + reporter.errors() + " warning" + (reporter.warnings() == 1 ? "" : "s") + "; see the compiler output for details.");
+ }
+
+ }
+
+ private String makeAbsolutePath(Path path, String pathName) {
+ String result = "";
+ String[] pathList = path.list();
+ for (int i = 0; i < pathList.length; i++) {
+ File pathFile = new File(pathList[i]);
+ if (pathFile.exists()) {
+ result = result + ((result == "") ? "" : File.pathSeparator) + pathFile.getAbsolutePath();
+ } else {
+ log("Element '" + pathFile.toString() + "' in " + pathName + " does not exist.", Project.MSG_WARN);
+ result = result + ((result == "") ? "" : File.pathSeparator) + pathFile.toString();
+ }
+ }
+ return result;
+ }
+
+ private Path getClassLoaderClasspath(ClassLoader classLoader) throws BuildException {
+ Path classLoaderClasspath = new Path(getProject());
+ ClassLoader parentClassLoader = classLoader.getParent();
+ String classloaderName = classLoader.getClass().getName();
+ boolean isURLClassLoader = classloaderName.endsWith("URLClassLoader");
+ boolean isAntClassLoader = classloaderName.endsWith("AntClassLoader2") || classloaderName.endsWith("AntClassLoader");
+ if (isURLClassLoader) {
+ URL[] urls = ((URLClassLoader) classLoader).getURLs();
+ for (int i = 0; i < urls.length; i++) {
+ classLoaderClasspath.append(new Path(getProject(), urls[i].toString()));
+ }
+ } else if (isAntClassLoader) {
+ String[] paths = ((AntClassLoader) classLoader).getClasspath().split(File.pathSeparator);
+ for (int i = 0; i < paths.length; i++) {
+ classLoaderClasspath.append(new Path(getProject(), paths[i]));
+ }
+ }
+ if (parentClassLoader != null && parentClassLoader != classLoader) {
+ classLoaderClasspath.append(getClassLoaderClasspath(parentClassLoader));
+ }
+ return classLoaderClasspath;
+ }
+
+}