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
1ee470282d
|
|
@ -2,7 +2,6 @@ package xsbt
|
||||||
|
|
||||||
import sbinary.{CollectionTypes, Format, JavaFormats}
|
import sbinary.{CollectionTypes, Format, JavaFormats}
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import scala.reflect.Manifest
|
|
||||||
|
|
||||||
trait Cache[I,O]
|
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] =
|
def wrapOutputCache[O,DO](implicit convert: O => DO, reverse: DO => O, base: OutputCache[DO]): OutputCache[O] =
|
||||||
new WrappedOutputCache[O,DO](convert, reverse, base)
|
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 =
|
def cached[I,O](file: File)(f: I => O)(implicit cache: Cache[I,O]): I => O =
|
||||||
in =>
|
in =>
|
||||||
cache(file)(in) match
|
cache(file)(in) match
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@ sealed trait ModifiedFileInfo extends FileInfo
|
||||||
{
|
{
|
||||||
val lastModified: Long
|
val lastModified: Long
|
||||||
}
|
}
|
||||||
|
sealed trait PlainFileInfo extends FileInfo
|
||||||
sealed trait HashModifiedFileInfo extends HashFileInfo with ModifiedFileInfo
|
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 FileHash(file: File, hash: List[Byte]) extends HashFileInfo
|
||||||
private final case class FileModified(file: File, lastModified: Long) extends ModifiedFileInfo
|
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
|
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 apply(file: File): F
|
||||||
implicit def unapply(info: F): File = info.file
|
implicit def unapply(info: F): File = info.file
|
||||||
implicit val format: Format[F]
|
implicit val format: Format[F]
|
||||||
/*val manifest: Manifest[F]
|
|
||||||
def formatManifest: Manifest[Format[F]] = CacheIO.manifest[Format[F]]*/
|
|
||||||
import Cache._
|
import Cache._
|
||||||
implicit def infoInputCache: InputCache[File] = wrapInputCache[File,F]
|
implicit def infoInputCache: InputCache[File] = wrapInputCache[File,F]
|
||||||
implicit def infoOutputCache: OutputCache[File] = wrapOutputCache[File,F]
|
implicit def infoOutputCache: OutputCache[File] = wrapOutputCache[File,F]
|
||||||
|
|
@ -41,7 +41,6 @@ object FileInfo
|
||||||
object full extends Style
|
object full extends Style
|
||||||
{
|
{
|
||||||
type F = HashModifiedFileInfo
|
type F = HashModifiedFileInfo
|
||||||
//val manifest: Manifest[F] = CacheIO.manifest[HashModifiedFileInfo]
|
|
||||||
implicit def apply(file: File): HashModifiedFileInfo = make(file, Hash(file).toList, file.lastModified)
|
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)
|
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 _))
|
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
|
object hash extends Style
|
||||||
{
|
{
|
||||||
type F = HashFileInfo
|
type F = HashFileInfo
|
||||||
//val manifest: Manifest[F] = CacheIO.manifest[HashFileInfo]
|
|
||||||
implicit def apply(file: File): HashFileInfo = make(file, computeHash(file).toList)
|
implicit def apply(file: File): HashFileInfo = make(file, computeHash(file).toList)
|
||||||
def make(file: File, hash: List[Byte]): HashFileInfo = FileHash(file.getAbsoluteFile, hash)
|
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 _))
|
implicit val format: Format[HashFileInfo] = wrap(f => (f.file, f.hash), tupled(make _))
|
||||||
|
|
@ -58,11 +56,17 @@ object FileInfo
|
||||||
object lastModified extends Style
|
object lastModified extends Style
|
||||||
{
|
{
|
||||||
type F = ModifiedFileInfo
|
type F = ModifiedFileInfo
|
||||||
//val manifest: Manifest[F] = CacheIO.manifest[ModifiedFileInfo]
|
|
||||||
implicit def apply(file: File): ModifiedFileInfo = make(file, file.lastModified)
|
implicit def apply(file: File): ModifiedFileInfo = make(file, file.lastModified)
|
||||||
def make(file: File, lastModified: Long): ModifiedFileInfo = FileModified(file.getAbsoluteFile, 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 _))
|
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
|
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 full: Style = new BasicStyle(FileInfo.full)
|
||||||
lazy val hash: Style = new BasicStyle(FileInfo.hash)
|
lazy val hash: Style = new BasicStyle(FileInfo.hash)
|
||||||
lazy val lastModified: Style = new BasicStyle(FileInfo.lastModified)
|
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 lengthCache = new File("/tmp/length-cache")
|
||||||
val cCache = new File("/tmp/c-cache")
|
val cCache = new File("/tmp/c-cache")
|
||||||
|
|
||||||
import Task._
|
|
||||||
import Cache._
|
import Cache._
|
||||||
import FileInfo.hash._
|
import FileInfo.hash._
|
||||||
def test
|
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 length = cached(lengthCache) {
|
||||||
val cachedLength = cached(lengthCache) ( length )
|
(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 c = cached(cCache) { (in: (File :: Long :: HNil)) =>
|
||||||
val cTask = (createTask :: lengthTask :: TNil) map cached(cCache) { case (file :: len :: HNil) => c(file, len) }
|
val file :: len :: HNil = in
|
||||||
|
println("File: " + file + " (" + file.exists + "), length: " + len)
|
||||||
try { TaskRunner(cTask) }
|
(len+1) :: file :: HNil
|
||||||
catch { case TasksFailed(failures) => failures.foreach(_.exception.printStackTrace) }
|
}
|
||||||
|
c(create :: fileLength :: HNil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,44 +7,74 @@ import java.io.{File,IOException}
|
||||||
import CacheIO.{fromFile, toFile}
|
import CacheIO.{fromFile, toFile}
|
||||||
import sbinary.Format
|
import sbinary.Format
|
||||||
import scala.reflect.Manifest
|
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
|
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.*/
|
/** Cleans outputs and clears the cache.*/
|
||||||
def clean: Task[Unit]
|
def clean: Unit
|
||||||
/** Clears the cache. If also cleaning, 'clean' should be called first as it might require information from the cache.*/
|
|
||||||
def clear: Task[Unit]
|
|
||||||
}
|
}
|
||||||
class Timestamp(val cacheFile: File) extends Tracked
|
class Timestamp(val cacheFile: File, useStartTime: Boolean) extends Tracked
|
||||||
{
|
{
|
||||||
val clean = Clean(cacheFile)
|
def clean = delete(cacheFile)
|
||||||
def clear = Task.empty
|
/** Reads the previous timestamp, evaluates the provided function, and then updates the timestamp.*/
|
||||||
def apply[T](f: Long => Task[T]): Task[T] =
|
def apply[T](f: Long => T): T =
|
||||||
{
|
{
|
||||||
val getTimestamp = Task { readTimestamp }
|
val start = now()
|
||||||
getTimestamp bind f map { result =>
|
val result = f(readTimestamp)
|
||||||
FileUtilities.write(cacheFile, System.currentTimeMillis.toString)
|
write(cacheFile, (if(useStartTime) start else now()).toString)
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
private def now() = System.currentTimeMillis
|
||||||
def readTimestamp: Long =
|
def readTimestamp: Long =
|
||||||
try { FileUtilities.read(cacheFile).toLong }
|
try { read(cacheFile).toLong }
|
||||||
catch { case _: NumberFormatException | _: java.io.FileNotFoundException => 0 }
|
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 clean = delete(cacheFile)
|
||||||
def clear = Task.empty
|
def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): O2 =
|
||||||
def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): Task[O2] =
|
{
|
||||||
task map { value =>
|
val value = getValue
|
||||||
val cache =
|
val cache =
|
||||||
try { OpenResource.fileInputStream(cacheFile)(input.uptodate(value)) }
|
try { OpenResource.fileInputStream(cacheFile)(input.uptodate(value)) }
|
||||||
catch { case _: IOException => new ForceResult(input)(value) }
|
catch { case _: IOException => new ForceResult(input)(value) }
|
||||||
|
|
@ -61,22 +91,31 @@ object Difference
|
||||||
{
|
{
|
||||||
sealed class Constructor private[Difference](defineClean: Boolean, filesAreOutputs: Boolean) extends NotNull
|
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 = new Difference(files, style, cache, defineClean, filesAreOutputs)
|
||||||
def apply(files: Set[File], style: FilesInfo.Style, cache: File): Difference = apply(Task(files), style, cache)
|
|
||||||
}
|
}
|
||||||
|
/** 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)
|
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)
|
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
|
def clean =
|
||||||
val clear = Clean(cache)
|
{
|
||||||
|
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 cachedFilesInfo = fromFile(style.formats, style.empty)(cache)(style.manifest).files
|
||||||
private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file)
|
private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file)
|
||||||
|
|
||||||
def apply[T](f: ChangeReport[File] => Task[T]): Task[T] =
|
def apply[T](f: ChangeReport[File] => T): T =
|
||||||
filesTask bind { files =>
|
{
|
||||||
|
val files = getFiles
|
||||||
val lastFilesInfo = cachedFilesInfo
|
val lastFilesInfo = cachedFilesInfo
|
||||||
val lastFiles = raw(lastFilesInfo)
|
val lastFiles = raw(lastFilesInfo)
|
||||||
val currentFiles = files.map(_.getAbsoluteFile)
|
val currentFiles = files.map(_.getAbsoluteFile)
|
||||||
|
|
@ -91,29 +130,30 @@ class Difference(val filesTask: Task[Set[File]], val style: FilesInfo.Style, val
|
||||||
lazy val unmodified = checked -- modified
|
lazy val unmodified = checked -- modified
|
||||||
}
|
}
|
||||||
|
|
||||||
f(report) map { result =>
|
val result = f(report)
|
||||||
val info = if(filesAreOutputs) style(currentFiles) else currentFilesInfo
|
val info = if(filesAreOutputs) style(currentFiles) else currentFilesInfo
|
||||||
toFile(style.formats)(info)(cache)(style.manifest)
|
toFile(style.formats)(info)(cache)(style.manifest)
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
class DependencyTracked[T](val cacheDirectory: File, val translateProducts: Boolean, cleanT: T => Unit)(implicit format: Format[T], mf: Manifest[T]) extends Tracked
|
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 val trackFormat = new TrackingFormat[T](cacheDirectory, translateProducts)
|
||||||
private def cleanAll(fs: Set[T]) = fs.foreach(cleanT)
|
private def cleanAll(fs: Set[T]) = fs.foreach(cleanT)
|
||||||
|
|
||||||
val clean = Task(cleanAll(trackFormat.read.allProducts))
|
def clean =
|
||||||
val clear = Clean(cacheDirectory)
|
{
|
||||||
|
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
|
val tracker = trackFormat.read
|
||||||
f(tracker) map { result =>
|
val result = f(tracker)
|
||||||
trackFormat.write(tracker)
|
trackFormat.write(tracker)
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
object InvalidateFiles
|
object InvalidateFiles
|
||||||
{
|
{
|
||||||
|
|
@ -179,30 +219,29 @@ class InvalidateTransitive[T](cacheDirectory: File, translateProducts: Boolean,
|
||||||
this(cacheDirectory, translateProducts, (_: T) => ())
|
this(cacheDirectory, translateProducts, (_: T) => ())
|
||||||
|
|
||||||
private val tracked = new DependencyTracked(cacheDirectory, translateProducts, cleanT)
|
private val tracked = new DependencyTracked(cacheDirectory, translateProducts, cleanT)
|
||||||
def clean = tracked.clean
|
def 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] =
|
|
||||||
{
|
{
|
||||||
changesTask bind { changes =>
|
tracked.clean
|
||||||
|
tracked.clear
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply[R](getChanges: => ChangeReport[T])(f: (InvalidationReport[T], UpdateTracking[T]) => R): R =
|
||||||
|
{
|
||||||
|
val changes = getChanges
|
||||||
tracked { tracker =>
|
tracked { tracker =>
|
||||||
val report = InvalidateTransitive.andClean[T](tracker, _.foreach(cleanT), changes.modified)
|
val report = InvalidateTransitive.andClean[T](tracker, _.foreach(cleanT), changes.modified)
|
||||||
f(report, tracker)
|
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 val invalidation = InvalidateFiles(new File(cacheDirectory, "invalidation"))
|
||||||
private def onTracked(f: Tracked => Task[Unit]) = Seq(invalidation, changed).forkTasks(f).joinIgnore
|
private def onTracked(f: Tracked => Unit) = { f(invalidation); f(changed) }
|
||||||
val clear = onTracked(_.clear)
|
def clean = onTracked(_.clean)
|
||||||
val 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 =>
|
changed { sourceChanges =>
|
||||||
invalidation(sourceChanges) { (report, tracking) =>
|
invalidation(sourceChanges) { (report, tracking) =>
|
||||||
f(sourceChanges, report, tracking)
|
f(sourceChanges, report, tracking)
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ private class SameAPI(a: Source, b: Source, includePrivate: Boolean)
|
||||||
import SameAPI._
|
import SameAPI._
|
||||||
/** de Bruijn levels for type parameters in source `a`*/
|
/** de Bruijn levels for type parameters in source `a`*/
|
||||||
private lazy val tagsA = TagTypeVariables(a)
|
private lazy val tagsA = TagTypeVariables(a)
|
||||||
/** de Bruijn levels for type parameters in source `a`*/
|
/** de Bruijn levels for type parameters in source `b`*/
|
||||||
private lazy val tagsB = TagTypeVariables(b)
|
private lazy val tagsB = TagTypeVariables(b)
|
||||||
|
|
||||||
def debug(flag: Boolean, msg: => String): Boolean =
|
def debug(flag: Boolean, msg: => String): Boolean =
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class ConfigurationParser extends NotNull
|
||||||
val (properties, m6) = processSection(m5, "app-properties", getAppProperties)
|
val (properties, m6) = processSection(m5, "app-properties", getAppProperties)
|
||||||
val (cacheDir, m7) = processSection(m6, "ivy", getIvy)
|
val (cacheDir, m7) = processSection(m6, "ivy", getIvy)
|
||||||
check(m7, "section")
|
check(m7, "section")
|
||||||
val classifiers = Classifiers("" :: scalaClassifiers, "" :: appClassifiers)
|
val classifiers = Classifiers("" :: scalaClassifiers, "" :: appClassifiers) // the added "" ensures that the main jars are retrieved
|
||||||
new LaunchConfiguration(scalaVersion, IvyOptions(cacheDir, classifiers, repositories), app, boot, logging, properties)
|
new LaunchConfiguration(scalaVersion, IvyOptions(cacheDir, classifiers, repositories), app, boot, logging, properties)
|
||||||
}
|
}
|
||||||
def getScala(m: LabelMap) =
|
def getScala(m: LabelMap) =
|
||||||
|
|
@ -43,7 +43,7 @@ class ConfigurationParser extends NotNull
|
||||||
val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version")
|
val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version")
|
||||||
val (scalaClassifiers, m2) = ids(m1, "classifiers", Nil)
|
val (scalaClassifiers, m2) = ids(m1, "classifiers", Nil)
|
||||||
check(m2, "label")
|
check(m2, "label")
|
||||||
(scalaVersion, scalaClassifiers) // the added "" ensures that the main jars are retrieved
|
(scalaVersion, scalaClassifiers)
|
||||||
}
|
}
|
||||||
def getVersion(m: LabelMap, label: String, defaultName: String): (Version, LabelMap) = process(m, "version", processVersion(label, defaultName))
|
def getVersion(m: LabelMap, label: String, defaultName: String): (Version, LabelMap) = process(m, "version", processVersion(label, defaultName))
|
||||||
def processVersion(label: String, defaultName: String)(value: Option[String]): Version =
|
def processVersion(label: String, defaultName: String)(value: Option[String]): Version =
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
||||||
val classpathSub = baseProject(utilPath / "classpath", "Classpath")
|
val classpathSub = baseProject(utilPath / "classpath", "Classpath")
|
||||||
|
|
||||||
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub, launchInterfaceSub)
|
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub, launchInterfaceSub)
|
||||||
val logSub = baseProject(utilPath / "log", "Logging", interfaceSub)
|
val logSub = project(utilPath / "log", "Logging", new LogProject(_), interfaceSub)
|
||||||
val datatypeSub = baseProject("util" /"datatype", "Datatype Generator", ioSub)
|
val datatypeSub = baseProject("util" /"datatype", "Datatype Generator", ioSub)
|
||||||
|
|
||||||
val testSub = project("scripted", "Test", new TestProject(_), ioSub)
|
val testSub = project("scripted", "Test", new TestProject(_), ioSub)
|
||||||
|
|
@ -26,17 +26,19 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
||||||
val compileInterfaceSub = project(compilePath / "interface", "Compiler Interface", new CompilerInterfaceProject(_), interfaceSub)
|
val compileInterfaceSub = project(compilePath / "interface", "Compiler Interface", new CompilerInterfaceProject(_), interfaceSub)
|
||||||
|
|
||||||
val taskSub = project(tasksPath, "Tasks", new TaskProject(_), controlSub, collectionSub)
|
val taskSub = project(tasksPath, "Tasks", new TaskProject(_), controlSub, collectionSub)
|
||||||
val cacheSub = project(cachePath, "Cache", new CacheProject(_), taskSub, ioSub)
|
val cacheSub = project(cachePath, "Cache", new CacheProject(_), ioSub, collectionSub)
|
||||||
val trackingSub = baseProject(cachePath / "tracking", "Tracking", cacheSub)
|
val trackingSub = baseProject(cachePath / "tracking", "Tracking", cacheSub)
|
||||||
val compilerSub = project(compilePath, "Compile", new CompileProject(_),
|
val compilerSub = project(compilePath, "Compile", new CompileProject(_),
|
||||||
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
|
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
|
||||||
val stdTaskSub = project(tasksPath / "standard", "Standard Tasks", new StandardTaskProject(_), trackingSub, compilerSub, apiSub)
|
val stdTaskSub = project(tasksPath / "standard", "Standard Tasks", new StandardTaskProject(_), trackingSub, taskSub, compilerSub, apiSub)
|
||||||
|
|
||||||
val altCompilerSub = baseProject("main", "Alternate Compiler Test", stdTaskSub, logSub)
|
val altCompilerSub = baseProject("main", "Alternate Compiler Test", stdTaskSub, logSub)
|
||||||
|
|
||||||
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {}, compilerSub, launchInterfaceSub)
|
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {}, compilerSub, launchInterfaceSub)
|
||||||
val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub)
|
val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub)
|
||||||
|
|
||||||
|
lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal)
|
||||||
|
|
||||||
def baseProject(path: Path, name: String, deps: Project*) = project(path, name, new Base(_), deps : _*)
|
def baseProject(path: Path, name: String, deps: Project*) = project(path, name, new Base(_), deps : _*)
|
||||||
|
|
||||||
/* Multi-subproject paths */
|
/* Multi-subproject paths */
|
||||||
|
|
@ -90,6 +92,10 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
||||||
{
|
{
|
||||||
override def testClasspath = super.testClasspath +++ compilerSub.testClasspath --- compilerInterfaceClasspath
|
override def testClasspath = super.testClasspath +++ compilerSub.testClasspath --- compilerInterfaceClasspath
|
||||||
}
|
}
|
||||||
|
class LogProject(info: ProjectInfo) extends Base(info)
|
||||||
|
{
|
||||||
|
val jline = jlineDep
|
||||||
|
}
|
||||||
|
|
||||||
class IOProject(info: ProjectInfo) extends Base(info) with TestDependencies
|
class IOProject(info: ProjectInfo) extends Base(info) with TestDependencies
|
||||||
class TaskProject(info: ProjectInfo) extends Base(info) with TestDependencies
|
class TaskProject(info: ProjectInfo) extends Base(info) with TestDependencies
|
||||||
|
|
|
||||||
|
|
@ -322,8 +322,6 @@ trait BasicManagedProject extends ManagedProject with ReflectiveManagedProject w
|
||||||
def deliverIvyModule = newIvyModule(deliverModuleSettings)
|
def deliverIvyModule = newIvyModule(deliverModuleSettings)
|
||||||
def publishModuleSettings = deliverModuleSettings
|
def publishModuleSettings = deliverModuleSettings
|
||||||
def publishIvyModule = newIvyModule(publishModuleSettings)
|
def publishIvyModule = newIvyModule(publishModuleSettings)
|
||||||
/** True if the 'provided' configuration should be included on the 'compile' classpath. The default value is true.*/
|
|
||||||
def includeProvidedWithCompile = true
|
|
||||||
/** True if the default implicit extensions should be used when determining classpaths. The default value is true. */
|
/** True if the default implicit extensions should be used when determining classpaths. The default value is true. */
|
||||||
def defaultConfigurationExtensions = true
|
def defaultConfigurationExtensions = true
|
||||||
/** If true, verify that explicit dependencies on Scala libraries use the same version as scala.version. */
|
/** If true, verify that explicit dependencies on Scala libraries use the same version as scala.version. */
|
||||||
|
|
@ -343,22 +341,15 @@ trait BasicManagedProject extends ManagedProject with ReflectiveManagedProject w
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Includes the Provided configuration on the Compile classpath, the Compile configuration on the Runtime classpath,
|
/** Includes the Compile configuration on the Runtime classpath, and Compile and Runtime on the Test classpath.
|
||||||
* and Compile and Runtime on the Test classpath. Including Provided can be disabled by setting
|
* Including Compile and Runtime can be disabled by setting defaultConfigurationExtensions to false.*/
|
||||||
* includeProvidedWithCompile to false. Including Compile and Runtime can be disabled by setting
|
|
||||||
* defaultConfigurationExtensions to false.*/
|
|
||||||
override def managedClasspath(config: Configuration) =
|
override def managedClasspath(config: Configuration) =
|
||||||
{
|
{
|
||||||
import Configurations.{Compile, CompilerPlugin, Default, Provided, Runtime, Test}
|
import Configurations.{Compile, Default, Runtime, Test}
|
||||||
val baseClasspath = configurationClasspath(config)
|
val baseClasspath = configurationClasspath(config)
|
||||||
config match
|
config match
|
||||||
{
|
{
|
||||||
case Compile =>
|
case Compile => baseClasspath +++ managedClasspath(Default)
|
||||||
val baseCompileClasspath = baseClasspath +++ managedClasspath(Default)
|
|
||||||
if(includeProvidedWithCompile)
|
|
||||||
baseCompileClasspath +++ managedClasspath(Provided)
|
|
||||||
else
|
|
||||||
baseCompileClasspath
|
|
||||||
case Runtime if defaultConfigurationExtensions => baseClasspath +++ managedClasspath(Compile)
|
case Runtime if defaultConfigurationExtensions => baseClasspath +++ managedClasspath(Compile)
|
||||||
case Test if defaultConfigurationExtensions => baseClasspath +++ managedClasspath(Runtime)
|
case Test if defaultConfigurationExtensions => baseClasspath +++ managedClasspath(Runtime)
|
||||||
case _ => baseClasspath
|
case _ => baseClasspath
|
||||||
|
|
|
||||||
|
|
@ -5,140 +5,8 @@ package sbt
|
||||||
|
|
||||||
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
|
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
|
||||||
|
|
||||||
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 level: Int) extends LogEvent
|
|
||||||
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
|
|
||||||
|
|
||||||
object ControlEvent extends Enumeration
|
trait Logger extends AbstractLogger with xsbt.CompileLogger with IvyLogger
|
||||||
{
|
|
||||||
val Start, Header, Finish = Value
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Logger extends xsbt.CompileLogger with IvyLogger
|
|
||||||
{
|
|
||||||
def getLevel: Level.Value
|
|
||||||
def setLevel(newLevel: Level.Value)
|
|
||||||
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)
|
|
||||||
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 => setTrace(setT.level)
|
|
||||||
case c: ControlEvent => control(c.event, c.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
import xsbti.F0
|
|
||||||
def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
|
|
||||||
def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
|
|
||||||
def info(msg: F0[String]): Unit = log(Level.Info, msg)
|
|
||||||
def error(msg: F0[String]): Unit = log(Level.Error, msg)
|
|
||||||
def trace(msg: F0[Throwable]) = trace(msg.apply)
|
|
||||||
def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Implements the level-setting methods of Logger.*/
|
|
||||||
abstract class BasicLogger extends Logger
|
|
||||||
{
|
|
||||||
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 setTrace(level: Int) { traceEnabledVar = level }
|
|
||||||
def getTrace = traceEnabledVar
|
|
||||||
}
|
|
||||||
|
|
||||||
final class SynchronizedLogger(delegate: Logger) extends Logger
|
|
||||||
{
|
|
||||||
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
|
|
||||||
def getLevel = { synchronized { delegate.getLevel } }
|
|
||||||
def setLevel(newLevel: Level.Value) { synchronized { delegate.setLevel(newLevel) } }
|
|
||||||
def setTrace(level: Int) { synchronized { delegate.setTrace(level) } }
|
|
||||||
def getTrace: Int = { synchronized { delegate.getTrace } }
|
|
||||||
|
|
||||||
def trace(t: => Throwable) { synchronized { delegate.trace(t) } }
|
|
||||||
def log(level: Level.Value, message: => String) { synchronized { delegate.log(level, message) } }
|
|
||||||
def success(message: => String) { synchronized { delegate.success(message) } }
|
|
||||||
def control(event: ControlEvent.Value, message: => String) { synchronized { delegate.control(event, message) } }
|
|
||||||
def logAll(events: Seq[LogEvent]) { synchronized { delegate.logAll(events) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
final class MultiLogger(delegates: List[Logger]) 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)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
* */
|
|
||||||
final class FilterLogger(delegate: Logger) 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A logger that can buffer the logging done on it by currently executing Thread and
|
/** A logger that can buffer the logging done on it by currently executing Thread and
|
||||||
* then can flush the buffer to the delegate logger provided in the constructor. Use
|
* then can flush the buffer to the delegate logger provided in the constructor. Use
|
||||||
|
|
@ -151,7 +19,7 @@ final class FilterLogger(delegate: Logger) extends BasicLogger
|
||||||
*
|
*
|
||||||
* This logger is thread-safe.
|
* This logger is thread-safe.
|
||||||
* */
|
* */
|
||||||
final class BufferedLogger(delegate: Logger) extends Logger
|
final class BufferedLogger(delegate: AbstractLogger) extends Logger
|
||||||
{
|
{
|
||||||
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
|
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
|
||||||
private[this] val buffers = wrap.Wrappers.weakMap[Thread, Buffer[LogEvent]]
|
private[this] val buffers = wrap.Wrappers.weakMap[Thread, Buffer[LogEvent]]
|
||||||
|
|
@ -244,9 +112,9 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
||||||
}
|
}
|
||||||
def control(event: ControlEvent.Value, message: => String): Unit =
|
def control(event: ControlEvent.Value, message: => String): Unit =
|
||||||
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
|
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)
|
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 =
|
||||||
synchronized
|
synchronized
|
||||||
{
|
{
|
||||||
if(condition)
|
if(condition)
|
||||||
|
|
@ -259,134 +127,3 @@ final class BufferedLogger(delegate: Logger) extends Logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ConsoleLogger
|
|
||||||
{
|
|
||||||
private val formatEnabled = ansiSupported && !formatExplicitlyDisabled
|
|
||||||
|
|
||||||
private[this] def formatExplicitlyDisabled = java.lang.Boolean.getBoolean("sbt.log.noformat")
|
|
||||||
private[this] def ansiSupported =
|
|
||||||
try { jline.Terminal.getTerminal.isANSISupported }
|
|
||||||
catch { case e: Exception => !isWindows }
|
|
||||||
|
|
||||||
private[this] def os = System.getProperty("os.name")
|
|
||||||
private[this] def isWindows = os.toLowerCase.indexOf("windows") >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A logger that logs to the console. On supported systems, the level labels are
|
|
||||||
* colored.
|
|
||||||
*
|
|
||||||
* This logger is not thread-safe.*/
|
|
||||||
class ConsoleLogger extends BasicLogger
|
|
||||||
{
|
|
||||||
override def ansiCodesSupported = ConsoleLogger.formatEnabled
|
|
||||||
def messageColor(level: Level.Value) = Console.RESET
|
|
||||||
def labelColor(level: Level.Value) =
|
|
||||||
level match
|
|
||||||
{
|
|
||||||
case Level.Error => Console.RED
|
|
||||||
case Level.Warn => Console.YELLOW
|
|
||||||
case _ => Console.RESET
|
|
||||||
}
|
|
||||||
def successLabelColor = Console.GREEN
|
|
||||||
def successMessageColor = Console.RESET
|
|
||||||
override def success(message: => String)
|
|
||||||
{
|
|
||||||
if(atLevel(Level.Info))
|
|
||||||
log(successLabelColor, Level.SuccessLabel, successMessageColor, message)
|
|
||||||
}
|
|
||||||
def trace(t: => Throwable): Unit =
|
|
||||||
System.out.synchronized
|
|
||||||
{
|
|
||||||
val traceLevel = getTrace
|
|
||||||
if(traceLevel >= 0)
|
|
||||||
System.out.synchronized { System.out.print(StackTrace.trimmed(t, traceLevel)) }
|
|
||||||
}
|
|
||||||
def log(level: Level.Value, message: => String)
|
|
||||||
{
|
|
||||||
if(atLevel(level))
|
|
||||||
log(labelColor(level), level.toString, messageColor(level), message)
|
|
||||||
}
|
|
||||||
private def setColor(color: String)
|
|
||||||
{
|
|
||||||
if(ansiCodesSupported)
|
|
||||||
System.out.synchronized { System.out.print(color) }
|
|
||||||
}
|
|
||||||
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =
|
|
||||||
System.out.synchronized
|
|
||||||
{
|
|
||||||
for(line <- message.split("""\n"""))
|
|
||||||
{
|
|
||||||
setColor(Console.RESET)
|
|
||||||
System.out.print('[')
|
|
||||||
setColor(labelColor)
|
|
||||||
System.out.print(label)
|
|
||||||
setColor(Console.RESET)
|
|
||||||
System.out.print("] ")
|
|
||||||
setColor(messageColor)
|
|
||||||
System.out.print(line)
|
|
||||||
setColor(Console.RESET)
|
|
||||||
System.out.println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def logAll(events: Seq[LogEvent]) = System.out.synchronized { events.foreach(log) }
|
|
||||||
def control(event: ControlEvent.Value, message: => String)
|
|
||||||
{ log(labelColor(Level.Info), Level.Info.toString, Console.BLUE, message) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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[sbt] def unapply(s: String) = apply(s)
|
|
||||||
}
|
|
||||||
/** 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.*/
|
|
||||||
final class LoggerWriter(delegate: Logger, level: Level.Value, nl: String) extends java.io.Writer
|
|
||||||
{
|
|
||||||
def this(delegate: Logger, level: Level.Value) = this(delegate, level, FileUtilities.Newline)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
@ -416,7 +416,7 @@ class xMain extends xsbti.AppMain
|
||||||
val ContinuousCompilePollDelaySeconds = 1
|
val ContinuousCompilePollDelaySeconds = 1
|
||||||
|
|
||||||
/** The list of logging levels.*/
|
/** The list of logging levels.*/
|
||||||
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.levels.map(_.toString)
|
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.values.map(_.toString)
|
||||||
/** The list of all interactive commands other than logging level.*/
|
/** The list of all interactive commands other than logging level.*/
|
||||||
private def basicCommands: Iterable[String] = TreeSet(ShowProjectsAction, ShowActions, ShowCurrent, HelpAction,
|
private def basicCommands: Iterable[String] = TreeSet(ShowProjectsAction, ShowActions, ShowCurrent, HelpAction,
|
||||||
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction, BuilderCommand) ++
|
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction, BuilderCommand) ++
|
||||||
|
|
@ -464,7 +464,7 @@ class xMain extends xsbti.AppMain
|
||||||
printCmd(RebootCommand, "Reloads sbt, picking up modifications to sbt.version or scala.version and recompiling modified project definitions.")
|
printCmd(RebootCommand, "Reloads sbt, picking up modifications to sbt.version or scala.version and recompiling modified project definitions.")
|
||||||
printCmd(HelpAction, "Displays this help message.")
|
printCmd(HelpAction, "Displays this help message.")
|
||||||
printCmd(ShowCurrent, "Shows the current project, Scala version, and logging level.")
|
printCmd(ShowCurrent, "Shows the current project, Scala version, and logging level.")
|
||||||
printCmd(Level.levels.mkString(", "), "Set logging for the current project to the specified level.")
|
printCmd(Level.values.mkString(", "), "Set logging for the current project to the specified level.")
|
||||||
printCmd(TraceCommand + " " + validTraceArguments, "Configures stack trace logging. " + traceExplanation)
|
printCmd(TraceCommand + " " + validTraceArguments, "Configures stack trace logging. " + traceExplanation)
|
||||||
printCmd(ProjectAction + " <project name>", "Sets the currently active project.")
|
printCmd(ProjectAction + " <project name>", "Sets the currently active project.")
|
||||||
printCmd(ShowProjectsAction, "Shows all available projects.")
|
printCmd(ShowProjectsAction, "Shows all available projects.")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import sbinary._
|
||||||
|
|
||||||
|
trait A
|
||||||
|
{
|
||||||
|
def format: Format[A]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import sbinary._
|
||||||
|
|
||||||
|
trait B
|
||||||
|
{
|
||||||
|
def format(a: A): Format[A]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import sbt._
|
||||||
|
|
||||||
|
class P(info: ProjectInfo) extends ParentProject(info)
|
||||||
|
{
|
||||||
|
val a = project("a", "A", new A(_))
|
||||||
|
val b = project("b", "B", new B(_), a)
|
||||||
|
|
||||||
|
def aLibrary = "org.scala-tools.sbinary" %% "sbinary" % "0.3" % "provided"
|
||||||
|
|
||||||
|
class A(info: ProjectInfo) extends DefaultProject(info)
|
||||||
|
{
|
||||||
|
val a = aLibrary
|
||||||
|
}
|
||||||
|
class B(info: ProjectInfo) extends DefaultWebProject(info)
|
||||||
|
{
|
||||||
|
override def libraryDependencies =
|
||||||
|
if("declare.lib".asFile.exists) Set(aLibrary) else Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
project.name=Multi Project Provided
|
||||||
|
project.version=1.0
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
> set build.scala.versions 2.7.7
|
||||||
|
$ copy-file changes/P.scala project/build/P.scala
|
||||||
|
$ copy-file changes/A.scala a/src/main/scala/A.scala
|
||||||
|
$ copy-file changes/B.scala b/src/main/scala/B.scala
|
||||||
|
> reload
|
||||||
|
|
||||||
|
> project A
|
||||||
|
-> compile
|
||||||
|
> update
|
||||||
|
> compile
|
||||||
|
|
||||||
|
> project B
|
||||||
|
-> compile
|
||||||
|
> update
|
||||||
|
-> compile
|
||||||
|
|
||||||
|
$ touch b/declare.lib
|
||||||
|
> reload
|
||||||
|
-> compile
|
||||||
|
> update
|
||||||
|
> compile
|
||||||
|
|
@ -93,7 +93,7 @@ object LogWriterTest extends Properties("Log Writer")
|
||||||
for(ls <- arbList[List[ToLog]].arbitrary; lv <- genLevel) yield
|
for(ls <- arbList[List[ToLog]].arbitrary; lv <- genLevel) yield
|
||||||
new Output(ls, lv)
|
new Output(ls, lv)
|
||||||
|
|
||||||
def levelsGen: Seq[Gen[Level.Value]] = Level.elements.toList.map(x => value(x))
|
def levelsGen: Seq[Gen[Level.Value]] = Level.values.toList.map(x => value(x))
|
||||||
|
|
||||||
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
|
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
|
||||||
def addNewline(l: ToLog): ToLog =
|
def addNewline(l: ToLog): ToLog =
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
// TODO: Incomplete needs to be parameterized with A[_] and have val node
|
import ErrorHandling.wideConvert
|
||||||
|
|
||||||
import Node._
|
|
||||||
import Types._
|
import Types._
|
||||||
import Execute._
|
import Execute._
|
||||||
|
|
||||||
|
|
@ -87,13 +85,21 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: A ~> No
|
||||||
readyInv( node )
|
readyInv( node )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.get(target) match {
|
||||||
|
case Some(result) => retire(node, result)
|
||||||
|
case None =>
|
||||||
state(node) = Calling
|
state(node) = Calling
|
||||||
addChecked(target)
|
addChecked(target)
|
||||||
addCaller(node, target)
|
addCaller(node, target)
|
||||||
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
assert( calling(node) )
|
if(done(target))
|
||||||
|
assert(done(node))
|
||||||
|
else {
|
||||||
|
assert(calling(node) )
|
||||||
assert( callers(target) contains node )
|
assert( callers(target) contains node )
|
||||||
|
}
|
||||||
readyInv( node )
|
readyInv( node )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,19 +206,17 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: A ~> No
|
||||||
def submit[T]( node: A[T] )(implicit strategy: Strategy)
|
def submit[T]( node: A[T] )(implicit strategy: Strategy)
|
||||||
{
|
{
|
||||||
val v = view(node)
|
val v = view(node)
|
||||||
val rs: v.Inputs#Map[Result] = v.inputs.map(results)
|
val rs: v.Mixed#Map[Result] = v.mixedIn.map(results)
|
||||||
val ud = v.unitDependencies.flatMap(incomplete)
|
val ud = v.uniformIn.map(results.apply[v.Uniform])
|
||||||
strategy.submit( node, () => work(node, v.work(rs, ud)) )
|
strategy.submit( node, () => work(node, v.work(rs, ud)) )
|
||||||
}
|
}
|
||||||
/** Evaluates the computation 'f' for 'node'.
|
/** Evaluates the computation 'f' for 'node'.
|
||||||
* This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy.*/
|
* This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy.*/
|
||||||
def work[T](node: A[T], f: => Either[A[T], T])(implicit strategy: Strategy): Completed =
|
def work[T](node: A[T], f: => Either[A[T], T])(implicit strategy: Strategy): Completed =
|
||||||
{
|
{
|
||||||
val result =
|
val result = wideConvert(f).left.map {
|
||||||
try { Right(f) }
|
case i: Incomplete => i
|
||||||
catch {
|
case e => Incomplete(Incomplete.Error, directCause = Some(e))
|
||||||
case i: Incomplete => Left(i)
|
|
||||||
case e => Left( Incomplete(Incomplete.Error, directCause = Some(e)) )
|
|
||||||
}
|
}
|
||||||
completed {
|
completed {
|
||||||
result match {
|
result match {
|
||||||
|
|
@ -229,7 +233,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: A ~> No
|
||||||
def addCaller[T](caller: A[T], target: A[T]): Unit = callers.getOrUpdate(target, IDSet.create[A[T]]) += caller
|
def addCaller[T](caller: A[T], target: A[T]): Unit = callers.getOrUpdate(target, IDSet.create[A[T]]) += caller
|
||||||
|
|
||||||
def dependencies(node: A[_]): Iterable[A[_]] = dependencies(view(node))
|
def dependencies(node: A[_]): Iterable[A[_]] = dependencies(view(node))
|
||||||
def dependencies(v: Node[A, _]): Iterable[A[_]] = v.unitDependencies ++ v.inputs.toList
|
def dependencies(v: Node[A, _]): Iterable[A[_]] = v.uniformIn ++ v.mixedIn.toList
|
||||||
|
|
||||||
// Contracts
|
// Contracts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,24 @@ final case class Incomplete(tpe: IValue = Error, message: Option[String] = None,
|
||||||
|
|
||||||
object Incomplete extends Enumeration {
|
object Incomplete extends Enumeration {
|
||||||
val Skipped, Error = Value
|
val Skipped, Error = Value
|
||||||
|
def show(i: Incomplete, traces: Boolean): String =
|
||||||
|
{
|
||||||
|
val exceptions = allExceptions(i)
|
||||||
|
val traces = exceptions.map(_.getStackTrace).mkString("\n")
|
||||||
|
val causeStr = if(i.causes.isEmpty) "" else (i.causes.length + " cause(s)")
|
||||||
|
"Incomplete (" + show(i.tpe) + ") " + i.message.getOrElse("") + causeStr + "\n" + traces
|
||||||
|
}
|
||||||
|
def allExceptions(i: Incomplete): Iterable[Throwable] =
|
||||||
|
{
|
||||||
|
val exceptions = IDSet.create[Throwable]
|
||||||
|
val visited = IDSet.create[Incomplete]
|
||||||
|
def visit(inc: Incomplete): Unit =
|
||||||
|
visited.process(inc)( () ) {
|
||||||
|
exceptions ++= inc.directCause.toList
|
||||||
|
inc.causes.foreach(visit)
|
||||||
|
}
|
||||||
|
visit(i)
|
||||||
|
exceptions.all
|
||||||
|
}
|
||||||
|
def show(tpe: Value) = tpe match { case Skipped=> "skipped"; case Error => "error" }
|
||||||
}
|
}
|
||||||
|
|
@ -3,33 +3,16 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import Node._
|
|
||||||
import Types._
|
import Types._
|
||||||
|
|
||||||
trait Node[A[_], T]
|
trait Node[A[_], T]
|
||||||
{
|
{
|
||||||
type Inputs <: MList[A]
|
type Mixed <: MList[A]
|
||||||
type Results = Inputs#Map[Result]
|
type MixedResults = Mixed#Map[Result]
|
||||||
|
type Uniform
|
||||||
|
|
||||||
val inputs: Inputs
|
val mixedIn: Mixed
|
||||||
def unitDependencies: Iterable[A[_]]
|
val uniformIn: Seq[A[Uniform]]
|
||||||
|
|
||||||
def work(results: Results, units: UnitResults[A]): Either[A[T], T]
|
def work(mixed: MixedResults, uniform: Seq[Result[Uniform]]): Either[A[T], T]
|
||||||
}
|
|
||||||
|
|
||||||
object Node
|
|
||||||
{
|
|
||||||
/*def pure[T](f: () => T): PureNode[T]= map[Id, T, MNil](MNil, Nil)((_,_) => f() )
|
|
||||||
|
|
||||||
def map[A[_], T, Inputs0 <: MList[A]](inputs0: Inputs0, deps0: Iterable[A[_]])(work0: (Inputs0#Map[Result], UnitResults[A]) => T):
|
|
||||||
Node[A,T] { type Inputs = Inputs0 } =
|
|
||||||
new Node[A,T] {
|
|
||||||
type Inputs = Inputs0
|
|
||||||
val inputs = inputs0
|
|
||||||
def unitDependencies = deps0
|
|
||||||
def work(results: Results, units: UnitResults[A]) = Right(work0(results, units))
|
|
||||||
}
|
|
||||||
|
|
||||||
type PureNode[T] = Node[Id, T] { type Inputs = MNil; type Results = MNil }*/
|
|
||||||
type UnitResults[A[_]] = Iterable[(A[_], Incomplete)]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
114
tasks/Test.scala
114
tasks/Test.scala
|
|
@ -1,114 +0,0 @@
|
||||||
/* sbt -- Simple Build Tool
|
|
||||||
* Copyright 2010 Mark Harrah
|
|
||||||
*/
|
|
||||||
package sbt
|
|
||||||
|
|
||||||
import Types._
|
|
||||||
import Node._
|
|
||||||
import Task._
|
|
||||||
import Execute._
|
|
||||||
|
|
||||||
sealed trait Task[+T]
|
|
||||||
sealed case class Pure[+T](eval: () => T) extends Task[T]
|
|
||||||
sealed case class Mapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => T) extends Task[T]
|
|
||||||
sealed case class MapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => T) extends Task[T]
|
|
||||||
sealed case class FlatMapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => Task[T]) extends Task[T]
|
|
||||||
sealed case class FlatMapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => Task[T]) extends Task[T]
|
|
||||||
|
|
||||||
object Task
|
|
||||||
{
|
|
||||||
implicit val taskToNode = new (Task ~> NodeT[Task]#Apply) {
|
|
||||||
def apply[T](t: Task[T]): Node[Task, T] = t match {
|
|
||||||
case Pure(eval) => toNode[T, MNil](MNil, _ => Right(eval()) )
|
|
||||||
case Mapped(in, f) => toNode[T, in.type](in, right ∙ f )
|
|
||||||
case MapAll(in, f) => toNode[T, in.type](in, right ∙ (f compose all) )
|
|
||||||
case FlatMapAll(in, f) => toNode[T, in.type](in, left ∙ (f compose all) )
|
|
||||||
case FlatMapped(in, f) => toNode[T, in.type](in, left ∙ f )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def toNode[T, In <: MList[Task]](in: In, f: In#Map[Result] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
|
|
||||||
type Inputs = In
|
|
||||||
val inputs = in
|
|
||||||
def unitDependencies = Nil
|
|
||||||
def work(results: Results, units: UnitResults[Task]) = f(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
def pure[T](name: String)(f: => T): Pure[T] = new Pure(f _) { override def toString = name }
|
|
||||||
def mapped[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result] => T): Mapped[T, In0] = new Mapped(in0, f0) { override def toString = name }
|
|
||||||
def flat[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result] => Task[T]): FlatMapped[T, In0] = new FlatMapped(in0, f0) { override def toString = name }
|
|
||||||
def mapAll[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result]#Raw => T): MapAll[T, In0] = new MapAll(in0, f0) { override def toString = name }
|
|
||||||
def flatAll[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result]#Raw => Task[T]): FlatMapAll[T, In0] = new FlatMapAll(in0, f0) { override def toString = name }
|
|
||||||
|
|
||||||
def all[In <: MList[Result]]: In => In#Raw = in =>
|
|
||||||
{
|
|
||||||
val incs = in.toList.collect { case Inc(i) => i }
|
|
||||||
if(incs.isEmpty) in.down(Result.tryValue) else throw Incomplete(causes = incs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
object Test
|
|
||||||
{
|
|
||||||
val a = pure("a")(3)
|
|
||||||
val b = pure[Boolean]("b")(error("test"))
|
|
||||||
val b2 = pure("b2")(true)
|
|
||||||
val c = pure("x")("asdf")
|
|
||||||
val i3 = a :^: b :^: c :^: MNil
|
|
||||||
val i32 = a :^: b2 :^: c :^: MNil
|
|
||||||
|
|
||||||
val fh= (_: Int :+: Boolean :+: String :+: HNil) match
|
|
||||||
{ case aa :+: bb :+: cc :+: HNil => aa + " " + bb + " " + cc }
|
|
||||||
val h1 = mapAll("h1")(i3)(fh)
|
|
||||||
val h2 = mapAll("h2")(i32)(fh)
|
|
||||||
|
|
||||||
val f: i3.Map[Result] => Any = {
|
|
||||||
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => aa + " " + bb + " " + cc
|
|
||||||
case x =>
|
|
||||||
val cs = x.toList.collect { case Inc(x) => x } // workaround for double definition bug
|
|
||||||
throw Incomplete(causes = cs)
|
|
||||||
}
|
|
||||||
val d2 = mapped("d2")(i32)(f)
|
|
||||||
val f2: i3.Map[Result] => Task[Any] = {
|
|
||||||
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
|
|
||||||
case x => d3
|
|
||||||
}
|
|
||||||
lazy val d = flat("d")(i3)(f2)
|
|
||||||
val f3: i3.Map[Result] => Task[Any] = {
|
|
||||||
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
|
|
||||||
case x => d2
|
|
||||||
}
|
|
||||||
lazy val d3= flat("d3")(i3)(f3)
|
|
||||||
|
|
||||||
def d4(i: Int): Task[Int] = flat("d4")(MNil){ _ => val x = math.random; if(x < 0.01) pure(x.toString)(i); else d4(i+1) }
|
|
||||||
|
|
||||||
lazy val pureEval =
|
|
||||||
new (Pure ~> Result) {
|
|
||||||
def apply[T](p: Pure[T]): Result[T] =
|
|
||||||
try { Value(p.eval()) }
|
|
||||||
catch { case e: Exception => throw Incomplete(Incomplete.Error, directCause = Some(e)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy val resultA = d.f( d.in.map(pureEval) )
|
|
||||||
|
|
||||||
def execute[T](root: Task[T]) = {
|
|
||||||
val (service, shutdown) = CompletionService[Task[_], Completed](2)
|
|
||||||
implicit val wrapped = CompletionService.manage(service)(x => println("Starting: " + x), x => println("Finished: " + x) )
|
|
||||||
|
|
||||||
val x = new Execute[Task](true)(taskToNode)
|
|
||||||
try { x.run(root) } finally { shutdown(); println(x.dump) }
|
|
||||||
}
|
|
||||||
|
|
||||||
def go()
|
|
||||||
{
|
|
||||||
def run[T](root: Task[T]) =
|
|
||||||
println("Result : " + execute(root))
|
|
||||||
|
|
||||||
run(a)
|
|
||||||
run(b)
|
|
||||||
run(b2)
|
|
||||||
run(c)
|
|
||||||
run(d)
|
|
||||||
run(d2)
|
|
||||||
run( d4(0) )
|
|
||||||
run(h1)
|
|
||||||
run(h2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import Types._
|
||||||
|
import Task._
|
||||||
|
import Execute._
|
||||||
|
|
||||||
|
sealed trait Task[+T]
|
||||||
|
sealed case class Pure[+T](eval: () => T) extends Task[T]
|
||||||
|
final case class Mapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => T) extends Task[T]
|
||||||
|
final case class MapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => T) extends Task[T]
|
||||||
|
final case class FlatMapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => Task[T]) extends Task[T]
|
||||||
|
final case class MapFailure[+T, In <: MList[Task]](in: In, f: Seq[Incomplete] => T) extends Task[T]
|
||||||
|
final case class FlatMapFailure[+T, In <: MList[Task]](in: In, f: Seq[Incomplete] => Task[T]) extends Task[T]
|
||||||
|
final case class FlatMapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => Task[T]) extends Task[T]
|
||||||
|
final case class DependsOn[+T](in: Task[T], deps: Seq[Task[_]]) extends Task[T]
|
||||||
|
final case class Join[+T, U](in: Seq[Task[U]], f: Seq[U] => Either[Task[T], T]) extends Task[T] { type Uniform = U }
|
||||||
|
|
||||||
|
trait MultiInTask[M <: MList[Task]]
|
||||||
|
{
|
||||||
|
def flatMap[T](f: M#Map[Result]#Raw => Task[T]): Task[T]
|
||||||
|
def flatMapR[T](f: M#Map[Result] => Task[T]): Task[T]
|
||||||
|
def mapH[T](f: M#Map[Result]#Raw => T): Task[T]
|
||||||
|
def mapR[T](f: M#Map[Result] => T): Task[T]
|
||||||
|
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T]
|
||||||
|
def mapFailure[T](f: Seq[Incomplete] => T): Task[T]
|
||||||
|
}
|
||||||
|
trait SingleInTask[S]
|
||||||
|
{
|
||||||
|
def flatMapR[T](f: Result[S] => Task[T]): Task[T]
|
||||||
|
def flatMap[T](f: S => Task[T]): Task[T]
|
||||||
|
def map[T](f: S => T): Task[T]
|
||||||
|
def mapR[T](f: Result[S] => T): Task[T]
|
||||||
|
def flatFailure[T](f: Incomplete => Task[T]): Task[T]
|
||||||
|
def mapFailure[T](f: Incomplete => T): Task[T]
|
||||||
|
def dependsOn(tasks: Task[_]*): Task[S]
|
||||||
|
}
|
||||||
|
trait ForkTask[S, CC[_]]
|
||||||
|
{
|
||||||
|
def fork[T](f: S => T): CC[Task[T]]
|
||||||
|
}
|
||||||
|
trait JoinTask[S, CC[_]]
|
||||||
|
{
|
||||||
|
def join: Task[CC[S]]
|
||||||
|
def reduce(f: (S,S) => S): Task[S]
|
||||||
|
}
|
||||||
|
object Task
|
||||||
|
{
|
||||||
|
def pure[T](f: => T): Task[T] = toPure(f _)
|
||||||
|
def pure[T](name: String, f: => T): Task[T] = new Pure(f _) { override def toString = name }
|
||||||
|
implicit def toPure[T](f: () => T): Task[T] = new Pure(f)
|
||||||
|
|
||||||
|
implicit def toTasks[S](in: Seq[S]): Seq[Task[S]] = in.map(s => pure(s))
|
||||||
|
implicit def toTasks[S](in: Seq[() => S]): Seq[Task[S]] = in.map(toPure)
|
||||||
|
implicit def iterableTask[S](in: Seq[S]): ForkTask[S, Seq] = new ForkTask[S, Seq] {
|
||||||
|
def fork[T](f: S => T): Seq[Task[T]] = in.map(x => pure(x) map f)
|
||||||
|
}
|
||||||
|
implicit def joinTasks[S](in: Seq[S]): JoinTask[S, Seq] = joinTasks(toTasks(in))
|
||||||
|
implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||||
|
def join: Task[Seq[S]] = new Join(in, (s: Seq[S]) => Right(s) )
|
||||||
|
//def join[T](f: Iterable[S] => T): Task[Iterable[T]] = new MapAll( MList.fromTCList[Task](in), ml => f(ml.toList))
|
||||||
|
//def joinR[T](f: Iterable[Result[S]] => T): Task[Iterable[Result[T]]] = new Mapped( MList.fromTCList[Task](in), ml => f(ml.toList))
|
||||||
|
def reduce(f: (S,S) => S): Task[S] = Task.reduce(in.toIndexedSeq, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
implicit def multInputTask[M <: MList[Task]](ml: M): MultiInTask[M] = new MultiInTask[M] {
|
||||||
|
def flatMap[T](f: M#Map[Result]#Raw => Task[T]): Task[T] = new FlatMapAll(ml, f)
|
||||||
|
def flatMapR[T](f: M#Map[Result] => Task[T]): Task[T] = new FlatMapped(ml, f)
|
||||||
|
def mapH[T](f: M#Map[Result]#Raw => T): Task[T] = new MapAll(ml, f)
|
||||||
|
def mapR[T](f: M#Map[Result] => T): Task[T] = new Mapped(ml, f)
|
||||||
|
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T] = new FlatMapFailure(ml, f)
|
||||||
|
def mapFailure[T](f: Seq[Incomplete] => T): Task[T] = new MapFailure(ml, f)
|
||||||
|
}
|
||||||
|
implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] = new SingleInTask[S] {
|
||||||
|
private val ml = in :^: MNil
|
||||||
|
private def headM = (_: ml.Map[Result]).head
|
||||||
|
private def headH = (_: S :+: HNil).head
|
||||||
|
private def headS = (_: Seq[Incomplete]).head
|
||||||
|
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, ml.type](ml, f ∙ headM)
|
||||||
|
def flatMap[T](f: S => Task[T]): Task[T] = new FlatMapAll[T, ml.type](ml, f ∙ headH)
|
||||||
|
def map[T](f: S => T): Task[T] = new MapAll[T, ml.type](ml, f ∙ headH)
|
||||||
|
def mapR[T](f: Result[S] => T): Task[T] = new Mapped[T, ml.type](ml, f ∙ headM)
|
||||||
|
def flatFailure[T](f: Incomplete => Task[T]): Task[T] = new FlatMapFailure(ml, f ∙ headS)
|
||||||
|
def mapFailure[T](f: Incomplete => T): Task[T] = new MapFailure(ml, f ∙ headS)
|
||||||
|
def dependsOn(tasks: Task[_]*): Task[S] = new DependsOn(in, tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val taskToNode = new (Task ~> NodeT[Task]#Apply) {
|
||||||
|
def apply[T](t: Task[T]): Node[Task, T] = t match {
|
||||||
|
case Pure(eval) => toNode[T, MNil](MNil, _ => Right(eval()) )
|
||||||
|
case Mapped(in, f) => toNode[T, in.type](in, right ∙ f )
|
||||||
|
case MapAll(in, f) => toNode[T, in.type](in, right ∙ (f compose allM) )
|
||||||
|
case MapFailure(in, f) => toNode[T, in.type](in, right ∙ (f compose failuresM))
|
||||||
|
case FlatMapped(in, f) => toNode[T, in.type](in, left ∙ f )
|
||||||
|
case FlatMapAll(in, f) => toNode[T, in.type](in, left ∙ (f compose allM) )
|
||||||
|
case FlatMapFailure(in, f) => toNode[T, in.type](in, left ∙ (f compose failuresM))
|
||||||
|
case DependsOn(in, tasks) => join[T, Any](tasks, (_: Seq[Result[_]]) => Left(in))
|
||||||
|
case j@ Join(in, f) => join[T, j.Uniform](in, f compose all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def join[T, D](tasks: Seq[Task[D]], f: Seq[Result[D]] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
|
||||||
|
type Mixed = MNil
|
||||||
|
val mixedIn = MNil
|
||||||
|
type Uniform = D
|
||||||
|
val uniformIn = tasks
|
||||||
|
def work(mixed: MNil, uniform: Seq[Result[Uniform]]) = {
|
||||||
|
val inc = failures(uniform)
|
||||||
|
if(inc.isEmpty) f(uniform) else throw Incomplete(causes = inc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def toNode[T, In <: MList[Task]](in: In, f: In#Map[Result] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
|
||||||
|
type Mixed = In
|
||||||
|
val mixedIn = in
|
||||||
|
type Uniform = Nothing
|
||||||
|
val uniformIn = Nil
|
||||||
|
def work(results: Mixed#Map[Result], units: Seq[Result[Uniform]]) = f(results)
|
||||||
|
}
|
||||||
|
def allM[In <: MList[Result]]: In => In#Raw = in =>
|
||||||
|
{
|
||||||
|
val incs = failuresM(in)
|
||||||
|
if(incs.isEmpty) in.down(Result.tryValue) else throw Incomplete(causes = incs)
|
||||||
|
}
|
||||||
|
def all[D]: Seq[Result[D]] => Seq[D] = in =>
|
||||||
|
{
|
||||||
|
val incs = failures(in)
|
||||||
|
if(incs.isEmpty) in.map(Result.tryValue.apply[D]) else throw Incomplete(causes = incs)
|
||||||
|
}
|
||||||
|
def failuresM[In <: MList[Result]]: In => Seq[Incomplete] = x => failures[Any](x.toList)
|
||||||
|
def failures[A]: Seq[Result[A]] => Seq[Incomplete] = _.collect { case Inc(i) => i }
|
||||||
|
|
||||||
|
def run[T](root: Task[T], checkCycles: Boolean, maxWorkers: Int): Result[T] =
|
||||||
|
{
|
||||||
|
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
||||||
|
|
||||||
|
val x = new Execute[Task](checkCycles)(taskToNode)
|
||||||
|
try { x.run(root)(service) } finally { shutdown() }
|
||||||
|
}
|
||||||
|
def tryRun[T](root: Task[T], checkCycles: Boolean, maxWorkers: Int): T =
|
||||||
|
run(root, checkCycles, maxWorkers) match {
|
||||||
|
case Value(v) => v
|
||||||
|
case Inc(i) => throw i
|
||||||
|
}
|
||||||
|
|
||||||
|
def reduce[S](i: IndexedSeq[Task[S]], f: (S, S) => S): Task[S] =
|
||||||
|
i match
|
||||||
|
{
|
||||||
|
case Seq() => error("Cannot reduce empty sequence")
|
||||||
|
case Seq(x) => x
|
||||||
|
case Seq(x, y) => reducePair(x, y, f)
|
||||||
|
case z =>
|
||||||
|
val (a, b) = i.splitAt(i.size / 2)
|
||||||
|
reducePair( reduce(a, f), reduce(b, f), f )
|
||||||
|
}
|
||||||
|
def reducePair[S](a: Task[S], b: Task[S], f: (S, S) => S): Task[S] =
|
||||||
|
(a :^: b :^: MNil) mapH { case x :+: y :+: HNil => f(x,y) }
|
||||||
|
}
|
||||||
|
|
@ -1,44 +1,49 @@
|
||||||
import xsbt._
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2009, 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import TaskGen._
|
import TaskGen._
|
||||||
|
import Task._
|
||||||
|
|
||||||
object TaskRunnerSpec extends Properties("TaskRunner")
|
object ExecuteSpec extends Properties("Execute")
|
||||||
{
|
{
|
||||||
val iGen = Arbitrary.arbInt.arbitrary
|
val iGen = Arbitrary.arbInt.arbitrary
|
||||||
property("evaluates simple task") = forAll(iGen, MaxWorkersGen) { (i: Int, workers: Int) =>
|
property("evaluates simple task") = forAll(iGen, MaxWorkersGen) { (i: Int, workers: Int) =>
|
||||||
("Workers: " + workers) |:
|
("Workers: " + workers) |:
|
||||||
checkResult(TaskRunner(Task(i), workers), i)
|
checkResult(tryRun(pure(i), false, workers), i)
|
||||||
}
|
}
|
||||||
property("evaluates simple static graph") = forAll(iGen, MaxWorkersGen) { (i: Int, workers: Int) =>
|
// no direct dependencies currently
|
||||||
|
/*property("evaluates simple static graph") = forAll(iGen, MaxWorkersGen) { (i: Int, workers: Int) =>
|
||||||
("Workers: " + workers) |:
|
("Workers: " + workers) |:
|
||||||
{
|
{
|
||||||
def result = TaskRunner(Task(i) dependsOn(Task(false),Task("a")), workers)
|
def result = tryRun(Task(i) dependsOn(pure(false),pure("a")), false, workers)
|
||||||
checkResult(result, i)
|
checkResult(result, i)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
property("evaluates simple mapped task") = forAll(iGen, MaxTasksGen, MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
property("evaluates simple mapped task") = forAll(iGen, MaxTasksGen, MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
||||||
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
||||||
{
|
{
|
||||||
def result = TaskRunner(Task(i).map(_*times), workers)
|
def result = tryRun(pure(i).map(_*times), false, workers)
|
||||||
checkResult(result, i*times)
|
checkResult(result, i*times)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property("evaluates chained mapped task") = forAllNoShrink(iGen, Gen.choose(0, 1000), MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
property("evaluates chained mapped task") = forAllNoShrink(iGen, MaxTasksGen, MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
||||||
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
||||||
{
|
{
|
||||||
val initial = Task(0) map(identity[Int])
|
val initial = pure(0) map(identity[Int])
|
||||||
def task = ( initial /: (0 until times) )( (t,ignore) => t.map(_ + i))
|
def task = ( initial /: (0 until times) )( (t,ignore) => t.map(_ + i))
|
||||||
checkResult(TaskRunner(task, workers), i*times)
|
checkResult(tryRun(task, false, workers), i*times)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property("evaluates simple bind") = forAll(iGen, MaxTasksGen, MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
property("evaluates simple bind") = forAll(iGen, MaxTasksGen, MaxWorkersGen) { (i: Int, times: Int, workers: Int) =>
|
||||||
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
("Workers: " + workers) |: ("Value: " + i) |: ("Times: " + times) |:
|
||||||
{
|
{
|
||||||
def result = TaskRunner(Task(i).bind(x => Task(x*times)), workers)
|
def result = tryRun(pure(i).flatMap(x => pure(x*times)), false, workers)
|
||||||
checkResult(result, i*times)
|
checkResult(result, i*times)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package xsbt
|
|
||||||
|
|
||||||
import HLists._
|
|
||||||
import Task._
|
|
||||||
|
|
||||||
/** This test just verifies that the HList support compiles.*/
|
|
||||||
object TListCompileTest
|
|
||||||
{
|
|
||||||
val n = Task(1)
|
|
||||||
val s = Task("3")
|
|
||||||
val t = Task(true)
|
|
||||||
val mapped = (n :: s :: t :: TNil) map { case n :: s :: t :: HNil => n }
|
|
||||||
val bound = (n :: s :: t :: TNil) bind { case n :: s :: t :: HNil => (Task(n*4) :: Task("Hi " + t) :: TNil).join }
|
|
||||||
|
|
||||||
val plusOne = mapped map { _ + 1 }
|
|
||||||
val forkN = plusOne bind { count => (0 until count) fork { i => Task(println(i)) } join }
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2009 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Gen.choose
|
import Gen.choose
|
||||||
|
|
||||||
object TaskGen
|
object TaskGen
|
||||||
{
|
{
|
||||||
// upper bounds to make the tests finish in reasonable time
|
// upper bounds to make the tests finish in reasonable time
|
||||||
val MaxTasks = 10000
|
val MaxTasks = 100
|
||||||
val MaxWorkers = 257
|
val MaxWorkers = 29
|
||||||
val MaxJoin = 100
|
val MaxJoin = 100
|
||||||
|
|
||||||
val MaxTasksGen = choose(0, MaxTasks)
|
val MaxTasksGen = choose(0, MaxTasks)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import xsbt._
|
package sbt
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import TaskGen._
|
import TaskGen._
|
||||||
|
import Task._
|
||||||
|
|
||||||
object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
||||||
{
|
{
|
||||||
|
|
@ -10,32 +11,33 @@ object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
||||||
property("Allows references to completed tasks") = forAllNoShrink(MaxTasksGen, MaxWorkersGen) { allowedReference _ }
|
property("Allows references to completed tasks") = forAllNoShrink(MaxTasksGen, MaxWorkersGen) { allowedReference _ }
|
||||||
final def allowedReference(intermediate: Int, workers: Int) =
|
final def allowedReference(intermediate: Int, workers: Int) =
|
||||||
{
|
{
|
||||||
val top = Task(intermediate) named("top")
|
val top = pure("top", intermediate)
|
||||||
def iterate(task: Task[Int]): Task[Int] =
|
def iterate(task: Task[Int]): Task[Int] =
|
||||||
task bind { t =>
|
task flatMap { t =>
|
||||||
if(t <= 0)
|
if(t <= 0)
|
||||||
top
|
top
|
||||||
else
|
else
|
||||||
iterate(Task(t-1) named (t-1).toString)
|
iterate(pure((t-1).toString, t-1) )
|
||||||
}
|
}
|
||||||
try { checkResult(TaskRunner(iterate(top), workers), intermediate) }
|
try { checkResult(tryRun(iterate(top), true, workers), intermediate) }
|
||||||
catch { case e: CircularDependency => ("Unexpected exception: " + e) |: false }
|
catch { case i: Incomplete if cyclic(i) => ("Unexpected cyclic exception: " + i) |: false }
|
||||||
}
|
}
|
||||||
final def checkCircularReferences(intermediate: Int, workers: Int) =
|
final def checkCircularReferences(intermediate: Int, workers: Int) =
|
||||||
{
|
{
|
||||||
lazy val top = iterate(Task(intermediate) named"bottom", intermediate)
|
lazy val top = iterate(pure("bottom", intermediate), intermediate)
|
||||||
def iterate(task: Task[Int], i: Int): Task[Int] =
|
def iterate(task: Task[Int], i: Int): Task[Int] =
|
||||||
{
|
{
|
||||||
lazy val it: Task[Int] =
|
lazy val it: Task[Int] =
|
||||||
task bind { t =>
|
task flatMap { t =>
|
||||||
if(t <= 0)
|
if(t <= 0)
|
||||||
top
|
top
|
||||||
else
|
else
|
||||||
iterate(Task(t-1) named (t-1).toString, i-1)
|
iterate(pure((t-1).toString, t-1), i-1)
|
||||||
} named("it_" + i)
|
}
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
try { TaskRunner(top, workers); false }
|
try { tryRun(top, true, workers); false }
|
||||||
catch { case TasksFailed(failures) => failures.exists(_.exception.isInstanceOf[CircularDependency]) }
|
catch { case i: Incomplete => cyclic(i) }
|
||||||
}
|
}
|
||||||
|
def cyclic(i: Incomplete) = Incomplete.allExceptions(i).exists(_.isInstanceOf[Execute[Task]#CyclicException[_]])
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
import xsbt._
|
import sbt._
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import Task._
|
import Task._
|
||||||
import TaskGen._
|
import TaskGen._
|
||||||
import Math.abs
|
import math.abs
|
||||||
|
|
||||||
object TaskRunnerForkTest extends Properties("TaskRunner Fork")
|
object TaskRunnerForkTest extends Properties("TaskRunner Fork")
|
||||||
{
|
{
|
||||||
property("fork m tasks and wait for all to complete") = forAll(MaxTasksGen, MaxWorkersGen) { (m: Int, workers: Int) =>
|
property("fork m tasks and wait for all to complete") = forAll(MaxTasksGen, MaxWorkersGen) { (m: Int, workers: Int) =>
|
||||||
val values = (0 until m).toList
|
val values = (0 until m).toList
|
||||||
checkResult(TaskRunner(values.fork(f => () ).join.map(_.toList),workers), values)
|
checkResult(tryRun(values.fork(f => () ).join.map(_.toList),false, workers), values)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
property("Fork and reduce 2") = forAll(MaxTasksGen, MaxWorkersGen) { (m: Int, workers: Int) =>
|
property("Fork and reduce 2") = forAll(MaxTasksGen, MaxWorkersGen) { (m: Int, workers: Int) =>
|
||||||
(m > 1) ==> {
|
(m > 1) ==> {
|
||||||
val task = (0 to m) fork {_ * 10} reduce{_ + _}
|
val task = (0 to m) fork {_ * 10} reduce{_ + _}
|
||||||
checkResult(TaskRunner(task, workers), 5*(m+1)*m)
|
checkResult(tryRun(task, false, workers), 5*(m+1)*m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property("Double join") = forAll(MaxJoinGen, MaxJoinGen, MaxWorkersGen) { (a: Int, b: Int, workers: Int) =>
|
property("Double join") = forAll(MaxJoinGen, MaxJoinGen, MaxWorkersGen) { (a: Int, b: Int, workers: Int) =>
|
||||||
|
|
@ -25,13 +25,13 @@ object TaskRunnerForkTest extends Properties("TaskRunner Fork")
|
||||||
}
|
}
|
||||||
def runDoubleJoin(a: Int, b: Int, workers: Int)
|
def runDoubleJoin(a: Int, b: Int, workers: Int)
|
||||||
{
|
{
|
||||||
def inner(i: Int) = List.range(0, b).map(j => Task(j) named(j.toString)).join.named("Join " + i)
|
def inner(i: Int) = List.range(0, b).map(j => pure(j.toString, j)).join
|
||||||
TaskRunner( List.range(0,a).map(inner).join.named("Outermost join"), workers)
|
tryRun( List.range(0,a).map(inner).join, false, workers)
|
||||||
}
|
}
|
||||||
property("fork and reduce") = forAll(TaskListGen, MaxWorkersGen) { (m: List[Int], workers: Int) =>
|
property("fork and reduce") = forAll(TaskListGen, MaxWorkersGen) { (m: List[Int], workers: Int) =>
|
||||||
(!m.isEmpty) ==> {
|
(!m.isEmpty) ==> {
|
||||||
val expected = m.reduceLeft(_+_)
|
val expected = m.reduceLeft(_+_)
|
||||||
checkResult(TaskRunner( m.reduce(_ + _), workers), expected)
|
checkResult(tryRun( m.reduce(_ + _), false, workers), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import Types._
|
||||||
|
import Task._
|
||||||
|
import Execute._
|
||||||
|
|
||||||
|
object Test
|
||||||
|
{
|
||||||
|
val a = pure(3)
|
||||||
|
val b = pure[Boolean](error("test"))
|
||||||
|
val b2 = pure(true)
|
||||||
|
val c = pure("asdf")
|
||||||
|
val i3 = a :^: b :^: c :^: MNil
|
||||||
|
val i32 = a :^: b2 :^: c :^: MNil
|
||||||
|
|
||||||
|
val fh= (_: Int :+: Boolean :+: String :+: HNil) match
|
||||||
|
{ case aa :+: bb :+: cc :+: HNil => aa + " " + bb + " " + cc }
|
||||||
|
val h1 = i3 mapH fh
|
||||||
|
val h2 = i32 mapH fh
|
||||||
|
|
||||||
|
val f: i3.Map[Result] => Any = {
|
||||||
|
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => aa + " " + bb + " " + cc
|
||||||
|
case x =>
|
||||||
|
val cs = x.toList.collect { case Inc(x) => x } // workaround for double definition bug
|
||||||
|
throw Incomplete(causes = cs)
|
||||||
|
}
|
||||||
|
val d2 = i32 mapR f
|
||||||
|
val f2: i3.Map[Result] => Task[Any] = {
|
||||||
|
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
|
||||||
|
case x => d3
|
||||||
|
}
|
||||||
|
lazy val d = i3 flatMapR f2
|
||||||
|
val f3: i3.Map[Result] => Task[Any] = {
|
||||||
|
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
|
||||||
|
case x => d2
|
||||||
|
}
|
||||||
|
lazy val d3= i3 flatMapR f3
|
||||||
|
|
||||||
|
def d4(i: Int): Task[Int] = MNil flatMap { _ => val x = math.random; if(x < 0.01) pure(i); else d4(i+1) }
|
||||||
|
|
||||||
|
def go()
|
||||||
|
{
|
||||||
|
def run[T](root: Task[T]) =
|
||||||
|
println("Result : " + Task.run(root, true, 2))
|
||||||
|
|
||||||
|
run(a)
|
||||||
|
run(b)
|
||||||
|
run(b2)
|
||||||
|
run(c)
|
||||||
|
run(d)
|
||||||
|
run(d2)
|
||||||
|
run( d4(0) )
|
||||||
|
run(h1)
|
||||||
|
run(h2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import xsbt._
|
import sbt._
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import TaskGen._
|
import TaskGen._
|
||||||
|
import Task._
|
||||||
|
|
||||||
object TaskRunnerCallTest extends Properties("TaskRunner Call")
|
object TaskRunnerCallTest extends Properties("TaskRunner Call")
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +12,7 @@ object TaskRunnerCallTest extends Properties("TaskRunner Call")
|
||||||
val f = fibDirect(i)
|
val f = fibDirect(i)
|
||||||
("Workers: " + workers) |: ("i: " + i) |: ("fib(i): " + f) |:
|
("Workers: " + workers) |: ("i: " + i) |: ("fib(i): " + f) |:
|
||||||
{
|
{
|
||||||
def result = TaskRunner( fibTask(i), workers)
|
def result = tryRun( fibTask(i), false, workers)
|
||||||
checkResult(result, f)
|
checkResult(result, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,11 +24,11 @@ object TaskRunnerCallTest extends Properties("TaskRunner Call")
|
||||||
(index, x1, x2) =>
|
(index, x1, x2) =>
|
||||||
{
|
{
|
||||||
if(index == i)
|
if(index == i)
|
||||||
Task(x2)
|
pure(x2)
|
||||||
else
|
else
|
||||||
iterate( (index+1, x2, x1+x2) )
|
iterate( (index+1, x2, x1+x2) )
|
||||||
}
|
}
|
||||||
def iterate(iteration: (Int,Int,Int)) = Task( iteration ) bind Function.tupled(next)
|
def iterate(iteration: (Int,Int,Int)) = pure( iteration ) flatMap next.tupled
|
||||||
iterate( (1, 0, 1) )
|
iterate( (1, 0, 1) )
|
||||||
}
|
}
|
||||||
final def fibDirect(i: Int): Int =
|
final def fibDirect(i: Int): Int =
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import xsbt._
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2009, 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import TaskGen._
|
import TaskGen._
|
||||||
|
import Task._
|
||||||
|
import Types._
|
||||||
|
|
||||||
object TaskRunnerSortTest extends Properties("TaskRunnerSort")
|
object TaskRunnerSortTest extends Properties("TaskRunnerSort")
|
||||||
{
|
{
|
||||||
|
|
@ -12,31 +17,33 @@ object TaskRunnerSortTest extends Properties("TaskRunnerSort")
|
||||||
java.util.Arrays.sort(sorted)
|
java.util.Arrays.sort(sorted)
|
||||||
("Workers: " + workers) |: ("Array: " + a.toList) |:
|
("Workers: " + workers) |: ("Array: " + a.toList) |:
|
||||||
{
|
{
|
||||||
def result = TaskRunner( sort(a.toArray), if(workers > 0) workers else 1)
|
def result = tryRun( sort(a.toSeq), false, if(workers > 0) workers else 1)
|
||||||
checkResult(result.toList, sorted.toList)
|
checkResult(result.toList, sorted.toList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final def sortDirect(a: RandomAccessSeq[Int]): RandomAccessSeq[Int] =
|
final def sortDirect(a: Seq[Int]): Seq[Int] =
|
||||||
{
|
{
|
||||||
if(a.length < 2)
|
if(a.length < 2)
|
||||||
a
|
a
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val pivot = a(0)
|
val pivot = a(0)
|
||||||
val (lt,gte) = a.projection.drop(1).partition(_ < pivot)
|
val (lt,gte) = a.view.drop(1).partition(_ < pivot)
|
||||||
sortDirect(lt) ++ List(pivot) ++ sortDirect(gte)
|
sortDirect(lt) ++ List(pivot) ++ sortDirect(gte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final def sort(a: RandomAccessSeq[Int]): Task[RandomAccessSeq[Int]] =
|
final def sort(a: Seq[Int]): Task[Seq[Int]] =
|
||||||
{
|
{
|
||||||
if(a.length < 200)
|
if(a.length < 200)
|
||||||
Task(sortDirect(a))
|
pure(sortDirect(a))
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task(a) bind { a =>
|
pure(a) flatMap { a =>
|
||||||
val pivot = a(0)
|
val pivot = a(0)
|
||||||
val (lt,gte) = a.projection.drop(1).partition(_ < pivot)
|
val (lt,gte) = a.view.drop(1).partition(_ < pivot)
|
||||||
(sort(lt), sort(gte)) map { _ ++ List(pivot) ++ _ }
|
sort(lt) :^: sort(gte) :^: MNil mapH {
|
||||||
|
case l :+: g :+: HNil => l ++ List(pivot) ++ g
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
package xsbt
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2009 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
import org.scalacheck.Prop._
|
import org.scalacheck.Prop._
|
||||||
|
|
||||||
|
|
@ -14,8 +17,8 @@ object checkResult
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
case TasksFailed(failures) =>
|
case i: Incomplete =>
|
||||||
failures.foreach(f => f.exception.printStackTrace)
|
println(Incomplete.show(i, true))
|
||||||
"One or more tasks failed" |: false
|
"One or more tasks failed" |: false
|
||||||
case e =>
|
case e =>
|
||||||
e.printStackTrace
|
e.printStackTrace
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,9 @@ final case class HCons[H, T <: HList](head : H, tail : T) extends HList
|
||||||
def up = MCons[H,tail.Up, Id](head, tail.up)
|
def up = MCons[H,tail.Up, Id](head, tail.up)
|
||||||
def :+: [G](g: G): G :+: H :+: T = HCons(g, this)
|
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) )
|
||||||
|
}
|
||||||
|
|
@ -42,3 +42,10 @@ sealed class MNil extends MList[Nothing]
|
||||||
def toList = Nil
|
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 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 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) }
|
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
|
object TypeFunctions extends TypeFunctions
|
||||||
|
|
||||||
|
|
@ -30,3 +34,6 @@ object ~>
|
||||||
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
|
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
|
||||||
implicit def tcIdEquals: (Id ~> Id) = Id
|
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) =
|
def translate[T](msg: => String)(f: => T) =
|
||||||
try { f }
|
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] =
|
def wideConvert[T](f: => T): Either[Throwable, T] =
|
||||||
try { Right(f) }
|
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] =
|
def convert[T](f: => T): Either[Exception, T] =
|
||||||
try { Right(f) }
|
try { Right(f) }
|
||||||
catch { case e: Exception => Left(e) }
|
catch { case e: Exception => Left(e) }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
/* sbt -- Simple Build Tool
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2009, 2010 Mark Harrah
|
* Copyright 2009, 2010 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt.api
|
package xsbt
|
||||||
|
package api
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import xsbt.FileUtilities
|
import xsbt.FileUtilities
|
||||||
|
|
|
||||||
|
|
@ -399,4 +399,36 @@ object IO
|
||||||
|
|
||||||
/** Splits a String around path separator characters. */
|
/** Splits a String around path separator characters. */
|
||||||
def pathSplit(s: String) = PathSeparatorPattern.split(s)
|
def pathSplit(s: String) = PathSeparatorPattern.split(s)
|
||||||
|
|
||||||
|
/** Move the provided files to a temporary location.
|
||||||
|
* If 'f' returns normally, delete the files.
|
||||||
|
* If 'f' throws an Exception, return the files to their original location.*/
|
||||||
|
def stash[T](files: Set[File])(f: => T): T =
|
||||||
|
withTemporaryDirectory { dir =>
|
||||||
|
val stashed = stashLocations(dir, files.toArray)
|
||||||
|
move(stashed)
|
||||||
|
|
||||||
|
try { f } catch { case e: Exception =>
|
||||||
|
try { move(stashed.map(_.swap)); throw e }
|
||||||
|
catch { case _: Exception => throw e }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def stashLocations(dir: File, files: Array[File]) =
|
||||||
|
for( (file, index) <- files.zipWithIndex) yield
|
||||||
|
(file, new File(dir, index.toHexString))
|
||||||
|
|
||||||
|
def move(files: Iterable[(File, File)]): Unit =
|
||||||
|
files.foreach(Function.tupled(move))
|
||||||
|
|
||||||
|
def move(a: File, b: File): Unit =
|
||||||
|
{
|
||||||
|
if(b.exists)
|
||||||
|
delete(b)
|
||||||
|
if(!a.renameTo(b))
|
||||||
|
{
|
||||||
|
copyFile(a, b)
|
||||||
|
delete(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah */
|
||||||
|
|
||||||
|
package xsbt
|
||||||
|
|
||||||
|
import org.specs._
|
||||||
|
|
||||||
|
import FileUtilities._
|
||||||
|
import java.io.File
|
||||||
|
import Function.tupled
|
||||||
|
|
||||||
|
object CheckStash extends Specification
|
||||||
|
{
|
||||||
|
"stash" should {
|
||||||
|
"handle empty files" in {
|
||||||
|
stash(Set()) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
"move files during execution" in {
|
||||||
|
WithFiles(TestFiles : _*) ( checkMove )
|
||||||
|
}
|
||||||
|
|
||||||
|
"restore files on exceptions but not errors" in {
|
||||||
|
WithFiles(TestFiles : _*) ( checkRestore )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkRestore(seq: Seq[File])
|
||||||
|
{
|
||||||
|
allCorrect(seq)
|
||||||
|
|
||||||
|
stash0(seq, throw new TestRuntimeException) must beFalse
|
||||||
|
allCorrect(seq)
|
||||||
|
|
||||||
|
stash0(seq, throw new TestException) must beFalse
|
||||||
|
allCorrect(seq)
|
||||||
|
|
||||||
|
stash0(seq, throw new TestError) must beFalse
|
||||||
|
noneExist(seq)
|
||||||
|
}
|
||||||
|
def checkMove(seq: Seq[File])
|
||||||
|
{
|
||||||
|
allCorrect(seq)
|
||||||
|
stash0(seq, ()) must beTrue
|
||||||
|
noneExist(seq)
|
||||||
|
}
|
||||||
|
def stash0(seq: Seq[File], post: => Unit): Boolean =
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stash(Set() ++ seq) {
|
||||||
|
noneExist(seq)
|
||||||
|
post
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
case _: TestError | _: TestException | _: TestRuntimeException => false
|
||||||
|
}
|
||||||
|
|
||||||
|
def allCorrect(s: Seq[File]) = (s.toList zip TestFiles.toList).forall(tupled(correct))
|
||||||
|
def correct(check: File, ref: (File, String)) =
|
||||||
|
{
|
||||||
|
check.exists must beTrue
|
||||||
|
read(check) must beEqual(ref._2)
|
||||||
|
}
|
||||||
|
def noneExist(s: Seq[File]) = s.forall(!_.exists) must beTrue
|
||||||
|
|
||||||
|
lazy val TestFiles =
|
||||||
|
Seq(
|
||||||
|
"a/b/c" -> "content1",
|
||||||
|
"a/b/e" -> "content1",
|
||||||
|
"c" -> "",
|
||||||
|
"e/g" -> "asdf",
|
||||||
|
"a/g/c" -> "other"
|
||||||
|
) map {
|
||||||
|
case (f, c) => (new File(f), c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TestError extends Error
|
||||||
|
class TestRuntimeException extends RuntimeException
|
||||||
|
class TestException extends Exception
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
/* sbt -- Simple Build Tool
|
/* 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.*/
|
/** 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
|
private var level: Level.Value = Level.Info
|
||||||
def getLevel = level
|
def getLevel = level
|
||||||
def setLevel(newLevel: Level.Value) { level = newLevel }
|
def setLevel(newLevel: Level.Value) { level = newLevel }
|
||||||
def enableTrace(flag: Boolean) { traceEnabledVar = flag }
|
def setTrace(level: Int) { traceEnabledVar = level }
|
||||||
def traceEnabled = traceEnabledVar
|
def getTrace = traceEnabledVar
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
/* sbt -- Simple Build Tool
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt
|
package xsbt
|
||||||
|
|
||||||
|
import sbt.{AbstractLogger, ControlEvent, Level, Log, LogEvent, SetLevel, SetTrace, Success, Trace}
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
/** A logger that can buffer the logging done on it and then can flush the buffer
|
/** 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.
|
* 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] val buffer = new ListBuffer[LogEvent]
|
||||||
private[this] var recording = false
|
private[this] var recording = false
|
||||||
|
|
@ -54,10 +55,10 @@ class BufferedLogger(delegate: Logger) extends Logger
|
||||||
}
|
}
|
||||||
def getLevel = delegate.getLevel
|
def getLevel = delegate.getLevel
|
||||||
def traceEnabled = delegate.traceEnabled
|
def traceEnabled = delegate.traceEnabled
|
||||||
def enableTrace(flag: Boolean)
|
def setTrace(level: Int)
|
||||||
{
|
{
|
||||||
buffer += new SetTrace(flag)
|
buffer += new SetTrace(level)
|
||||||
delegate.enableTrace(flag)
|
delegate.setTrace(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
def trace(t: => Throwable): Unit =
|
def trace(t: => Throwable): Unit =
|
||||||
|
|
@ -73,9 +74,9 @@ class BufferedLogger(delegate: Logger) extends Logger
|
||||||
delegate.logAll(events)
|
delegate.logAll(events)
|
||||||
def control(event: ControlEvent.Value, message: => String): Unit =
|
def control(event: ControlEvent.Value, message: => String): Unit =
|
||||||
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
|
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)
|
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(condition)
|
||||||
{
|
{
|
||||||
if(recording)
|
if(recording)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* sbt -- Simple Build Tool
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt
|
package sbt
|
||||||
|
|
||||||
object ConsoleLogger
|
object ConsoleLogger
|
||||||
{
|
{
|
||||||
|
|
@ -17,10 +17,12 @@ object ConsoleLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A logger that logs to the console. On supported systems, the level labels are
|
/** 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
|
class ConsoleLogger extends BasicLogger
|
||||||
{
|
{
|
||||||
import ConsoleLogger.formatEnabled
|
override def ansiCodesSupported = ConsoleLogger.formatEnabled
|
||||||
def messageColor(level: Level.Value) = Console.RESET
|
def messageColor(level: Level.Value) = Console.RESET
|
||||||
def labelColor(level: Level.Value) =
|
def labelColor(level: Level.Value) =
|
||||||
level match
|
level match
|
||||||
|
|
@ -39,8 +41,9 @@ class ConsoleLogger extends BasicLogger
|
||||||
def trace(t: => Throwable): Unit =
|
def trace(t: => Throwable): Unit =
|
||||||
System.out.synchronized
|
System.out.synchronized
|
||||||
{
|
{
|
||||||
if(traceEnabled)
|
val traceLevel = getTrace
|
||||||
t.printStackTrace
|
if(traceLevel >= 0)
|
||||||
|
System.out.synchronized { System.out.print(StackTrace.trimmed(t, traceLevel)) }
|
||||||
}
|
}
|
||||||
def log(level: Level.Value, message: => String)
|
def log(level: Level.Value, message: => String)
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +52,7 @@ class ConsoleLogger extends BasicLogger
|
||||||
}
|
}
|
||||||
private def setColor(color: String)
|
private def setColor(color: String)
|
||||||
{
|
{
|
||||||
if(formatEnabled)
|
if(ansiCodesSupported)
|
||||||
System.out.synchronized { System.out.print(color) }
|
System.out.synchronized { System.out.print(color) }
|
||||||
}
|
}
|
||||||
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =
|
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
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
* Copyright 2008, 2009 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt
|
package sbt
|
||||||
|
|
||||||
/** An enumeration defining the levels available for logging. A level includes all of the levels
|
/** 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).*/
|
* 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 Debug = Value(1, "debug")
|
||||||
val Info = Value(2, "info")
|
val Info = Value(2, "info")
|
||||||
|
|
@ -16,10 +16,8 @@ object Level extends Enumeration with NotNull
|
||||||
* label is also defined here. */
|
* label is also defined here. */
|
||||||
val SuccessLabel = "success"
|
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. */
|
/** 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. */
|
/** 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
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
* Copyright 2008, 2009 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt
|
package sbt
|
||||||
|
|
||||||
sealed trait LogEvent extends NotNull
|
sealed trait LogEvent extends NotNull
|
||||||
final class Success(val msg: String) extends LogEvent
|
final class Success(val msg: String) extends LogEvent
|
||||||
final class Log(val level: Level.Value, 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 Trace(val exception: Throwable) extends LogEvent
|
||||||
final class SetLevel(val newLevel: Level.Value) 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
|
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
|
||||||
|
|
||||||
object ControlEvent extends Enumeration
|
object ControlEvent extends Enumeration
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
/* sbt -- Simple Build Tool
|
/* sbt -- Simple Build Tool
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
* Copyright 2008, 2009 Mark Harrah
|
||||||
*/
|
*/
|
||||||
package xsbt
|
package sbt
|
||||||
|
|
||||||
import xsbti.{Logger => xLogger, F0}
|
import xsbti.{Logger => xLogger, F0}
|
||||||
abstract class Logger extends xLogger with NotNull
|
|
||||||
|
abstract class AbstractLogger extends xLogger with NotNull
|
||||||
{
|
{
|
||||||
def getLevel: Level.Value
|
def getLevel: Level.Value
|
||||||
def setLevel(newLevel: Level.Value)
|
def setLevel(newLevel: Level.Value)
|
||||||
def enableTrace(flag: Boolean)
|
def setTrace(flag: Int)
|
||||||
def traceEnabled: Boolean
|
def getTrace: Int
|
||||||
|
final def traceEnabled = getTrace >= 0
|
||||||
|
def ansiCodesSupported = false
|
||||||
|
|
||||||
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
def atLevel(level: Level.Value) = level.id >= getLevel.id
|
||||||
def trace(t: => Throwable): Unit
|
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 debug(message: => String): Unit = log(Level.Debug, message)
|
||||||
final def info(message: => String): Unit = log(Level.Info, message)
|
final def info(message: => String): Unit = log(Level.Info, message)
|
||||||
final def warn(message: => String): Unit = log(Level.Warn, 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 l: Log => log(l.level, l.msg)
|
||||||
case t: Trace => trace(t.exception)
|
case t: Trace => trace(t.exception)
|
||||||
case setL: SetLevel => setLevel(setL.newLevel)
|
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)
|
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