mirror of https://github.com/sbt/sbt.git
Merge branch '0.9' of github.com:harrah/xsbt into 0.9
This commit is contained in:
commit
20935b98fa
|
|
@ -2,7 +2,6 @@ package xsbt
|
|||
|
||||
import sbinary.{CollectionTypes, Format, JavaFormats}
|
||||
import java.io.File
|
||||
import scala.reflect.Manifest
|
||||
|
||||
trait Cache[I,O]
|
||||
{
|
||||
|
|
@ -23,13 +22,6 @@ object Cache extends BasicCacheImplicits with SBinaryFormats with HListCacheImpl
|
|||
def wrapOutputCache[O,DO](implicit convert: O => DO, reverse: DO => O, base: OutputCache[DO]): OutputCache[O] =
|
||||
new WrappedOutputCache[O,DO](convert, reverse, base)
|
||||
|
||||
def apply[I,O](file: File)(f: I => Task[O])(implicit cache: Cache[I,O]): I => Task[O] =
|
||||
in =>
|
||||
cache(file)(in) match
|
||||
{
|
||||
case Left(value) => Task(value)
|
||||
case Right(store) => f(in) map { out => store(out); out }
|
||||
}
|
||||
def cached[I,O](file: File)(f: I => O)(implicit cache: Cache[I,O]): I => O =
|
||||
in =>
|
||||
cache(file)(in) match
|
||||
|
|
@ -61,4 +53,4 @@ trait HListCacheImplicits extends HLists
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ sealed trait ModifiedFileInfo extends FileInfo
|
|||
{
|
||||
val lastModified: Long
|
||||
}
|
||||
sealed trait PlainFileInfo extends FileInfo
|
||||
sealed trait HashModifiedFileInfo extends HashFileInfo with ModifiedFileInfo
|
||||
|
||||
private final case class PlainFile(file: File) extends PlainFileInfo
|
||||
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
|
||||
|
|
@ -32,8 +34,6 @@ object FileInfo
|
|||
implicit def apply(file: File): F
|
||||
implicit def unapply(info: F): File = info.file
|
||||
implicit val format: Format[F]
|
||||
/*val manifest: Manifest[F]
|
||||
def formatManifest: Manifest[Format[F]] = CacheIO.manifest[Format[F]]*/
|
||||
import Cache._
|
||||
implicit def infoInputCache: InputCache[File] = wrapInputCache[File,F]
|
||||
implicit def infoOutputCache: OutputCache[File] = wrapOutputCache[File,F]
|
||||
|
|
@ -41,7 +41,6 @@ object FileInfo
|
|||
object full extends Style
|
||||
{
|
||||
type F = HashModifiedFileInfo
|
||||
//val manifest: Manifest[F] = CacheIO.manifest[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 _))
|
||||
|
|
@ -49,7 +48,6 @@ object FileInfo
|
|||
object hash extends Style
|
||||
{
|
||||
type F = HashFileInfo
|
||||
//val manifest: Manifest[F] = CacheIO.manifest[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 _))
|
||||
|
|
@ -58,11 +56,17 @@ object FileInfo
|
|||
object lastModified extends Style
|
||||
{
|
||||
type F = ModifiedFileInfo
|
||||
//val manifest: Manifest[F] = CacheIO.manifest[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 _))
|
||||
}
|
||||
object exists extends Style
|
||||
{
|
||||
type F = PlainFileInfo
|
||||
implicit def apply(file: File): PlainFileInfo = make(file)
|
||||
def make(file: File): PlainFileInfo = PlainFile(file.getAbsoluteFile)
|
||||
implicit val format: Format[PlainFileInfo] = wrap(_.file, make)
|
||||
}
|
||||
}
|
||||
|
||||
final case class FilesInfo[F <: FileInfo] private(files: Set[F]) extends NotNull
|
||||
|
|
@ -92,4 +96,5 @@ object FilesInfo
|
|||
lazy val full: Style = new BasicStyle(FileInfo.full)
|
||||
lazy val hash: Style = new BasicStyle(FileInfo.hash)
|
||||
lazy val lastModified: Style = new BasicStyle(FileInfo.lastModified)
|
||||
lazy val exists: Style = new BasicStyle(FileInfo.exists)
|
||||
}
|
||||
|
|
@ -7,22 +7,23 @@ object CacheTest// extends Properties("Cache test")
|
|||
val lengthCache = new File("/tmp/length-cache")
|
||||
val cCache = new File("/tmp/c-cache")
|
||||
|
||||
import Task._
|
||||
import Cache._
|
||||
import FileInfo.hash._
|
||||
def test
|
||||
{
|
||||
val createTask = Task { new File("test") }
|
||||
lazy val create = new File("test")
|
||||
|
||||
val length = (f: File) => { println("File length: " + f.length); f.length }
|
||||
val cachedLength = cached(lengthCache) ( length )
|
||||
val length = cached(lengthCache) {
|
||||
(f: File) => { println("File length: " + f.length); f.length }
|
||||
}
|
||||
|
||||
val lengthTask = createTask map cachedLength
|
||||
lazy val fileLength = length(create)
|
||||
|
||||
val c = (file: File, len: Long) => { println("File: " + file + ", length: " + len); len :: file :: HNil }
|
||||
val cTask = (createTask :: lengthTask :: TNil) map cached(cCache) { case (file :: len :: HNil) => c(file, len) }
|
||||
|
||||
try { TaskRunner(cTask) }
|
||||
catch { case TasksFailed(failures) => failures.foreach(_.exception.printStackTrace) }
|
||||
val c = cached(cCache) { (in: (File :: Long :: HNil)) =>
|
||||
val file :: len :: HNil = in
|
||||
println("File: " + file + " (" + file.exists + "), length: " + len)
|
||||
(len+1) :: file :: HNil
|
||||
}
|
||||
c(create :: fileLength :: HNil)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,112 +7,152 @@ import java.io.{File,IOException}
|
|||
import CacheIO.{fromFile, toFile}
|
||||
import sbinary.Format
|
||||
import scala.reflect.Manifest
|
||||
import Task.{iterableToBuilder, iterableToForkBuilder}
|
||||
import xsbt.FileUtilities.{delete, read, write}
|
||||
|
||||
/* A proper implementation of fileTask that tracks inputs and outputs properly
|
||||
|
||||
def fileTask(cacheBaseDirectory: Path)(inputs: PathFinder, outputs: PathFinder)(action: => Unit): Task =
|
||||
fileTask(cacheBaseDirectory, FilesInfo.hash, FilesInfo.lastModified)
|
||||
def fileTask(cacheBaseDirectory: Path, inStyle: FilesInfo.Style, outStyle: FilesInfo.Style)(inputs: PathFinder, outputs: PathFinder)(action: => Unit): Task =
|
||||
{
|
||||
lazy val inCache = diffInputs(base / "in-cache", inStyle)(inputs)
|
||||
lazy val outCache = diffOutputs(base / "out-cache", outStyle)(outputs)
|
||||
task
|
||||
{
|
||||
inCache { inReport =>
|
||||
outCache { outReport =>
|
||||
if(inReport.modified.isEmpty && outReport.modified.isEmpty) () else action
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
object Tracked
|
||||
{
|
||||
/** Creates a tracker that provides the last time it was evaluated.
|
||||
* If 'useStartTime' is true, the recorded time is the start of the evaluated function.
|
||||
* If 'useStartTime' is false, the recorded time is when the evaluated function completes.
|
||||
* In both cases, the timestamp is not updated if the function throws an exception.*/
|
||||
def tstamp(cacheFile: File, useStartTime: Boolean): Timestamp = new Timestamp(cacheFile)
|
||||
/** Creates a tracker that only evaluates a function when the input has changed.*/
|
||||
def changed[O](cacheFile: File)(getValue: => O)(implicit input: InputCache[O]): Changed[O] =
|
||||
new Changed[O](getValue, cacheFile)
|
||||
|
||||
/** Creates a tracker that provides the difference between the set of input files provided for successive invocations.*/
|
||||
def diffInputs(cache: File, style: FilesInfo.Style)(files: => Set[File]): Difference =
|
||||
Difference.inputs(files, style, cache)
|
||||
/** Creates a tracker that provides the difference between the set of output files provided for successive invocations.*/
|
||||
def diffOutputs(cache: File, style: FilesInfo.Style)(files: => Set[File]): Difference =
|
||||
Difference.outputs(files, style, cache)
|
||||
}
|
||||
|
||||
trait Tracked extends NotNull
|
||||
{
|
||||
/** Cleans outputs. This operation might require information from the cache, so it should be called first if clear is also called.*/
|
||||
def clean: Task[Unit]
|
||||
/** Clears the cache. If also cleaning, 'clean' should be called first as it might require information from the cache.*/
|
||||
def clear: Task[Unit]
|
||||
/** Cleans outputs and clears the cache.*/
|
||||
def clean: Unit
|
||||
}
|
||||
class Timestamp(val cacheFile: File) extends Tracked
|
||||
class Timestamp(val cacheFile: File, useStartTime: Boolean) extends Tracked
|
||||
{
|
||||
val clean = Clean(cacheFile)
|
||||
def clear = Task.empty
|
||||
def apply[T](f: Long => Task[T]): Task[T] =
|
||||
def clean = delete(cacheFile)
|
||||
/** Reads the previous timestamp, evaluates the provided function, and then updates the timestamp.*/
|
||||
def apply[T](f: Long => T): T =
|
||||
{
|
||||
val getTimestamp = Task { readTimestamp }
|
||||
getTimestamp bind f map { result =>
|
||||
FileUtilities.write(cacheFile, System.currentTimeMillis.toString)
|
||||
result
|
||||
}
|
||||
val start = now()
|
||||
val result = f(readTimestamp)
|
||||
write(cacheFile, (if(useStartTime) start else now()).toString)
|
||||
result
|
||||
}
|
||||
private def now() = System.currentTimeMillis
|
||||
def readTimestamp: Long =
|
||||
try { FileUtilities.read(cacheFile).toLong }
|
||||
try { read(cacheFile).toLong }
|
||||
catch { case _: NumberFormatException | _: java.io.FileNotFoundException => 0 }
|
||||
}
|
||||
object Clean
|
||||
{
|
||||
def apply(src: Task[Set[File]]): Task[Unit] = src map FileUtilities.delete
|
||||
def apply(srcs: File*): Task[Unit] = Task(FileUtilities.delete(srcs))
|
||||
def apply(srcs: Set[File]): Task[Unit] = Task(FileUtilities.delete(srcs))
|
||||
}
|
||||
|
||||
class Changed[O](val task: Task[O], val cacheFile: File)(implicit input: InputCache[O]) extends Tracked
|
||||
class Changed[O](getValue: => O, val cacheFile: File)(implicit input: InputCache[O]) extends Tracked
|
||||
{
|
||||
val clean = Clean(cacheFile)
|
||||
def clear = Task.empty
|
||||
def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): Task[O2] =
|
||||
task map { value =>
|
||||
val cache =
|
||||
try { OpenResource.fileInputStream(cacheFile)(input.uptodate(value)) }
|
||||
catch { case _: IOException => new ForceResult(input)(value) }
|
||||
if(cache.uptodate)
|
||||
ifUnchanged(value)
|
||||
else
|
||||
{
|
||||
OpenResource.fileOutputStream(false)(cacheFile)(cache.update)
|
||||
ifChanged(value)
|
||||
}
|
||||
def clean = delete(cacheFile)
|
||||
def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): O2 =
|
||||
{
|
||||
val value = getValue
|
||||
val cache =
|
||||
try { OpenResource.fileInputStream(cacheFile)(input.uptodate(value)) }
|
||||
catch { case _: IOException => new ForceResult(input)(value) }
|
||||
if(cache.uptodate)
|
||||
ifUnchanged(value)
|
||||
else
|
||||
{
|
||||
OpenResource.fileOutputStream(false)(cacheFile)(cache.update)
|
||||
ifChanged(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
object Difference
|
||||
{
|
||||
sealed class Constructor private[Difference](defineClean: Boolean, filesAreOutputs: Boolean) extends NotNull
|
||||
{
|
||||
def apply(filesTask: Task[Set[File]], style: FilesInfo.Style, cache: File): Difference = new Difference(filesTask, style, cache, defineClean, filesAreOutputs)
|
||||
def apply(files: Set[File], style: FilesInfo.Style, cache: File): Difference = apply(Task(files), style, cache)
|
||||
def apply(files: => Set[File], style: FilesInfo.Style, cache: File): Difference = new Difference(files, style, cache, defineClean, filesAreOutputs)
|
||||
}
|
||||
/** Provides a constructor for a Difference that removes the files from the previous run on a call to 'clean' and saves the
|
||||
* hash/last modified time of the files as they are after running the function. This means that this information must be evaluated twice:
|
||||
* before and after running the function.*/
|
||||
object outputs extends Constructor(true, true)
|
||||
/** Provides a constructor for a Difference that does nothing on a call to 'clean' and saves the
|
||||
* hash/last modified time of the files as they were prior to running the function.*/
|
||||
object inputs extends Constructor(false, false)
|
||||
}
|
||||
class Difference(val filesTask: Task[Set[File]], val style: FilesInfo.Style, val cache: File, val defineClean: Boolean, val filesAreOutputs: Boolean) extends Tracked
|
||||
class Difference(getFiles: => Set[File], val style: FilesInfo.Style, val cache: File, val defineClean: Boolean, val filesAreOutputs: Boolean) extends Tracked
|
||||
{
|
||||
val clean = if(defineClean) Clean(Task(raw(cachedFilesInfo))) else Task.empty
|
||||
val clear = Clean(cache)
|
||||
def clean =
|
||||
{
|
||||
if(defineClean) delete(raw(cachedFilesInfo)) else ()
|
||||
clearCache()
|
||||
}
|
||||
private def clearCache = delete(cache)
|
||||
|
||||
private def cachedFilesInfo = fromFile(style.formats, style.empty)(cache)(style.manifest).files
|
||||
private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file)
|
||||
|
||||
def apply[T](f: ChangeReport[File] => Task[T]): Task[T] =
|
||||
filesTask bind { files =>
|
||||
val lastFilesInfo = cachedFilesInfo
|
||||
val lastFiles = raw(lastFilesInfo)
|
||||
val currentFiles = files.map(_.getAbsoluteFile)
|
||||
val currentFilesInfo = style(currentFiles)
|
||||
def apply[T](f: ChangeReport[File] => T): T =
|
||||
{
|
||||
val files = getFiles
|
||||
val lastFilesInfo = cachedFilesInfo
|
||||
val lastFiles = raw(lastFilesInfo)
|
||||
val currentFiles = files.map(_.getAbsoluteFile)
|
||||
val currentFilesInfo = style(currentFiles)
|
||||
|
||||
val report = new ChangeReport[File]
|
||||
{
|
||||
lazy val checked = currentFiles
|
||||
lazy val removed = lastFiles -- checked // all files that were included previously but not this time. This is independent of whether the files exist.
|
||||
lazy val added = checked -- lastFiles // all files included now but not previously. This is independent of whether the files exist.
|
||||
lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) ++ added
|
||||
lazy val unmodified = checked -- modified
|
||||
}
|
||||
|
||||
f(report) map { result =>
|
||||
val info = if(filesAreOutputs) style(currentFiles) else currentFilesInfo
|
||||
toFile(style.formats)(info)(cache)(style.manifest)
|
||||
result
|
||||
}
|
||||
val report = new ChangeReport[File]
|
||||
{
|
||||
lazy val checked = currentFiles
|
||||
lazy val removed = lastFiles -- checked // all files that were included previously but not this time. This is independent of whether the files exist.
|
||||
lazy val added = checked -- lastFiles // all files included now but not previously. This is independent of whether the files exist.
|
||||
lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) ++ added
|
||||
lazy val unmodified = checked -- modified
|
||||
}
|
||||
|
||||
val result = f(report)
|
||||
val info = if(filesAreOutputs) style(currentFiles) else currentFilesInfo
|
||||
toFile(style.formats)(info)(cache)(style.manifest)
|
||||
result
|
||||
}
|
||||
}
|
||||
class DependencyTracked[T](val cacheDirectory: File, val translateProducts: Boolean, cleanT: T => Unit)(implicit format: Format[T], mf: Manifest[T]) extends Tracked
|
||||
{
|
||||
private val trackFormat = new TrackingFormat[T](cacheDirectory, translateProducts)
|
||||
private def cleanAll(fs: Set[T]) = fs.foreach(cleanT)
|
||||
|
||||
val clean = Task(cleanAll(trackFormat.read.allProducts))
|
||||
val clear = Clean(cacheDirectory)
|
||||
def clean =
|
||||
{
|
||||
cleanAll(trackFormat.read.allProducts)
|
||||
delete(cacheDirectory)
|
||||
}
|
||||
|
||||
def apply[R](f: UpdateTracking[T] => Task[R]): Task[R] =
|
||||
def apply[R](f: UpdateTracking[T] => R): R =
|
||||
{
|
||||
val tracker = trackFormat.read
|
||||
f(tracker) map { result =>
|
||||
trackFormat.write(tracker)
|
||||
result
|
||||
}
|
||||
val result = f(tracker)
|
||||
trackFormat.write(tracker)
|
||||
result
|
||||
}
|
||||
}
|
||||
object InvalidateFiles
|
||||
|
|
@ -179,30 +219,29 @@ class InvalidateTransitive[T](cacheDirectory: File, translateProducts: Boolean,
|
|||
this(cacheDirectory, translateProducts, (_: T) => ())
|
||||
|
||||
private val tracked = new DependencyTracked(cacheDirectory, translateProducts, cleanT)
|
||||
def clean = tracked.clean
|
||||
def clear = tracked.clear
|
||||
|
||||
def apply[R](changes: ChangeReport[T])(f: (InvalidationReport[T], UpdateTracking[T]) => Task[R]): Task[R] =
|
||||
apply(Task(changes))(f)
|
||||
def apply[R](changesTask: Task[ChangeReport[T]])(f: (InvalidationReport[T], UpdateTracking[T]) => Task[R]): Task[R] =
|
||||
def clean
|
||||
{
|
||||
changesTask bind { changes =>
|
||||
tracked { tracker =>
|
||||
val report = InvalidateTransitive.andClean[T](tracker, _.foreach(cleanT), changes.modified)
|
||||
f(report, tracker)
|
||||
}
|
||||
tracked.clean
|
||||
tracked.clear
|
||||
}
|
||||
|
||||
def apply[R](getChanges: => ChangeReport[T])(f: (InvalidationReport[T], UpdateTracking[T]) => R): R =
|
||||
{
|
||||
val changes = getChanges
|
||||
tracked { tracker =>
|
||||
val report = InvalidateTransitive.andClean[T](tracker, _.foreach(cleanT), changes.modified)
|
||||
f(report, tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
class BasicTracked(filesTask: Task[Set[File]], style: FilesInfo.Style, cacheDirectory: File) extends Tracked
|
||||
class BasicTracked(files: => Set[File], style: FilesInfo.Style, cacheDirectory: File) extends Tracked
|
||||
{
|
||||
private val changed = Difference.inputs(filesTask, style, new File(cacheDirectory, "files"))
|
||||
private val changed = Difference.inputs(files, style, new File(cacheDirectory, "files"))
|
||||
private val invalidation = InvalidateFiles(new File(cacheDirectory, "invalidation"))
|
||||
private def onTracked(f: Tracked => Task[Unit]) = Seq(invalidation, changed).forkTasks(f).joinIgnore
|
||||
val clear = onTracked(_.clear)
|
||||
val clean = onTracked(_.clean)
|
||||
private def onTracked(f: Tracked => Unit) = { f(invalidation); f(changed) }
|
||||
def clean = onTracked(_.clean)
|
||||
|
||||
def apply[R](f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => Task[R]): Task[R] =
|
||||
def apply[R](f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => R): R =
|
||||
changed { sourceChanges =>
|
||||
invalidation(sourceChanges) { (report, tracking) =>
|
||||
f(sourceChanges, report, tracking)
|
||||
|
|
|
|||
|
|
@ -25,4 +25,10 @@ final case class HCons[H, T <: HList](head : H, tail : T) extends HList
|
|||
type Up = MCons[H, tail.Up, Id]
|
||||
def up = MCons[H,tail.Up, Id](head, tail.up)
|
||||
def :+: [G](g: G): G :+: H :+: T = HCons(g, this)
|
||||
}
|
||||
|
||||
object HList
|
||||
{
|
||||
// contains no type information: not even A
|
||||
implicit def fromList[A](list: Traversable[A]): HList = ((HNil: HList) /: list) ( (hl,v) => HCons(v, hl) )
|
||||
}
|
||||
|
|
@ -41,4 +41,11 @@ sealed class MNil extends MList[Nothing]
|
|||
|
||||
def toList = Nil
|
||||
}
|
||||
object MNil extends MNil
|
||||
object MNil extends MNil
|
||||
|
||||
|
||||
object MList
|
||||
{
|
||||
implicit def fromTCList[A[_]](list: Traversable[A[_]]): MList[A] = ((MNil: MList[A]) /: list) ( (hl,v) => MCons(v, hl) )
|
||||
implicit def fromList[A](list: Traversable[A]): MList[Const[A]#Apply] = ((MNil: MList[Const[A]#Apply]) /: list) ( (hl,v) => MCons[A, hl.type, Const[A]#Apply](v, hl) )
|
||||
}
|
||||
|
|
@ -13,6 +13,10 @@ trait TypeFunctions
|
|||
final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) }
|
||||
final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) }
|
||||
final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) }
|
||||
|
||||
implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] {
|
||||
def ∙[C](g: C => A) = f compose g
|
||||
}
|
||||
}
|
||||
object TypeFunctions extends TypeFunctions
|
||||
|
||||
|
|
@ -29,4 +33,7 @@ object ~>
|
|||
import TypeFunctions._
|
||||
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
|
||||
implicit def tcIdEquals: (Id ~> Id) = Id
|
||||
}
|
||||
trait Fn1[A, B] {
|
||||
def ∙[C](g: C => A): C => B
|
||||
}
|
||||
|
|
@ -7,10 +7,17 @@ object ErrorHandling
|
|||
{
|
||||
def translate[T](msg: => String)(f: => T) =
|
||||
try { f }
|
||||
catch { case e => throw new TranslatedException(msg + e.toString, e) }
|
||||
catch { case e: Exception => 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
|
||||
catch
|
||||
{
|
||||
case ex @ (_: Exception | _: StackOverflowError) => Left(ex)
|
||||
case err @ (_: ThreadDeath | _: VirtualMachineError) => throw err
|
||||
case x => Left(x)
|
||||
}
|
||||
|
||||
def convert[T](f: => T): Either[Exception, T] =
|
||||
try { Right(f) }
|
||||
catch { case e: Exception => Left(e) }
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
package sbt
|
||||
|
||||
/** Implements the level-setting methods of Logger.*/
|
||||
abstract class BasicLogger extends Logger
|
||||
abstract class BasicLogger extends AbstractLogger
|
||||
{
|
||||
private var traceEnabledVar = true
|
||||
private var traceEnabledVar = java.lang.Integer.MAX_VALUE
|
||||
private var level: Level.Value = Level.Info
|
||||
def getLevel = level
|
||||
def setLevel(newLevel: Level.Value) { level = newLevel }
|
||||
def enableTrace(flag: Boolean) { traceEnabledVar = flag }
|
||||
def traceEnabled = traceEnabledVar
|
||||
}
|
||||
def setTrace(level: Int) { traceEnabledVar = level }
|
||||
def getTrace = traceEnabledVar
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
|
||||
import sbt.{AbstractLogger, ControlEvent, Level, Log, LogEvent, SetLevel, SetTrace, Success, Trace}
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/** A logger that can buffer the logging done on it and then can flush the buffer
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
*
|
||||
* This class assumes that it is the only client of the delegate logger.
|
||||
* */
|
||||
class BufferedLogger(delegate: Logger) extends Logger
|
||||
class BufferedLogger(delegate: AbstractLogger) extends AbstractLogger
|
||||
{
|
||||
private[this] val buffer = new ListBuffer[LogEvent]
|
||||
private[this] var recording = false
|
||||
|
|
@ -54,10 +55,10 @@ class BufferedLogger(delegate: Logger) extends Logger
|
|||
}
|
||||
def getLevel = delegate.getLevel
|
||||
def traceEnabled = delegate.traceEnabled
|
||||
def enableTrace(flag: Boolean)
|
||||
def setTrace(level: Int)
|
||||
{
|
||||
buffer += new SetTrace(flag)
|
||||
delegate.enableTrace(flag)
|
||||
buffer += new SetTrace(level)
|
||||
delegate.setTrace(level)
|
||||
}
|
||||
|
||||
def trace(t: => Throwable): Unit =
|
||||
|
|
@ -73,9 +74,9 @@ class BufferedLogger(delegate: Logger) extends Logger
|
|||
delegate.logAll(events)
|
||||
def control(event: ControlEvent.Value, message: => String): Unit =
|
||||
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
|
||||
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
|
||||
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
|
||||
doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
|
||||
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
|
||||
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
|
||||
if(condition)
|
||||
{
|
||||
if(recording)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
package sbt
|
||||
|
||||
object ConsoleLogger
|
||||
{
|
||||
|
|
@ -17,10 +17,12 @@ object ConsoleLogger
|
|||
}
|
||||
|
||||
/** A logger that logs to the console. On supported systems, the level labels are
|
||||
* colored. */
|
||||
* colored.
|
||||
*
|
||||
* This logger is not thread-safe.*/
|
||||
class ConsoleLogger extends BasicLogger
|
||||
{
|
||||
import ConsoleLogger.formatEnabled
|
||||
override def ansiCodesSupported = ConsoleLogger.formatEnabled
|
||||
def messageColor(level: Level.Value) = Console.RESET
|
||||
def labelColor(level: Level.Value) =
|
||||
level match
|
||||
|
|
@ -39,8 +41,9 @@ class ConsoleLogger extends BasicLogger
|
|||
def trace(t: => Throwable): Unit =
|
||||
System.out.synchronized
|
||||
{
|
||||
if(traceEnabled)
|
||||
t.printStackTrace
|
||||
val traceLevel = getTrace
|
||||
if(traceLevel >= 0)
|
||||
System.out.synchronized { System.out.print(StackTrace.trimmed(t, traceLevel)) }
|
||||
}
|
||||
def log(level: Level.Value, message: => String)
|
||||
{
|
||||
|
|
@ -49,7 +52,7 @@ class ConsoleLogger extends BasicLogger
|
|||
}
|
||||
private def setColor(color: String)
|
||||
{
|
||||
if(formatEnabled)
|
||||
if(ansiCodesSupported)
|
||||
System.out.synchronized { System.out.print(color) }
|
||||
}
|
||||
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
/** A filter logger is used to delegate messages but not the logging level to another logger. This means
|
||||
* that messages are logged at the higher of the two levels set by this logger and its delegate.
|
||||
* */
|
||||
class FilterLogger(delegate: AbstractLogger) extends BasicLogger
|
||||
{
|
||||
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
|
||||
def trace(t: => Throwable)
|
||||
{
|
||||
if(traceEnabled)
|
||||
delegate.trace(t)
|
||||
}
|
||||
override def setTrace(level: Int) { delegate.setTrace(level) }
|
||||
override def getTrace = delegate.getTrace
|
||||
def log(level: Level.Value, message: => String)
|
||||
{
|
||||
if(atLevel(level))
|
||||
delegate.log(level, message)
|
||||
}
|
||||
def success(message: => String)
|
||||
{
|
||||
if(atLevel(Level.Info))
|
||||
delegate.success(message)
|
||||
}
|
||||
def control(event: ControlEvent.Value, message: => String)
|
||||
{
|
||||
if(atLevel(Level.Info))
|
||||
delegate.control(event, message)
|
||||
}
|
||||
def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events)
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
package sbt
|
||||
|
||||
/** 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
|
||||
object Level extends Enumeration
|
||||
{
|
||||
val Debug = Value(1, "debug")
|
||||
val Info = Value(2, "info")
|
||||
|
|
@ -16,10 +16,8 @@ object Level extends Enumeration with NotNull
|
|||
* 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)
|
||||
def apply(s: String) = values.find(s == _.toString)
|
||||
/** Same as apply, defined for use in pattern matching. */
|
||||
private[xsbt] def unapply(s: String) = apply(s)
|
||||
private[sbt] def unapply(s: String) = apply(s)
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
package sbt
|
||||
|
||||
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 SetTrace(val level: Int) extends LogEvent
|
||||
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
|
||||
|
||||
object ControlEvent extends Enumeration
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbt
|
||||
package sbt
|
||||
|
||||
import xsbti.{Logger => xLogger, F0}
|
||||
abstract class Logger extends xLogger with NotNull
|
||||
|
||||
abstract class AbstractLogger extends xLogger with NotNull
|
||||
{
|
||||
def getLevel: Level.Value
|
||||
def setLevel(newLevel: Level.Value)
|
||||
def enableTrace(flag: Boolean)
|
||||
def traceEnabled: Boolean
|
||||
def setTrace(flag: Int)
|
||||
def getTrace: Int
|
||||
final def traceEnabled = getTrace >= 0
|
||||
def ansiCodesSupported = false
|
||||
|
||||
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
||||
def trace(t: => Throwable): Unit
|
||||
final def verbose(message: => String): Unit = debug(message)
|
||||
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)
|
||||
|
|
@ -31,7 +35,7 @@ abstract class Logger extends xLogger with NotNull
|
|||
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 setT: SetTrace => setTrace(setT.level)
|
||||
case c: ControlEvent => control(c.event, c.msg)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
/** Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`.
|
||||
* A line is delimited by `nl`, which is by default the platform line separator.*/
|
||||
class LoggerWriter(delegate: AbstractLogger, level: Level.Value, nl: String) extends java.io.Writer
|
||||
{
|
||||
def this(delegate: AbstractLogger, level: Level.Value) = this(delegate, level, System.getProperty("line.separator"))
|
||||
|
||||
private[this] val buffer = new StringBuilder
|
||||
|
||||
override def close() = flush()
|
||||
override def flush(): Unit =
|
||||
synchronized {
|
||||
if(buffer.length > 0)
|
||||
{
|
||||
log(buffer.toString)
|
||||
buffer.clear()
|
||||
}
|
||||
}
|
||||
override def write(content: Array[Char], offset: Int, length: Int): Unit =
|
||||
synchronized {
|
||||
buffer.append(content, offset, length)
|
||||
process()
|
||||
}
|
||||
|
||||
private[this] def process()
|
||||
{
|
||||
val i = buffer.indexOf(nl)
|
||||
if(i >= 0)
|
||||
{
|
||||
log(buffer.substring(0, i))
|
||||
buffer.delete(0, i + nl.length)
|
||||
process()
|
||||
}
|
||||
}
|
||||
private[this] def log(s: String): Unit = delegate.log(level, s)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
|
||||
class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger
|
||||
{
|
||||
override lazy val ansiCodesSupported = delegates.forall(_.ansiCodesSupported)
|
||||
override def setLevel(newLevel: Level.Value)
|
||||
{
|
||||
super.setLevel(newLevel)
|
||||
dispatch(new SetLevel(newLevel))
|
||||
}
|
||||
override def setTrace(level: Int)
|
||||
{
|
||||
super.setTrace(level)
|
||||
dispatch(new SetTrace(level))
|
||||
}
|
||||
def trace(t: => Throwable) { dispatch(new Trace(t)) }
|
||||
def log(level: Level.Value, message: => String) { dispatch(new Log(level, message)) }
|
||||
def success(message: => String) { dispatch(new Success(message)) }
|
||||
def logAll(events: Seq[LogEvent]) { delegates.foreach(_.logAll(events)) }
|
||||
def control(event: ControlEvent.Value, message: => String) { delegates.foreach(_.control(event, message)) }
|
||||
private def dispatch(event: LogEvent) { delegates.foreach(_.log(event)) }
|
||||
}
|
||||
Loading…
Reference in New Issue