Initial xsbt commit

This commit is contained in:
Mark Harrah 2009-08-16 14:29:08 -04:00
commit f83d59b8cc
12 changed files with 422 additions and 0 deletions

58
cache/Cache.scala vendored Normal file
View File

@ -0,0 +1,58 @@
package xsbt
import sbinary.{CollectionTypes, Format, JavaFormats}
import java.io.File
trait Cache[I,O]
{
def apply(file: File)(i: I): Either[O, O => Unit]
}
trait SBinaryFormats extends CollectionTypes with JavaFormats with NotNull
{
//TODO: add basic types minus FileFormat
}
object Cache extends BasicCacheImplicits with SBinaryFormats with HListCacheImplicits
{
def cache[I,O](implicit c: Cache[I,O]): Cache[I,O] = c
def outputCache[O](implicit c: OutputCache[O]): OutputCache[O] = c
def inputCache[O](implicit c: InputCache[O]): InputCache[O] = c
def wrapInputCache[I,DI](implicit convert: I => DI, base: InputCache[DI]): InputCache[I] =
new WrappedInputCache(convert, base)
def wrapOutputCache[O,DO](implicit convert: O => DO, reverse: DO => O, base: OutputCache[DO]): OutputCache[O] =
new WrappedOutputCache[O,DO](convert, reverse, base)
/* Note: Task[O] { type Input = I } is written out because ITask[I,O] did not work (type could not be inferred properly) with a task
* with an HList input.*/
def apply[I,O](task: Task[O] { type Input = I }, file: File)(implicit cache: Cache[I,O]): Task[O] { type Input = I } =
task match { case m: M[I,O,_] =>
new M[I,O,Result[O]](None)(m.dependencies)(m.extract)(computeWithCache(m, cache, file))
}
private def computeWithCache[I,O](m: M[I,O,_], cache: Cache[I,O], file: File)(in: I): Result[O] =
cache(file)(in) match
{
case Left(value) => Value(value)
case Right(store) => NewTask(m.map { out => store(out); out })
}
}
trait BasicCacheImplicits extends NotNull
{
implicit def basicInputCache[I](implicit format: Format[I], equiv: Equiv[I]): InputCache[I] =
new BasicInputCache(format, equiv)
implicit def basicOutputCache[O](implicit format: Format[O]): OutputCache[O] =
new BasicOutputCache(format)
implicit def ioCache[I,O](implicit input: InputCache[I], output: OutputCache[O]): Cache[I,O] =
new SeparatedCache(input, output)
implicit def defaultEquiv[T]: Equiv[T] = new Equiv[T] { def equiv(a: T, b: T) = a == b }
}
trait HListCacheImplicits extends HLists
{
implicit def hConsInputCache[H,T<:HList](implicit headCache: InputCache[H], tailCache: InputCache[T]): InputCache[HCons[H,T]] =
new HConsInputCache(headCache, tailCache)
implicit lazy val hNilInputCache: InputCache[HNil] = new HNilInputCache
implicit def hConsOutputCache[H,T<:HList](implicit headCache: OutputCache[H], tailCache: OutputCache[T]): OutputCache[HCons[H,T]] =
new HConsOutputCache(headCache, tailCache)
implicit lazy val hNilOutputCache: OutputCache[HNil] = new HNilOutputCache
}

74
cache/FileInfo.scala vendored Normal file
View File

@ -0,0 +1,74 @@
package xsbt
import java.io.{File, IOException}
import sbinary.{DefaultProtocol, Format}
import DefaultProtocol._
import Function.tupled
sealed trait FileInfo extends NotNull
{
val file: File
}
sealed trait HashFileInfo extends FileInfo
{
val hash: List[Byte]
}
sealed trait ModifiedFileInfo extends FileInfo
{
val lastModified: Long
}
sealed trait HashModifiedFileInfo extends HashFileInfo with ModifiedFileInfo
private final case class FileHash(file: File, hash: List[Byte]) extends HashFileInfo
private final case class FileModified(file: File, lastModified: Long) extends ModifiedFileInfo
private final case class FileHashModified(file: File, hash: List[Byte], lastModified: Long) extends HashModifiedFileInfo
object FileInfo
{
sealed trait Style[F <: FileInfo] extends NotNull
{
implicit def apply(file: File): F
implicit def unapply(info: F): File = info.file
implicit val format: Format[F]
import Cache._
implicit def infoInputCache: InputCache[File] = wrapInputCache[File,F]
implicit def infoOutputCache: OutputCache[File] = wrapOutputCache[File,F]
}
object full extends Style[HashModifiedFileInfo]
{
implicit def apply(file: File): HashModifiedFileInfo = make(file, Hash(file).toList, file.lastModified)
def make(file: File, hash: List[Byte], lastModified: Long): HashModifiedFileInfo = FileHashModified(file.getAbsoluteFile, hash, lastModified)
implicit val format: Format[HashModifiedFileInfo] = wrap(f => (f.file, f.hash, f.lastModified), tupled(make _))
}
object hash extends Style[HashFileInfo]
{
implicit def apply(file: File): HashFileInfo = make(file, computeHash(file).toList)
def make(file: File, hash: List[Byte]): HashFileInfo = FileHash(file.getAbsoluteFile, hash)
implicit val format: Format[HashFileInfo] = wrap(f => (f.file, f.hash), tupled(make _))
private def computeHash(file: File) = try { Hash(file) } catch { case e: Exception => Nil }
}
object lastModified extends Style[ModifiedFileInfo]
{
implicit def apply(file: File): ModifiedFileInfo = make(file, file.lastModified)
def make(file: File, lastModified: Long): ModifiedFileInfo = FileModified(file.getAbsoluteFile, lastModified)
implicit val format: Format[ModifiedFileInfo] = wrap(f => (f.file, f.lastModified), tupled(make _))
}
}
final case class FilesInfo[F <: FileInfo] private(files: Set[F]) extends NotNull
object FilesInfo
{
sealed trait Style[F <: FileInfo] extends NotNull
{
implicit def apply(files: Iterable[File]): FilesInfo[F]
implicit val format: Format[FilesInfo[F]]
}
private final class BasicStyle[F <: FileInfo](fileStyle: FileInfo.Style[F])(implicit infoFormat: Format[F]) extends Style[F]
{
implicit def apply(files: Iterable[File]) = FilesInfo( (Set() ++ files.map(_.getAbsoluteFile)).map(fileStyle.apply) )
implicit val format: Format[FilesInfo[F]] = wrap(_.files, (fs: Set[F]) => new FilesInfo(fs))
}
lazy val full: Style[HashModifiedFileInfo] = new BasicStyle(FileInfo.full)(FileInfo.full.format)
lazy val hash: Style[HashFileInfo] = new BasicStyle(FileInfo.hash)(FileInfo.hash.format)
lazy val lastModified: Style[ModifiedFileInfo] = new BasicStyle(FileInfo.lastModified)(FileInfo.lastModified.format)
}

44
cache/HListCache.scala vendored Normal file
View File

@ -0,0 +1,44 @@
package xsbt
import java.io.{InputStream,OutputStream}
import metascala.HLists.{HCons,HList,HNil}
class HNilInputCache extends NoInputCache[HNil]
class HConsInputCache[H,T <: HList](val headCache: InputCache[H], val tailCache: InputCache[T]) extends InputCache[HCons[H,T]]
{
def uptodate(in: HCons[H,T])(cacheStream: InputStream) =
{
lazy val headResult = headCache.uptodate(in.head)(cacheStream)
lazy val tailResult = tailCache.uptodate(in.tail)(cacheStream)
new CacheResult
{
lazy val uptodate = headResult.uptodate && tailResult.uptodate
def update(outputStream: OutputStream) =
{
headResult.update(outputStream)
tailResult.update(outputStream)
}
}
}
def force(in: HCons[H,T])(cacheStream: OutputStream) =
{
headCache.force(in.head)(cacheStream)
tailCache.force(in.tail)(cacheStream)
}
}
class HNilOutputCache extends NoOutputCache[HNil](HNil)
class HConsOutputCache[H,T <: HList](val headCache: OutputCache[H], val tailCache: OutputCache[T]) extends OutputCache[HCons[H,T]]
{
def loadCached(cacheStream: InputStream) =
{
val head = headCache.loadCached(cacheStream)
val tail = tailCache.loadCached(cacheStream)
HCons(head, tail)
}
def update(out: HCons[H,T])(cacheStream: OutputStream)
{
headCache.update(out.head)(cacheStream)
tailCache.update(out.tail)(cacheStream)
}
}

19
cache/NoCache.scala vendored Normal file
View File

@ -0,0 +1,19 @@
package xsbt
import java.io.{InputStream,OutputStream}
class NoInputCache[T] extends InputCache[T]
{
def uptodate(in: T)(cacheStream: InputStream) =
new CacheResult
{
def uptodate = true
def update(outputStream: OutputStream) {}
}
def force(in: T)(outputStream: OutputStream) {}
}
class NoOutputCache[O](create: => O) extends OutputCache[O]
{
def loadCached(cacheStream: InputStream) = create
def update(out: O)(cacheStream: OutputStream) {}
}

80
cache/SeparatedCache.scala vendored Normal file
View File

@ -0,0 +1,80 @@
package xsbt
import sbinary.Format
import sbinary.JavaIO._
import java.io.{File, InputStream, OutputStream}
trait CacheResult
{
def uptodate: Boolean
def update(stream: OutputStream): Unit
}
trait InputCache[I] extends NotNull
{
def uptodate(in: I)(cacheStream: InputStream): CacheResult
def force(in: I)(cacheStream: OutputStream): Unit
}
trait OutputCache[O] extends NotNull
{
def loadCached(cacheStream: InputStream): O
def update(out: O)(cacheStream: OutputStream): Unit
}
class SeparatedCache[I,O](input: InputCache[I], output: OutputCache[O]) extends Cache[I,O]
{
def apply(file: File)(in: I) =
try { applyImpl(file, in) }
catch { case _: Exception => Right(update(file)(in)) }
protected def applyImpl(file: File, in: I) =
{
OpenResource.fileInputStream(file) { stream =>
val cache = input.uptodate(in)(stream)
lazy val doUpdate = (result: O) =>
{
OpenResource.fileOutputStream(false)(file) { stream =>
cache.update(stream)
output.update(result)(stream)
}
}
if(cache.uptodate)
try { Left(output.loadCached(stream)) }
catch { case _: Exception => Right(doUpdate) }
else
Right(doUpdate)
}
}
protected def update(file: File)(in: I)(out: O)
{
OpenResource.fileOutputStream(false)(file) { stream =>
input.force(in)(stream)
output.update(out)(stream)
}
}
}
class BasicOutputCache[O](val format: Format[O]) extends OutputCache[O]
{
def loadCached(cacheStream: InputStream): O = format.reads(cacheStream)
def update(out: O)(cacheStream: OutputStream): Unit = format.writes(cacheStream, out)
}
class BasicInputCache[I](val format: Format[I], val equiv: Equiv[I]) extends InputCache[I]
{
def uptodate(in: I)(cacheStream: InputStream) =
{
val loaded = format.reads(cacheStream)
new CacheResult
{
val uptodate = equiv.equiv(in, loaded)
def update(outputStream: OutputStream) = force(in)(outputStream)
}
}
def force(in: I)(outputStream: OutputStream) = format.writes(outputStream, in)
}
class WrappedInputCache[I,DI](val convert: I => DI, val base: InputCache[DI]) extends InputCache[I]
{
def uptodate(in: I)(cacheStream: InputStream) = base.uptodate(convert(in))(cacheStream)
def force(in: I)(outputStream: OutputStream) = base.force(convert(in))(outputStream)
}
class WrappedOutputCache[O,DO](val convert: O => DO, val reverse: DO => O, val base: OutputCache[DO]) extends OutputCache[O]
{
def loadCached(cacheStream: InputStream): O = reverse(base.loadCached(cacheStream))
def update(out: O)(cacheStream: OutputStream): Unit = base.update(convert(out))(cacheStream)
}

BIN
cache/lib/sbinary-0.3-alpha.jar vendored Normal file

Binary file not shown.

21
cache/src/test/scala/CacheTest.scala vendored Normal file
View File

@ -0,0 +1,21 @@
package xsbt
import java.io.File
object CacheTest// extends Properties("Cache test")
{
import Task._
import Cache._
import FileInfo.hash._
def checkFormattable(file: File)
{
val createTask = Task { new File("test") }
val lengthTask = createTask map { f => println("File length: " + f.length); f.length }
val cached = Cache(lengthTask, new File("/tmp/length-cache"))
val cTask = (createTask :: cached :: TNil) map { case (file :: len :: HNil) => println("File: " + file + " length: " + len); len :: file :: HNil }
val cachedC = Cache(cTask, new File("/tmp/c-cache"))
TaskRunner(cachedC).left.foreach(_.foreach(f => f.exception.printStackTrace))
}
}

View File

@ -0,0 +1,15 @@
package xsbt
import metascala.HLists.{HCons => metaHCons, HList => metaHList, HNil => metaHNil}
object HLists extends HLists
// add an extractor to metascala.HLists and define aliases to the HList classes in the xsbt namespace
trait HLists extends NotNull
{
object :: { def unapply[H,T<:HList](list: HCons[H,T]) = Some((list.head,list.tail)) }
final val HNil = metaHNil
final type ::[H, T <: HList] = metaHCons[H, T]
final type HNil = metaHNil
final type HList = metaHList
final type HCons[H, T <: HList] = metaHCons[H, T]
}

View File

@ -0,0 +1,22 @@
package xsbt
import scala.collection.{mutable,immutable}
// immutable.HashSet is not suitable for multi-threaded access, so this
// implementation uses an underlying immutable.TreeHashMap, which is suitable
object TreeHashSet
{
def apply[T](contents: T*) = new TreeHashSet(immutable.TreeHashMap( andUnit(contents) : _*))
def andUnit[T](contents: Iterable[T]) = contents.map(c => (c,()) ).toSeq
}
final class TreeHashSet[T](backing: immutable.TreeHashMap[T,Unit]) extends immutable.Set[T]
{
import TreeHashSet.andUnit
override def contains(t: T) = backing.contains(t)
override def ++(s: Iterable[T]) = new TreeHashSet(backing ++ andUnit(s))
override def +(s: T) = ++( Seq(s) )
override def -(s: T) = new TreeHashSet(backing - s)
override def elements = backing.keys
override def empty[A] = TreeHashSet[A]()
override def size = backing.size
}

Binary file not shown.

View File

@ -0,0 +1,18 @@
package xsbt
object ErrorHandling
{
def translate[T](msg: => String)(f: => T) =
try { f }
catch { case e => throw new TranslatedException(msg + e.toString, e) }
def wideConvert[T](f: => T): Either[Throwable, T] =
try { Right(f) }
catch { case e => Left(e) } // TODO: restrict type of e
def convert[T](f: => T): Either[Exception, T] =
try { Right(f) }
catch { case e: Exception => Left(e) }
}
final class TranslatedException private[xsbt](msg: String, cause: Throwable) extends RuntimeException(msg, cause)
{
override def toString = msg
}

71
util/log/Logger.scala Normal file
View File

@ -0,0 +1,71 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
abstract class Logger extends NotNull
{
def getLevel: Level.Value
def setLevel(newLevel: Level.Value)
def enableTrace(flag: Boolean)
def traceEnabled: Boolean
def atLevel(level: Level.Value) = level.id >= getLevel.id
def trace(t: => Throwable): Unit
final def debug(message: => String): Unit = log(Level.Debug, message)
final def info(message: => String): Unit = log(Level.Info, message)
final def warn(message: => String): Unit = log(Level.Warn, message)
final def error(message: => String): Unit = log(Level.Error, message)
def success(message: => String): Unit
def log(level: Level.Value, message: => String): Unit
def control(event: ControlEvent.Value, message: => String): Unit
def logAll(events: Seq[LogEvent]): Unit
/** Defined in terms of other methods in Logger and should not be called from them. */
final def log(event: LogEvent)
{
event match
{
case s: Success => success(s.msg)
case l: Log => log(l.level, l.msg)
case t: Trace => trace(t.exception)
case setL: SetLevel => setLevel(setL.newLevel)
case setT: SetTrace => enableTrace(setT.enabled)
case c: ControlEvent => control(c.event, c.msg)
}
}
}
sealed trait LogEvent extends NotNull
final class Success(val msg: String) extends LogEvent
final class Log(val level: Level.Value, val msg: String) extends LogEvent
final class Trace(val exception: Throwable) extends LogEvent
final class SetLevel(val newLevel: Level.Value) extends LogEvent
final class SetTrace(val enabled: Boolean) extends LogEvent
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
object ControlEvent extends Enumeration
{
val Start, Header, Finish = Value
}
/** An enumeration defining the levels available for logging. A level includes all of the levels
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).*/
object Level extends Enumeration with NotNull
{
val Debug = Value(1, "debug")
val Info = Value(2, "info")
val Warn = Value(3, "warn")
val Error = Value(4, "error")
/** Defines the label to use for success messages. A success message is logged at the info level but
* uses this label. Because the label for levels is defined in this module, the success
* label is also defined here. */
val SuccessLabel = "success"
// added because elements was renamed to iterator in 2.8.0 nightly
def levels = Debug :: Info :: Warn :: Error :: Nil
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
def apply(s: String) = levels.find(s == _.toString)
/** Same as apply, defined for use in pattern matching. */
private[xsbt] def unapply(s: String) = apply(s)
}