aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/io
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/io
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/io')
-rw-r--r--compiler/src/dotty/tools/io/ClassPath.scala421
-rw-r--r--compiler/src/dotty/tools/io/DaemonThreadFactory.scala16
-rw-r--r--compiler/src/dotty/tools/io/Fileish.scala34
-rw-r--r--compiler/src/dotty/tools/io/Jar.scala172
-rw-r--r--compiler/src/dotty/tools/io/package.scala58
5 files changed, 701 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/io/ClassPath.scala b/compiler/src/dotty/tools/io/ClassPath.scala
new file mode 100644
index 000000000..e30eca492
--- /dev/null
+++ b/compiler/src/dotty/tools/io/ClassPath.scala
@@ -0,0 +1,421 @@
+/* NSC -- new Scala compiler
+ * Copyright 2006-2012 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package dotty.tools
+package io
+
+import java.net.URL
+import scala.collection.{ mutable, immutable }
+import scala.reflect.internal.util.StringOps.splitWhere
+import File.pathSeparator
+import java.net.MalformedURLException
+import Jar.isJarOrZip
+import ClassPath._
+import scala.Option.option2Iterable
+import scala.reflect.io.Path.string2path
+import language.postfixOps
+
+/** <p>
+ * This module provides star expansion of '-classpath' option arguments, behaves the same as
+ * java, see [http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html]
+ * </p>
+ *
+ * @author Stepan Koltsov
+ */
+object ClassPath {
+
+ /** Expand single path entry */
+ private def expandS(pattern: String): List[String] = {
+ val wildSuffix = File.separator + "*"
+
+ /** Get all subdirectories, jars, zips out of a directory. */
+ def lsDir(dir: Directory, filt: String => Boolean = _ => true) = {
+ val files = synchronized(dir.list)
+ files filter (x => filt(x.name) && (x.isDirectory || isJarOrZip(x))) map (_.path) toList
+ }
+
+ def basedir(s: String) =
+ if (s contains File.separator) s.substring(0, s.lastIndexOf(File.separator))
+ else "."
+
+ if (pattern == "*") lsDir(Directory("."))
+ else if (pattern endsWith wildSuffix) lsDir(Directory(pattern dropRight 2))
+ else if (pattern contains '*') {
+ val regexp = ("^%s$" format pattern.replaceAll("""\*""", """.*""")).r
+ lsDir(Directory(pattern).parent, regexp findFirstIn _ isDefined)
+ }
+ else List(pattern)
+ }
+
+ /** Split classpath using platform-dependent path separator */
+ def split(path: String): List[String] = (path split pathSeparator).toList filterNot (_ == "") distinct
+
+ /** Join classpath using platform-dependent path separator */
+ def join(paths: String*): String = paths filterNot (_ == "") mkString pathSeparator
+
+ /** Split the classpath, apply a transformation function, and reassemble it. */
+ def map(cp: String, f: String => String): String = join(split(cp) map f: _*)
+
+ /** Split the classpath, filter according to predicate, and reassemble. */
+ def filter(cp: String, p: String => Boolean): String = join(split(cp) filter p: _*)
+
+ /** Split the classpath and map them into Paths */
+ def toPaths(cp: String): List[Path] = split(cp) map (x => Path(x).toAbsolute)
+
+ /** Make all classpath components absolute. */
+ def makeAbsolute(cp: String): String = fromPaths(toPaths(cp): _*)
+
+ /** Join the paths as a classpath */
+ def fromPaths(paths: Path*): String = join(paths map (_.path): _*)
+ def fromURLs(urls: URL*): String = fromPaths(urls map (x => Path(x.getPath)) : _*)
+
+ /** Split the classpath and map them into URLs */
+ def toURLs(cp: String): List[URL] = toPaths(cp) map (_.toURL)
+
+ /** Expand path and possibly expanding stars */
+ def expandPath(path: String, expandStar: Boolean = true): List[String] =
+ if (expandStar) split(path) flatMap expandS
+ else split(path)
+
+ /** Expand dir out to contents, a la extdir */
+ def expandDir(extdir: String): List[String] = {
+ AbstractFile getDirectory extdir match {
+ case null => Nil
+ case dir => dir filter (_.isClassContainer) map (x => new java.io.File(dir.file, x.name) getPath) toList
+ }
+ }
+ /** Expand manifest jar classpath entries: these are either urls, or paths
+ * relative to the location of the jar.
+ */
+ def expandManifestPath(jarPath: String): List[URL] = {
+ val file = File(jarPath)
+ if (!file.isFile) return Nil
+
+ val baseDir = file.parent
+ new Jar(file).classPathElements map (elem =>
+ specToURL(elem) getOrElse (baseDir / elem).toURL
+ )
+ }
+
+ /** A useful name filter. */
+ def isTraitImplementation(name: String) = name endsWith "$class.class"
+
+ def specToURL(spec: String): Option[URL] =
+ try Some(new URL(spec))
+ catch { case _: MalformedURLException => None }
+
+ /** A class modeling aspects of a ClassPath which should be
+ * propagated to any classpaths it creates.
+ */
+ abstract class ClassPathContext {
+ /** A filter which can be used to exclude entities from the classpath
+ * based on their name.
+ */
+ def isValidName(name: String): Boolean = true
+
+ /** From the representation to its identifier.
+ */
+ def toBinaryName(rep: AbstractFile): String
+
+ /** Create a new classpath based on the abstract file.
+ */
+ def newClassPath(file: AbstractFile): ClassPath
+
+ /** Creators for sub classpaths which preserve this context.
+ */
+ def sourcesInPath(path: String): List[ClassPath] =
+ for (file <- expandPath(path, false) ; dir <- Option(AbstractFile getDirectory file)) yield
+ new SourcePath(dir, this)
+
+ def contentsOfDirsInPath(path: String): List[ClassPath] =
+ for (dir <- expandPath(path, false) ; name <- expandDir(dir) ; entry <- Option(AbstractFile getDirectory name)) yield
+ newClassPath(entry)
+
+ def classesAtAllURLS(path: String): List[ClassPath] =
+ (path split " ").toList flatMap classesAtURL
+
+ def classesAtURL(spec: String) =
+ for (url <- specToURL(spec).toList ; location <- Option(AbstractFile getURL url)) yield
+ newClassPath(location)
+
+ def classesInExpandedPath(path: String): IndexedSeq[ClassPath] =
+ classesInPathImpl(path, true).toIndexedSeq
+
+ def classesInPath(path: String) = classesInPathImpl(path, false)
+
+ // Internal
+ private def classesInPathImpl(path: String, expand: Boolean) =
+ for (file <- expandPath(path, expand) ; dir <- Option(AbstractFile getDirectory file)) yield
+ newClassPath(dir)
+ }
+
+ class JavaContext extends ClassPathContext {
+ def toBinaryName(rep: AbstractFile) = {
+ val name = rep.name
+ assert(endsClass(name), name)
+ name.substring(0, name.length - 6)
+ }
+ def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this)
+ }
+
+ object DefaultJavaContext extends JavaContext {
+ override def isValidName(name: String) = !isTraitImplementation(name)
+ }
+
+ private def endsClass(s: String) = s.length > 6 && s.substring(s.length - 6) == ".class"
+ private def endsScala(s: String) = s.length > 6 && s.substring(s.length - 6) == ".scala"
+ private def endsJava(s: String) = s.length > 5 && s.substring(s.length - 5) == ".java"
+
+ /** From the source file to its identifier.
+ */
+ def toSourceName(f: AbstractFile): String = {
+ val name = f.name
+
+ if (endsScala(name)) name.substring(0, name.length - 6)
+ else if (endsJava(name)) name.substring(0, name.length - 5)
+ else throw new FatalError("Unexpected source file ending: " + name)
+ }
+}
+
+/**
+ * Represents a package which contains classes and other packages
+ */
+abstract class ClassPath {
+ type AnyClassRep = ClassPath#ClassRep
+
+ /**
+ * The short name of the package (without prefix)
+ */
+ def name: String
+
+ /**
+ * A String representing the origin of this classpath element, if known.
+ * For example, the path of the directory or jar.
+ */
+ def origin: Option[String] = None
+
+ /** A list of URLs representing this classpath.
+ */
+ def asURLs: List[URL]
+
+ /** The whole classpath in the form of one String.
+ */
+ def asClasspathString: String
+
+ /** Info which should be propagated to any sub-classpaths.
+ */
+ def context: ClassPathContext
+
+ /** Lists of entities.
+ */
+ def classes: IndexedSeq[AnyClassRep]
+ def packages: IndexedSeq[ClassPath]
+ def sourcepaths: IndexedSeq[AbstractFile]
+
+ /**
+ * Represents classes which can be loaded with a ClassfileLoader
+ * and / or a SourcefileLoader.
+ */
+ case class ClassRep(binary: Option[AbstractFile], source: Option[AbstractFile]) {
+ def name: String = binary match {
+ case Some(x) => context.toBinaryName(x)
+ case _ =>
+ assert(source.isDefined)
+ toSourceName(source.get)
+ }
+ }
+
+ /** Filters for assessing validity of various entities.
+ */
+ def validClassFile(name: String) = endsClass(name) && context.isValidName(name)
+ def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.')
+ def validSourceFile(name: String) = endsScala(name) || endsJava(name)
+
+ /**
+ * Find a ClassRep given a class name of the form "package.subpackage.ClassName".
+ * Does not support nested classes on .NET
+ */
+ def findClass(name: String): Option[AnyClassRep] =
+ splitWhere(name, _ == '.', true) match {
+ case Some((pkg, rest)) =>
+ val rep = packages find (_.name == pkg) flatMap (_ findClass rest)
+ rep map {
+ case x: ClassRep => x
+ case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name))
+ }
+ case _ =>
+ classes find (_.name == name)
+ }
+
+ def findSourceFile(name: String): Option[AbstractFile] =
+ findClass(name) match {
+ case Some(ClassRep(Some(x: AbstractFile), _)) => Some(x)
+ case _ => None
+ }
+
+ def sortString = join(split(asClasspathString).sorted: _*)
+ override def equals(that: Any) = that match {
+ case x: ClassPath => this.sortString == x.sortString
+ case _ => false
+ }
+ override def hashCode = sortString.hashCode()
+}
+
+/**
+ * A Classpath containing source files
+ */
+class SourcePath(dir: AbstractFile, val context: ClassPathContext) extends ClassPath {
+ def name = dir.name
+ override def origin = dir.underlyingSource map (_.path)
+ def asURLs = if (dir.file == null) Nil else List(dir.toURL)
+ def asClasspathString = dir.path
+ val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir)
+
+ private def traverse() = {
+ val classBuf = immutable.Vector.newBuilder[ClassRep]
+ val packageBuf = immutable.Vector.newBuilder[SourcePath]
+ dir foreach { f =>
+ if (!f.isDirectory && validSourceFile(f.name))
+ classBuf += ClassRep(None, Some(f))
+ else if (f.isDirectory && validPackage(f.name))
+ packageBuf += new SourcePath(f, context)
+ }
+ (packageBuf.result, classBuf.result)
+ }
+
+ lazy val (packages, classes) = traverse()
+ override def toString() = "sourcepath: " + dir.toString()
+}
+
+/**
+ * A directory (or a .jar file) containing classfiles and packages
+ */
+class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext) extends ClassPath {
+ def name = dir.name
+ override def origin = dir.underlyingSource map (_.path)
+ def asURLs = if (dir.file == null) Nil else List(dir.toURL)
+ def asClasspathString = dir.path
+ val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq()
+
+ // calculates (packages, classes) in one traversal.
+ private def traverse() = {
+ val classBuf = immutable.Vector.newBuilder[ClassRep]
+ val packageBuf = immutable.Vector.newBuilder[DirectoryClassPath]
+ dir foreach { f =>
+ if (!f.isDirectory && validClassFile(f.name))
+ classBuf += ClassRep(Some(f), None)
+ else if (f.isDirectory && validPackage(f.name))
+ packageBuf += new DirectoryClassPath(f, context)
+ }
+ (packageBuf.result, classBuf.result)
+ }
+
+ lazy val (packages, classes) = traverse()
+ override def toString() = "directory classpath: " + origin.getOrElse("?")
+}
+
+class DeltaClassPath(original: MergedClassPath, subst: Map[ClassPath, ClassPath])
+extends MergedClassPath(original.entries map (e => subst getOrElse (e, e)), original.context) {
+ // not sure we should require that here. Commented out for now.
+ // require(subst.keySet subsetOf original.entries.toSet)
+ // We might add specialized operations for computing classes packages here. Not sure it's worth it.
+}
+
+/**
+ * A classpath unifying multiple class- and sourcepath entries.
+ */
+class MergedClassPath(
+ val entries: IndexedSeq[ClassPath],
+ val context: ClassPathContext)
+extends ClassPath {
+ def this(entries: TraversableOnce[ClassPath], context: ClassPathContext) =
+ this(entries.toIndexedSeq, context)
+
+ def name = entries.head.name
+ def asURLs = (entries flatMap (_.asURLs)).toList
+ lazy val sourcepaths: IndexedSeq[AbstractFile] = entries flatMap (_.sourcepaths)
+
+ override def origin = Some(entries map (x => x.origin getOrElse x.name) mkString ("Merged(", ", ", ")"))
+ override def asClasspathString: String = join(entries map (_.asClasspathString) : _*)
+
+ lazy val classes: IndexedSeq[AnyClassRep] = {
+ var count = 0
+ val indices = mutable.AnyRefMap[String, Int]()
+ val cls = new mutable.ArrayBuffer[AnyClassRep](1024)
+
+ for (e <- entries; c <- e.classes) {
+ val name = c.name
+ if (indices contains name) {
+ val idx = indices(name)
+ val existing = cls(idx)
+
+ if (existing.binary.isEmpty && c.binary.isDefined)
+ cls(idx) = existing.copy(binary = c.binary)
+ if (existing.source.isEmpty && c.source.isDefined)
+ cls(idx) = existing.copy(source = c.source)
+ }
+ else {
+ indices(name) = count
+ cls += c
+ count += 1
+ }
+ }
+ cls.toIndexedSeq
+ }
+
+ lazy val packages: IndexedSeq[ClassPath] = {
+ var count = 0
+ val indices = mutable.AnyRefMap[String, Int]()
+ val pkg = new mutable.ArrayBuffer[ClassPath](256)
+
+ for (e <- entries; p <- e.packages) {
+ val name = p.name
+ if (indices contains name) {
+ val idx = indices(name)
+ pkg(idx) = addPackage(pkg(idx), p)
+ }
+ else {
+ indices(name) = count
+ pkg += p
+ count += 1
+ }
+ }
+ pkg.toIndexedSeq
+ }
+
+ private def addPackage(to: ClassPath, pkg: ClassPath) = {
+ val newEntries: IndexedSeq[ClassPath] = to match {
+ case cp: MergedClassPath => cp.entries :+ pkg
+ case _ => IndexedSeq(to, pkg)
+ }
+ new MergedClassPath(newEntries, context)
+ }
+ def show(): Unit = {
+ println("ClassPath %s has %d entries and results in:\n".format(name, entries.size))
+ asClasspathString split ':' foreach (x => println(" " + x))
+ }
+ override def toString() = "merged classpath " + entries.mkString("(", "\n", ")")
+}
+
+/**
+ * The classpath when compiling with target:jvm. Binary files (classfiles) are represented
+ * as AbstractFile. nsc.io.ZipArchive is used to view zip/jar archives as directories.
+ */
+class JavaClassPath(
+ containers: IndexedSeq[ClassPath],
+ context: JavaContext)
+extends MergedClassPath(containers, context) { }
+
+object JavaClassPath {
+ def fromURLs(urls: Seq[URL], context: JavaContext): JavaClassPath = {
+ val containers = {
+ for (url <- urls ; f = AbstractFile getURL url ; if f != null) yield
+ new DirectoryClassPath(f, context)
+ }
+ new JavaClassPath(containers.toIndexedSeq, context)
+ }
+ def fromURLs(urls: Seq[URL]): JavaClassPath =
+ fromURLs(urls, ClassPath.DefaultJavaContext)
+}
diff --git a/compiler/src/dotty/tools/io/DaemonThreadFactory.scala b/compiler/src/dotty/tools/io/DaemonThreadFactory.scala
new file mode 100644
index 000000000..ae0cda260
--- /dev/null
+++ b/compiler/src/dotty/tools/io/DaemonThreadFactory.scala
@@ -0,0 +1,16 @@
+package dotty.tools
+package io
+
+import java.util.concurrent._
+
+class DaemonThreadFactory extends ThreadFactory {
+ def newThread(r: Runnable): Thread = {
+ val thread = new Thread(r)
+ thread setDaemon true
+ thread
+ }
+}
+
+object DaemonThreadFactory {
+ def newPool() = Executors.newCachedThreadPool(new DaemonThreadFactory)
+}
diff --git a/compiler/src/dotty/tools/io/Fileish.scala b/compiler/src/dotty/tools/io/Fileish.scala
new file mode 100644
index 000000000..0fcb13307
--- /dev/null
+++ b/compiler/src/dotty/tools/io/Fileish.scala
@@ -0,0 +1,34 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2012 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package dotty.tools
+package io
+
+import java.io.{ InputStream }
+import java.util.jar.JarEntry
+import language.postfixOps
+
+/** A common interface for File-based things and Stream-based things.
+ * (In particular, io.File and JarEntry.)
+ */
+class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars {
+ def inputStream() = input()
+
+ def parent = path.parent
+ def name = path.name
+ def isSourceFile = path.hasExtension("java", "scala")
+
+ private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim }
+ lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".")
+ lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "."
+
+ override def toString = path.path
+}
+
+object Fileish {
+ def apply(f: File): Fileish = new Fileish(f, () => f.inputStream())
+ def apply(f: JarEntry, in: () => InputStream): Fileish = new Fileish(Path(f.getName), in)
+ def apply(path: String, in: () => InputStream): Fileish = new Fileish(Path(path), in)
+}
diff --git a/compiler/src/dotty/tools/io/Jar.scala b/compiler/src/dotty/tools/io/Jar.scala
new file mode 100644
index 000000000..42efc7e06
--- /dev/null
+++ b/compiler/src/dotty/tools/io/Jar.scala
@@ -0,0 +1,172 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2012 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+
+package dotty.tools
+package io
+
+import java.io.{ InputStream, OutputStream, IOException, FileNotFoundException, FileInputStream, DataOutputStream }
+import java.util.jar._
+import scala.collection.JavaConverters._
+import Attributes.Name
+import scala.language.{postfixOps, implicitConversions}
+
+// Attributes.Name instances:
+//
+// static Attributes.Name CLASS_PATH
+// static Attributes.Name CONTENT_TYPE
+// static Attributes.Name EXTENSION_INSTALLATION
+// static Attributes.Name EXTENSION_LIST
+// static Attributes.Name EXTENSION_NAME
+// static Attributes.Name IMPLEMENTATION_TITLE
+// static Attributes.Name IMPLEMENTATION_URL
+// static Attributes.Name IMPLEMENTATION_VENDOR
+// static Attributes.Name IMPLEMENTATION_VENDOR_ID
+// static Attributes.Name IMPLEMENTATION_VERSION
+// static Attributes.Name MAIN_CLASS
+// static Attributes.Name MANIFEST_VERSION
+// static Attributes.Name SEALED
+// static Attributes.Name SIGNATURE_VERSION
+// static Attributes.Name SPECIFICATION_TITLE
+// static Attributes.Name SPECIFICATION_VENDOR
+// static Attributes.Name SPECIFICATION_VERSION
+
+class Jar(file: File) extends Iterable[JarEntry] {
+ def this(jfile: JFile) = this(File(jfile))
+ def this(path: String) = this(File(path))
+
+ protected def errorFn(msg: String): Unit = Console println msg
+
+ lazy val jarFile = new JarFile(file.jfile)
+ lazy val manifest = withJarInput(s => Option(s.getManifest))
+
+ def mainClass = manifest map (f => f(Name.MAIN_CLASS))
+ /** The manifest-defined classpath String if available. */
+ def classPathString: Option[String] =
+ for (m <- manifest ; cp <- m.attrs get Name.CLASS_PATH) yield cp
+ def classPathElements: List[String] = classPathString match {
+ case Some(s) => s split "\\s+" toList
+ case _ => Nil
+ }
+
+ def withJarInput[T](f: JarInputStream => T): T = {
+ val in = new JarInputStream(file.inputStream())
+ try f(in)
+ finally in.close()
+ }
+ def jarWriter(mainAttrs: (Attributes.Name, String)*) = {
+ new JarWriter(file, Jar.WManifest(mainAttrs: _*).underlying)
+ }
+
+ override def foreach[U](f: JarEntry => U): Unit = withJarInput { in =>
+ Iterator continually in.getNextJarEntry() takeWhile (_ != null) foreach f
+ }
+ override def iterator: Iterator[JarEntry] = this.toList.iterator
+ def fileishIterator: Iterator[Fileish] = jarFile.entries.asScala map (x => Fileish(x, () => getEntryStream(x)))
+
+ private def getEntryStream(entry: JarEntry) = jarFile getInputStream entry match {
+ case null => errorFn("No such entry: " + entry) ; null
+ case x => x
+ }
+ override def toString = "" + file
+}
+
+class JarWriter(val file: File, val manifest: Manifest) {
+ private lazy val out = new JarOutputStream(file.outputStream(), manifest)
+
+ /** Adds a jar entry for the given path and returns an output
+ * stream to which the data should immediately be written.
+ * This unusual interface exists to work with fjbg.
+ */
+ def newOutputStream(path: String): DataOutputStream = {
+ val entry = new JarEntry(path)
+ out putNextEntry entry
+ new DataOutputStream(out)
+ }
+
+ def writeAllFrom(dir: Directory): Unit = {
+ try dir.list foreach (x => addEntry(x, ""))
+ finally out.close()
+ }
+ def addStream(entry: JarEntry, in: InputStream): Unit = {
+ out putNextEntry entry
+ try transfer(in, out)
+ finally out.closeEntry()
+ }
+ def addFile(file: File, prefix: String): Unit = {
+ val entry = new JarEntry(prefix + file.name)
+ addStream(entry, file.inputStream())
+ }
+ def addEntry(entry: Path, prefix: String): Unit = {
+ if (entry.isFile) addFile(entry.toFile, prefix)
+ else addDirectory(entry.toDirectory, prefix + entry.name + "/")
+ }
+ def addDirectory(entry: Directory, prefix: String): Unit = {
+ entry.list foreach (p => addEntry(p, prefix))
+ }
+
+ private def transfer(in: InputStream, out: OutputStream) = {
+ val buf = new Array[Byte](10240)
+ def loop(): Unit = in.read(buf, 0, buf.length) match {
+ case -1 => in.close()
+ case n => out.write(buf, 0, n) ; loop
+ }
+ loop
+ }
+
+ def close() = out.close()
+}
+
+object Jar {
+ type AttributeMap = java.util.Map[Attributes.Name, String]
+
+ object WManifest {
+ def apply(mainAttrs: (Attributes.Name, String)*): WManifest = {
+ val m = WManifest(new JManifest)
+ for ((k, v) <- mainAttrs)
+ m(k) = v
+
+ m
+ }
+ def apply(manifest: JManifest): WManifest = new WManifest(manifest)
+ implicit def unenrichManifest(x: WManifest): JManifest = x.underlying
+ }
+ class WManifest(manifest: JManifest) {
+ for ((k, v) <- initialMainAttrs)
+ this(k) = v
+
+ def underlying = manifest
+ def attrs = manifest.getMainAttributes().asInstanceOf[AttributeMap].asScala withDefaultValue null
+ def initialMainAttrs: Map[Attributes.Name, String] = {
+ import scala.util.Properties._
+ Map(
+ Name.MANIFEST_VERSION -> "1.0",
+ ScalaCompilerVersion -> versionNumberString
+ )
+ }
+
+ def apply(name: Attributes.Name): String = attrs(name)
+ def apply(name: String): String = apply(new Attributes.Name(name))
+ def update(key: Attributes.Name, value: String) = attrs.put(key, value)
+ def update(key: String, value: String) = attrs.put(new Attributes.Name(key), value)
+
+ def mainClass: String = apply(Name.MAIN_CLASS)
+ def mainClass_=(value: String) = update(Name.MAIN_CLASS, value)
+ }
+
+ // See http://download.java.net/jdk7/docs/api/java/nio/file/Path.html
+ // for some ideas.
+ private val ZipMagicNumber = List[Byte](80, 75, 3, 4)
+ private def magicNumberIsZip(f: Path) = f.isFile && (f.toFile.bytes().take(4).toList == ZipMagicNumber)
+
+ def isJarOrZip(f: Path): Boolean = isJarOrZip(f, true)
+ def isJarOrZip(f: Path, examineFile: Boolean): Boolean =
+ f.hasExtension("zip", "jar") || (examineFile && magicNumberIsZip(f))
+
+ def create(file: File, sourceDir: Directory, mainClass: String): Unit = {
+ val writer = new Jar(file).jarWriter(Name.MAIN_CLASS -> mainClass)
+ writer writeAllFrom sourceDir
+ }
+}
diff --git a/compiler/src/dotty/tools/io/package.scala b/compiler/src/dotty/tools/io/package.scala
new file mode 100644
index 000000000..1c0e0b5c4
--- /dev/null
+++ b/compiler/src/dotty/tools/io/package.scala
@@ -0,0 +1,58 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2012 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package dotty.tools
+
+import java.util.concurrent.{ Future, Callable }
+import java.util.{ Timer, TimerTask }
+import java.util.jar.{ Attributes }
+import scala.language.implicitConversions
+
+package object io {
+ // Forwarders from scala.reflect.io
+ type AbstractFile = scala.reflect.io.AbstractFile
+ val AbstractFile = scala.reflect.io.AbstractFile
+ type Directory = scala.reflect.io.Directory
+ val Directory = scala.reflect.io.Directory
+ type File = scala.reflect.io.File
+ val File = scala.reflect.io.File
+ type Path = scala.reflect.io.Path
+ val Path = scala.reflect.io.Path
+ type PlainFile = scala.reflect.io.PlainFile
+ //val PlainFile = scala.reflect.io.PlainFile
+ val Streamable = scala.reflect.io.Streamable
+ type VirtualDirectory = scala.reflect.io.VirtualDirectory
+ type VirtualFile = scala.reflect.io.VirtualFile
+ val ZipArchive = scala.reflect.io.ZipArchive
+ type ZipArchive = scala.reflect.io.ZipArchive
+ type JManifest = java.util.jar.Manifest
+ type JFile = java.io.File
+
+ implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m)
+ private lazy val daemonThreadPool = DaemonThreadFactory.newPool()
+
+ def runnable(body: => Unit): Runnable = new Runnable { override def run() = body }
+ def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body }
+ def spawn[T](body: => T): Future[T] = daemonThreadPool submit callable(body)
+ def submit(runnable: Runnable) = daemonThreadPool submit runnable
+
+ // Create, start, and return a daemon thread
+ def daemonize(body: => Unit): Thread = newThread(_ setDaemon true)(body)
+ def newThread(f: Thread => Unit)(body: => Unit): Thread = {
+ val thread = new Thread(runnable(body))
+ f(thread)
+ thread.start
+ thread
+ }
+
+ // Set a timer to execute the given code.
+ def timer(seconds: Int)(body: => Unit): Timer = {
+ val alarm = new Timer(true) // daemon
+ val tt = new TimerTask { def run() = body }
+
+ alarm.schedule(tt, seconds * 1000)
+ alarm
+ }
+}