diff --git a/cache/Cache.scala b/cache/Cache.scala
index ae46845c9..90677f29a 100644
--- a/cache/Cache.scala
+++ b/cache/Cache.scala
@@ -1,6 +1,6 @@
package xsbt
-import sbinary.{CollectionTypes, Format, JavaFormats}
+import sbinary.{CollectionTypes, Format, JavaFormats, Operations}
import java.io.File
trait Cache[I,O]
@@ -11,7 +11,7 @@ trait SBinaryFormats extends CollectionTypes with JavaFormats with NotNull
{
//TODO: add basic types from SBinary minus FileFormat
}
-object Cache extends BasicCacheImplicits with SBinaryFormats with HListCacheImplicits
+object Cache extends BasicCacheImplicits with SBinaryFormats with HListCacheImplicits with TupleCacheImplicits
{
def cache[I,O](implicit c: Cache[I,O]): Cache[I,O] = c
def outputCache[O](implicit c: OutputCache[O]): OutputCache[O] = c
@@ -32,7 +32,7 @@ object Cache extends BasicCacheImplicits with SBinaryFormats with HListCacheImpl
cache(file)(in) match
{
case Left(value) => Value(value)
- case Right(store) => NewTask(m.map { out => store(out); out })
+ case Right(store) => m.map { out => store(out); out }
}
}
trait BasicCacheImplicits extends NotNull
@@ -55,4 +55,31 @@ trait HListCacheImplicits extends HLists
implicit def hConsOutputCache[H,T<:HList](implicit headCache: OutputCache[H], tailCache: OutputCache[T]): OutputCache[HCons[H,T]] =
new HConsOutputCache(headCache, tailCache)
implicit lazy val hNilOutputCache: OutputCache[HNil] = new HNilOutputCache
+}
+trait TupleCacheImplicits extends HLists
+{
+ import Cache._
+ implicit def tuple2HList[A,B](t: (A,B)): A :: B :: HNil = t._1 :: t._2 :: HNil
+ implicit def hListTuple2[A,B](t: A :: B :: HNil): (A,B) = t match { case a :: b :: HNil => (a,b) }
+
+ implicit def tuple2InputCache[A,B](implicit aCache: InputCache[A], bCache: InputCache[B]): InputCache[(A,B)] =
+ wrapInputCache[(A,B), A :: B :: HNil]
+ implicit def tuple2OutputCache[A,B](implicit aCache: OutputCache[A], bCache: OutputCache[B]): OutputCache[(A,B)] =
+ wrapOutputCache[(A,B), A :: B :: HNil]
+
+ implicit def tuple3HList[A,B,C](t: (A,B,C)): A :: B :: C :: HNil = t._1 :: t._2 :: t._3 :: HNil
+ implicit def hListTuple3[A,B,C](t: A :: B :: C :: HNil): (A,B,C) = t match { case a :: b :: c :: HNil => (a,b,c) }
+
+ implicit def tuple3InputCache[A,B,C](implicit aCache: InputCache[A], bCache: InputCache[B], cCache: InputCache[C]): InputCache[(A,B,C)] =
+ wrapInputCache[(A,B,C), A :: B :: C :: HNil]
+ implicit def tuple3OutputCache[A,B,C](implicit aCache: OutputCache[A], bCache: OutputCache[B], cCache: OutputCache[C]): OutputCache[(A,B,C)] =
+ wrapOutputCache[(A,B,C), A :: B :: C :: HNil]
+
+ implicit def tuple4HList[A,B,C,D](t: (A,B,C,D)): A :: B :: C :: D :: HNil = t._1 :: t._2 :: t._3 :: t._4 :: HNil
+ implicit def hListTuple4[A,B,C,D](t: A :: B :: C :: D :: HNil): (A,B,C,D) = t match { case a :: b :: c :: d:: HNil => (a,b,c,d) }
+
+ implicit def tuple4InputCache[A,B,C,D](implicit aCache: InputCache[A], bCache: InputCache[B], cCache: InputCache[C], dCache: InputCache[D]): InputCache[(A,B,C,D)] =
+ wrapInputCache[(A,B,C,D), A :: B :: C :: D :: HNil]
+ implicit def tuple4OutputCache[A,B,C,D](implicit aCache: OutputCache[A], bCache: OutputCache[B], cCache: OutputCache[C], dCache: OutputCache[D]): OutputCache[(A,B,C,D)] =
+ wrapOutputCache[(A,B,C,D), A :: B :: C :: D :: HNil]
}
\ No newline at end of file
diff --git a/cache/CacheIO.scala b/cache/CacheIO.scala
new file mode 100644
index 000000000..ba4cc0edc
--- /dev/null
+++ b/cache/CacheIO.scala
@@ -0,0 +1,26 @@
+package xsbt
+
+import java.io.File
+import sbinary.{DefaultProtocol, Format, Operations}
+import scala.reflect.Manifest
+
+object CacheIO
+{
+ def fromFile[T](format: Format[T])(file: File)(implicit mf: Manifest[Format[T]]): T =
+ fromFile(file)(format, mf)
+ def fromFile[T](file: File)(implicit format: Format[T], mf: Manifest[Format[T]]): T =
+ Operations.fromFile(file)(stampedFormat(format))
+ def toFile[T](format: Format[T])(value: T)(file: File)(implicit mf: Manifest[Format[T]]): Unit =
+ toFile(value)(file)(format, mf)
+ def toFile[T](value: T)(file: File)(implicit format: Format[T], mf: Manifest[Format[T]]): Unit =
+ Operations.toFile(value)(file)(stampedFormat(format))
+ def stampedFormat[T](format: Format[T])(implicit mf: Manifest[Format[T]]): Format[T] =
+ {
+ import DefaultProtocol._
+ withStamp(stamp(format))(format)
+ }
+ def stamp[T](format: Format[T])(implicit mf: Manifest[Format[T]]): Int = typeHash(mf)
+ def typeHash[T](implicit mf: Manifest[T]) = mf.toString.hashCode
+ def manifest[T](implicit mf: Manifest[T]): Manifest[T] = mf
+ def objManifest[T](t: T)(implicit mf: Manifest[T]): Manifest[T] = mf
+}
\ No newline at end of file
diff --git a/cache/ChangeReport.scala b/cache/ChangeReport.scala
index baa078034..a16b875dc 100644
--- a/cache/ChangeReport.scala
+++ b/cache/ChangeReport.scala
@@ -1,5 +1,21 @@
package xsbt
+object ChangeReport
+{
+ def modified[T](files: Set[T]) =
+ new EmptyChangeReport[T]
+ {
+ override def allInputs = files
+ override def modified = files
+ override def markAllModified = this
+ }
+ def unmodified[T](files: Set[T]) =
+ new EmptyChangeReport[T]
+ {
+ override def allInputs = files
+ override def unmodified = files
+ }
+}
trait ChangeReport[T] extends NotNull
{
def allInputs: Set[T]
@@ -8,6 +24,24 @@ trait ChangeReport[T] extends NotNull
def added: Set[T]
def removed: Set[T]
def +++(other: ChangeReport[T]): ChangeReport[T] = new CompoundChangeReport(this, other)
+ def markAllModified: ChangeReport[T] =
+ new ChangeReport[T]
+ {
+ def allInputs = ChangeReport.this.allInputs
+ def unmodified = Set.empty[T]
+ def modified = ChangeReport.this.allInputs
+ def added = ChangeReport.this.added
+ def removed = ChangeReport.this.removed
+ override def markAllModified = this
+ }
+}
+class EmptyChangeReport[T] extends ChangeReport[T]
+{
+ def allInputs = Set.empty[T]
+ def unmodified = Set.empty[T]
+ def modified = Set.empty[T]
+ def added = Set.empty[T]
+ def removed = Set.empty[T]
}
trait InvalidationReport[T] extends NotNull
{
diff --git a/cache/DependencyTracking.scala b/cache/DependencyTracking.scala
index 931efba1c..d7f889dc3 100644
--- a/cache/DependencyTracking.scala
+++ b/cache/DependencyTracking.scala
@@ -1,12 +1,14 @@
package xsbt
import java.io.File
-import sbinary.{Format, Operations}
+import CacheIO.{fromFile, toFile}
+import sbinary.Format
+import scala.reflect.Manifest
object DependencyTracking
{
def trackBasic[T, F <: FileInfo](filesTask: Task[Set[File]], style: FilesInfo.Style[F], cacheDirectory: File)
- (f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => Task[T]): Task[T] =
+ (f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => Task[T])(implicit mf: Manifest[F]): Task[T] =
{
changed(filesTask, style, new File(cacheDirectory, "files")) { sourceChanges =>
invalidate(sourceChanges, cacheDirectory) { (report, tracking) =>
@@ -14,9 +16,24 @@ object DependencyTracking
}
}
}
- def changed[T, F <: FileInfo](filesTask: Task[Set[File]], style: FilesInfo.Style[F], cache: File)(f: ChangeReport[File] => Task[T]): Task[T] =
+ def changed[O,O2](task: Task[O], file: File)(ifChanged: O => O2, ifUnchanged: O => O2)(implicit input: InputCache[O]): Task[O2] { type Input = O } =
+ task map { value =>
+ val cache = OpenResource.fileInputStream(file)(input.uptodate(value))
+ if(cache.uptodate)
+ ifUnchanged(value)
+ else
+ {
+ OpenResource.fileOutputStream(false)(file)(cache.update)
+ ifChanged(value)
+ }
+ }
+ def changed[T, F <: FileInfo](files: Set[File], style: FilesInfo.Style[F], cache: File)
+ (f: ChangeReport[File] => Task[T])(implicit mf: Manifest[F]): Task[T] =
+ changed(Task(files), style, cache)(f)
+ def changed[T, F <: FileInfo](filesTask: Task[Set[File]], style: FilesInfo.Style[F], cache: File)
+ (f: ChangeReport[File] => Task[T])(implicit mf: Manifest[F]): Task[T] =
filesTask bind { files =>
- val lastFilesInfo = Operations.fromFile(cache)(style.format).files
+ val lastFilesInfo = fromFile(style.formats)(cache).files
val lastFiles = lastFilesInfo.map(_.file)
val currentFiles = files.map(_.getAbsoluteFile)
val currentFilesInfo = style(files)
@@ -31,7 +48,7 @@ object DependencyTracking
}
f(report) map { result =>
- Operations.toFile(currentFilesInfo)(cache)(style.format)
+ toFile(style.formats)(currentFilesInfo)(cache)
result
}
}
@@ -41,10 +58,11 @@ object DependencyTracking
report.invalidProducts.foreach(_.delete)
f(report, tracking)
}
- invalidate(Task(changes), cacheDirectory, true)(pruneAndF)(sbinary.DefaultProtocol.FileFormat)
+ implicit val format = sbinary.DefaultProtocol.FileFormat
+ invalidate(Task(changes), cacheDirectory, true)(pruneAndF)
}
def invalidate[T,R](changesTask: Task[ChangeReport[T]], cacheDirectory: File, translateProducts: Boolean)
- (f: (InvalidationReport[T], UpdateTracking[T]) => Task[R])(implicit format: Format[T]): Task[R] =
+ (f: (InvalidationReport[T], UpdateTracking[T]) => Task[R])(implicit format: Format[T], mf: Manifest[T]): Task[R] =
{
changesTask bind { changes =>
val trackFormat = new TrackingFormat[T](cacheDirectory, translateProducts)
diff --git a/cache/FileInfo.scala b/cache/FileInfo.scala
index 8c835fe95..0271ad0ec 100644
--- a/cache/FileInfo.scala
+++ b/cache/FileInfo.scala
@@ -60,13 +60,17 @@ object FilesInfo
{
sealed trait Style[F <: FileInfo] extends NotNull
{
- implicit def apply(files: Iterable[File]): FilesInfo[F]
- implicit val format: Format[FilesInfo[F]]
+ implicit def apply(files: Set[File]): FilesInfo[F]
+ implicit def unapply(info: FilesInfo[F]): Set[File] = info.files.map(_.file)
+ implicit val formats: Format[FilesInfo[F]]
+ import Cache._
+ implicit def infosInputCache: InputCache[Set[File]] = wrapInputCache[Set[File],FilesInfo[F]]
+ implicit def infosOutputCache: OutputCache[Set[File]] = wrapOutputCache[Set[File],FilesInfo[F]]
}
private final class BasicStyle[F <: FileInfo](fileStyle: FileInfo.Style[F])(implicit infoFormat: Format[F]) extends Style[F]
{
- implicit def apply(files: Iterable[File]) = FilesInfo( (Set() ++ files.map(_.getAbsoluteFile)).map(fileStyle.apply) )
- implicit val format: Format[FilesInfo[F]] = wrap(_.files, (fs: Set[F]) => new FilesInfo(fs))
+ implicit def apply(files: Set[File]): FilesInfo[F] = FilesInfo( files.map(_.getAbsoluteFile).map(fileStyle.apply) )
+ implicit val formats: Format[FilesInfo[F]] = wrap(_.files, (fs: Set[F]) => new FilesInfo(fs))
}
lazy val full: Style[HashModifiedFileInfo] = new BasicStyle(FileInfo.full)(FileInfo.full.format)
lazy val hash: Style[HashFileInfo] = new BasicStyle(FileInfo.hash)(FileInfo.hash.format)
diff --git a/cache/TrackingFormat.scala b/cache/TrackingFormat.scala
index 1b6c463a0..166c22df2 100644
--- a/cache/TrackingFormat.scala
+++ b/cache/TrackingFormat.scala
@@ -2,29 +2,31 @@ package xsbt
import java.io.File
import scala.collection.mutable.{HashMap, Map, MultiMap, Set}
-import sbinary.{DefaultProtocol, Format, Operations}
+import scala.reflect.Manifest
+import sbinary.{DefaultProtocol, Format}
import DefaultProtocol._
import TrackingFormat._
+import CacheIO.{fromFile, toFile}
import DependencyTracking.{DependencyMap => DMap, newMap}
-private class TrackingFormat[T](directory: File, translateProducts: Boolean)(implicit tFormat: Format[T]) extends NotNull
+private class TrackingFormat[T](directory: File, translateProducts: Boolean)(implicit tFormat: Format[T], mf: Manifest[T]) extends NotNull
{
-
val indexFile = new File(directory, "index")
val dependencyFile = new File(directory, "dependencies")
def read(): DependencyTracking[T] =
{
- val indexMap = Operations.fromFile[Map[Int,T]](indexFile)
+ val indexMap = CacheIO.fromFile[Map[Int,T]](indexFile)
val indexedFormat = wrap[T,Int](ignore => error("Read-only"), indexMap.apply)
- Operations.fromFile(dependencyFile)(trackingFormat(translateProducts)(indexedFormat))
+ val trackFormat = trackingFormat(translateProducts)(indexedFormat)
+ fromFile(trackFormat)(dependencyFile)
}
def write(tracking: DependencyTracking[T])
{
val index = new IndexMap[T]
val indexedFormat = wrap[T,Int](t => index(t), ignore => error("Write-only"))
-
- Operations.toFile(tracking)(dependencyFile)(trackingFormat(translateProducts)(indexedFormat))
- Operations.toFile(index.indices)(indexFile)
+ val trackFormat = trackingFormat(translateProducts)(indexedFormat)
+ toFile(trackFormat)(tracking)(dependencyFile)
+ toFile(index.indices)(indexFile)
}
}
private object TrackingFormat
diff --git a/cache/src/test/scala/Tracking.scala b/cache/src/test/scala/Tracking.scala
deleted file mode 100644
index ff952556b..000000000
--- a/cache/src/test/scala/Tracking.scala
+++ /dev/null
@@ -1,45 +0,0 @@
-package xsbt
-
-import java.io.File
-
-trait examples
-{
- def classpathTask: Task[Set[File]]
- def sourcesTask: Task[Set[File]]
- import DependencyTracking._
- lazy val compile =
- changed(classpathTask, FilesInfo.lastModified, new File("cache/compile/classpath/")) { classpathChanges =>
- changed(sourcesTask, FilesInfo.hash, new File("cache/compile/sources/")) { sourceChanges =>
- invalidate(classpathChanges +++ sourceChanges, new File("cache/compile/dependencies/'")) { (report, tracking) =>
- val recompileSources = report.invalid ** sourceChanges.allInputs
- val classpath = classpathChanges.allInputs
- Task()
- }
- }
- }
-
- trait sync
- {
- def sources: Task[Set[File]] = Task(Set.empty[File])
- def mapper: Task[FileMapper] = outputDirectory map(FileMapper.basic)
- def outputDirectory: Task[File] = Task(new File("test"))
-
- import Task._
- lazy val task = syncTask
- def syncTask =
- (sources, mapper) bind { (srcs,mp) =>
- DependencyTracking.trackBasic(sources, FilesInfo.hash, new File("cache/sync/")) { (sourceChanges, report, tracking) =>
- Task
- {
- for(src <- report.invalid ** sourceChanges.allInputs) yield
- {
- val target = mp(src)
- FileUtilities.copyFile(src, target)
- tracking.product(src, target)
- target
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/compile/ComponentCompiler.scala b/compile/ComponentCompiler.scala
index 369c0cb85..c9d8cbadb 100644
--- a/compile/ComponentCompiler.scala
+++ b/compile/ComponentCompiler.scala
@@ -35,6 +35,7 @@ class ComponentCompiler(scalaVersion: String, compiler: RawCompiler, manager: Co
* any resources from the source jars into a final jar.*/
private def compileSources(sourceJars: Iterable[File], targetJar: File, id: String)
{
+ import Paths._
withTemporaryDirectory { dir =>
val extractedSources = (Set[File]() /: sourceJars) { (extracted, sourceJar)=> extracted ++ unzip(sourceJar, dir) }
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
@@ -42,8 +43,8 @@ class ComponentCompiler(scalaVersion: String, compiler: RawCompiler, manager: Co
val xsbtiJars = manager.files(xsbtiID)
val arguments = Seq("-d", outputDirectory.getAbsolutePath, "-cp", xsbtiJars.mkString(File.pathSeparator)) ++ sourceFiles.toSeq.map(_.getAbsolutePath)
compiler(arguments)
- copy(resources, outputDirectory, PathMapper.relativeTo(dir))
- zip(Seq(outputDirectory), targetJar, true, PathMapper.relativeTo(outputDirectory))
+ copy(resources x (FileMapper.rebase(dir, outputDirectory)))
+ zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
}
}
}
diff --git a/compile/src/test/scala/CompileTest.scala b/compile/src/test/scala/CompileTest.scala
index dbb26516a..3e63d5240 100644
--- a/compile/src/test/scala/CompileTest.scala
+++ b/compile/src/test/scala/CompileTest.scala
@@ -45,6 +45,7 @@ object CompileTest extends Specification
error("Resource not found: " + resource)
prepare(manager, id, FileUtilities.asFile(src))
}
+ import Paths._
private def prepare(manager: ComponentManager, id: String, file: File): Unit =
- FileUtilities.copy(Seq(file), manager.location(id), PathMapper.flat)
+ FileUtilities.copy(file x FileMapper.flat(manager.location(id)))
}
\ No newline at end of file
diff --git a/project/build.properties b/project/build.properties
index 2597cae5b..2b57548c4 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1,7 +1,7 @@
-#Project Properties
-#Sat Aug 15 11:30:36 EDT 2009
-project.name=xsbt
+#Project properties
+#Fri Aug 28 10:19:53 EDT 2009
project.organization=org.scala-tools.sbt
+project.name=xsbt
sbt.version=0.5.3-p1
-scala.version=2.7.5
project.version=0.7
+scala.version=2.7.5
diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala
index 7e387e868..fdf0669aa 100644
--- a/project/build/XSbt.scala
+++ b/project/build/XSbt.scala
@@ -19,13 +19,15 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub)
val logSub = project(utilPath / "log", "Logging", new Base(_))
- val taskSub = project("tasks", "Tasks", new TaskProject(_), controlSub, collectionSub)
+ val taskSub = project(tasksPath, "Tasks", new TaskProject(_), controlSub, collectionSub)
val cacheSub = project("cache", "Cache", new CacheProject(_), taskSub, ioSub)
+ val stdTaskSub = project(tasksPath / "standard", "Standard Tasks", new Base(_), taskSub, cacheSub)
val compilerSub = project(compilePath, "Compile", new CompileProject(_),
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
/* Multi-subproject paths */
+ def tasksPath = path("tasks")
def launchPath = path("launch")
def utilPath = path("util")
def compilePath = path("compile")
diff --git a/tasks/Task.scala b/tasks/Task.scala
index 0b99198ac..7ff3d2d3d 100644
--- a/tasks/Task.scala
+++ b/tasks/Task.scala
@@ -3,12 +3,14 @@ package xsbt
import Task.{mapTask,bindTask, ITask}
import scala.collection.{mutable,immutable}
-sealed abstract class Task[O] extends Identity
+sealed trait Result[O] extends NotNull
+final case class Value[O](t: O) extends Result[O]
+sealed abstract class Task[O] extends Identity with Result[O]
{
type Input
def dependencies: TreeHashSet[Task[_]] // IMPORTANT!! immutable.HashSet is NOT suitable. It has issues with multi-threaded access
def map[N](f: O => N): ITask[O,N]
- def bind[N](f: O => Task[N]): ITask[O,N]
+ def bind[N](f: O => Result[N]): ITask[O,N]
def dependsOn(addDependencies: Task[_]*): ITask[Input,O]
def named(s: String): ITask[Input,O]
}
@@ -21,7 +23,7 @@ private final class M[I,O,R <: Result[O]](name: Option[String])
final def dependsOn(addDependencies: Task[_]*) = new M(name)(dependencies ++ addDependencies)(extract)(compute)
final def map[N](f: O => N) = mapTask(this)(_(this))(f)
- final def bind[N](f: O => Task[N]) = bindTask(this)(_(this))(f)
+ final def bind[N](f: O => Result[N]) = bindTask(this)(_(this))(f)
final def named(s: String) =
name match
{
@@ -42,9 +44,6 @@ private trait Results extends NotNull
def contains(task: Task[_]): Boolean
}
-private sealed trait Result[O] extends NotNull
-private final case class NewTask[O](t: Task[O]) extends Result[O]
-private final case class Value[O](t: O) extends Result[O]
object Task
{
@@ -52,8 +51,8 @@ object Task
import Function.tupled
def apply[O](o: => O): ITask[Unit,O] =
new M[Unit,O,Value[O]]()(r => ())( u => Value(o) )
- def bindTask[I,O](dependencies: Task[_]*)(extract: Results => I)(compute: I => Task[O]): ITask[I,O] =
- new M[I,O,NewTask[O]](dependencies : _*)(extract)(in => NewTask(compute(in)))
+ def bindTask[I,O](dependencies: Task[_]*)(extract: Results => I)(compute: I => Result[O]): ITask[I,O] =
+ new M[I,O,Result[O]](dependencies : _*)(extract)(compute)
def mapTask[I,O](dependencies: Task[_]*)(extract: Results => I)(compute: I => O): ITask[I,O] =
new M[I,O,Value[O]](dependencies : _*)(extract)(in => Value(compute(in)))
@@ -123,7 +122,7 @@ object Task
def get(results: Results) = HCons(results(head), tail.get(results))
def map[X](f: HListType => X): ITask[HListType,X] = mapTask(tasks: _*)(get)(f)
- def bind[X](f: HListType => Task[X]): ITask[HListType,X] = bindTask(tasks: _*)(get)(f)
+ def bind[X](f: HListType => Result[X]): ITask[HListType,X] = bindTask(tasks: _*)(get)(f)
def join: ITask[HListType,HListType] = map(identity[HListType])
}
val TNil = new TNil
@@ -133,13 +132,20 @@ object Task
final class Builder2[A,B] private[Task](a: Task[A], b: Task[B]) extends NotNull
{
def map[X](f: (A,B) => X): ITask[(A,B),X] = mapTask(a,b)(r => (r(a), r(b)))(tupled(f))
- def bind[X](f: (A,B) => Task[X]): ITask[(A,B),X] = bindTask(a,b)( r => (r(a), r(b)) )(tupled(f))
+ def bind[X](f: (A,B) => Result[X]): ITask[(A,B),X] = bindTask(a,b)( r => (r(a), r(b)) )(tupled(f))
}
implicit def threeToBuilder[A,B,C](t: (Task[A], Task[B], Task[C])): Builder3[A,B,C] = t match { case (a,b,c) => new Builder3(a,b,c) }
final class Builder3[A,B,C] private[Task](a: Task[A], b: Task[B], c: Task[C]) extends NotNull
{
def map[X](f: (A,B,C) => X): ITask[(A,B,C),X] = mapTask(a,b,c)( r => (r(a), r(b), r(c)) )(tupled(f))
- def bind[X](f: (A,B,C) => Task[X]): ITask[(A,B,C),X] = bindTask(a,b,c)( r => (r(a), r(b), r(c)) )(tupled(f))
+ def bind[X](f: (A,B,C) => Result[X]): ITask[(A,B,C),X] = bindTask(a,b,c)( r => (r(a), r(b), r(c)) )(tupled(f))
+ }
+
+ implicit def fourToBuilder[A,B,C,D](t: (Task[A], Task[B], Task[C], Task[D])): Builder4[A,B,C,D] = t match { case (a,b,c,d) => new Builder4(a,b,c,d) }
+ final class Builder4[A,B,C,D] private[Task](a: Task[A], b: Task[B], c: Task[C], d: Task[D]) extends NotNull
+ {
+ def map[X](f: (A,B,C,D) => X): ITask[(A,B,C,D),X] = mapTask(a,b,c,d)( r => (r(a), r(b), r(c), r(d)) )(tupled(f))
+ def bind[X](f: (A,B,C,D) => Result[X]): ITask[(A,B,C,D),X] = bindTask(a,b,c,d)( r => (r(a), r(b), r(c),r(d)) )(tupled(f))
}
}
diff --git a/tasks/TaskScheduler.scala b/tasks/TaskScheduler.scala
index 9c2f47a9f..8810cfd48 100644
--- a/tasks/TaskScheduler.scala
+++ b/tasks/TaskScheduler.scala
@@ -176,7 +176,7 @@ private final class TaskScheduler[O](root: Task[O], strategy: ScheduleStrategy[W
private def success[O](task: Task[O], value: Result[O]): Unit =
value match
{
- case NewTask(t) =>
+ case t: Task[O] =>
if(t eq task)
{
failureReports += WorkFailure(t, CircularDependency(t, task))
diff --git a/util/io/FileUtilities.scala b/util/io/FileUtilities.scala
index 7458c4069..eff8897ed 100644
--- a/util/io/FileUtilities.scala
+++ b/util/io/FileUtilities.scala
@@ -13,6 +13,7 @@ import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputSt
import java.util.zip.{GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream}
import scala.collection.mutable.HashSet
import scala.reflect.{Manifest => SManifest}
+import Function.tupled
object FileUtilities
{
@@ -38,7 +39,7 @@ object FileUtilities
def toFile(url: URL) =
try { new File(url.toURI) }
catch { case _: URISyntaxException => new File(url.getPath) }
-
+
/** Converts the given URL to a File. If the URL is for an entry in a jar, the File for the jar is returned. */
def asFile(url: URL): File =
{
@@ -52,7 +53,8 @@ object FileUtilities
case _ => error("Invalid protocol " + url.getProtocol)
}
}
-
+ def assertDirectory(file: File) { assert(file.isDirectory, (if(file.exists) "Not a directory: " else "Directory not found: ") + file) }
+ def assertDirectory(file: File*) { file.foreach(assertDirectory) }
// "base.extension" -> (base, extension)
def split(name: String): (String, String) =
@@ -198,7 +200,8 @@ object FileUtilities
file.delete
}
}
- def listFiles(dir: File, filter: FileFilter): Array[File] = wrapNull(dir.listFiles(filter))
+ def listFiles(filter: java.io.FileFilter)(dir: File): Array[File] = wrapNull(dir.listFiles(filter))
+ def listFiles(dir: File, filter: java.io.FileFilter): Array[File] = wrapNull(dir.listFiles(filter))
def listFiles(dir: File): Array[File] = wrapNull(dir.listFiles())
private def wrapNull(a: Array[File]) =
{
@@ -210,23 +213,18 @@ object FileUtilities
/** Creates a jar file.
- * @param sources The files to include in the jar file.
+ * @param sources The files to include in the jar file paired with the entry name in the jar.
* @param outputJar The file to write the jar to.
- * @param manifest The manifest for the jar.
- * @param recursive If true, any directories in sources are recursively processed.
- * @param mapper The mapper that determines the name of a File in the jar. */
- def jar(sources: Iterable[File], outputJar: File, manifest: Manifest, recursive: Boolean, mapper: PathMapper): Unit =
- archive(sources, outputJar, Some(manifest), recursive, mapper)
+ * @param manifest The manifest for the jar.*/
+ def jar(sources: Iterable[(File,String)], outputJar: File, manifest: Manifest): Unit =
+ archive(sources, outputJar, Some(manifest))
/** Creates a zip file.
- * @param sources The files to include in the jar file.
- * @param outputZip The file to write the zip to.
- * @param recursive If true, any directories in sources are recursively processed. Otherwise,
- * they are not
- * @param mapper The mapper that determines the name of a File in the jar. */
- def zip(sources: Iterable[File], outputZip: File, recursive: Boolean, mapper: PathMapper): Unit =
- archive(sources, outputZip, None, recursive, mapper)
+ * @param sources The files to include in the zip file paired with the entry name in the zip.
+ * @param outputZip The file to write the zip to.*/
+ def zip(sources: Iterable[(File,String)], outputZip: File): Unit =
+ archive(sources, outputZip, None)
- private def archive(sources: Iterable[File], outputFile: File, manifest: Option[Manifest], recursive: Boolean, mapper: PathMapper)
+ private def archive(sources: Iterable[(File,String)], outputFile: File, manifest: Option[Manifest])
{
if(outputFile.isDirectory)
error("Specified output file " + outputFile + " is a directory.")
@@ -237,22 +235,18 @@ object FileUtilities
withZipOutput(outputFile, manifest)
{ output =>
val createEntry: (String => ZipEntry) = if(manifest.isDefined) new JarEntry(_) else new ZipEntry(_)
- writeZip(sources, output, recursive, mapper)(createEntry)
+ writeZip(sources, output)(createEntry)
}
}
}
- private def writeZip(sources: Iterable[File], output: ZipOutputStream, recursive: Boolean, mapper: PathMapper)(createEntry: String => ZipEntry)
+ private def writeZip(sources: Iterable[(File,String)], output: ZipOutputStream)(createEntry: String => ZipEntry)
{
- def add(sourceFile: File)
+ def add(sourceFile: File, name: String)
{
if(sourceFile.isDirectory)
- {
- if(recursive)
- listFiles(sourceFile).foreach(add)
- }
+ ()
else if(sourceFile.exists)
{
- val name = mapper(sourceFile)
val nextEntry = createEntry(name)
nextEntry.setTime(sourceFile.lastModified)
output.putNextEntry(nextEntry)
@@ -261,7 +255,7 @@ object FileUtilities
else
error("Source " + sourceFile + " does not exist.")
}
- sources.foreach(add)
+ sources.foreach(tupled(add))
output.closeEntry()
}
@@ -314,30 +308,20 @@ object FileUtilities
else
None
}
- def copy(sources: Iterable[File], destinationDirectory: File, mapper: PathMapper) =
+ def copy(sources: Iterable[(File,File)]): Set[File] = Set( sources.map(tupled(copyImpl)).toSeq.toArray : _*)
+ private def copyImpl(from: File, to: File): File =
{
- val targetSet = new scala.collection.mutable.HashSet[File]
- copyImpl(sources, destinationDirectory) { from =>
- val to = new File(destinationDirectory, mapper(from))
- targetSet += to
- if(!to.exists || from.lastModified > to.lastModified)
+ if(!to.exists || from.lastModified > to.lastModified)
+ {
+ if(from.isDirectory)
+ createDirectory(to)
+ else
{
- if(from.isDirectory)
- createDirectory(to)
- else
- {
- //log.debug("Copying " + source + " to " + toPath)
- copyFile(from, to)
- }
+ createDirectory(to.getParentFile)
+ copyFile(from, to)
}
}
- targetSet.readOnly
- }
- private def copyImpl(sources: Iterable[File], target: File)(doCopy: File => Unit)
- {
- if(!target.isDirectory)
- createDirectory(target)
- sources.toList.foreach(doCopy)
+ to
}
def copyFile(sourceFile: File, targetFile: File)
{
@@ -361,5 +345,4 @@ object FileUtilities
else
error("String cannot be encoded by charset " + charset.name)
}
-
}
diff --git a/util/io/PathMapper.scala b/util/io/PathMapper.scala
index eea4c37c1..2d04b5c2e 100644
--- a/util/io/PathMapper.scala
+++ b/util/io/PathMapper.scala
@@ -8,22 +8,27 @@ import java.io.File
trait PathMapper extends NotNull
{
def apply(file: File): String
+ def apply(files: Set[File]): Iterable[(File,String)] = files.projection.map(f => (f,apply(f)))
}
-class PMapper(f: File => String) extends PathMapper
+final case class RelativePathMapper(base: File) extends PMapper(file => FileUtilities.relativize(base, file).getOrElse(file.getPath))
+final case object BasicPathMapper extends PMapper(_.getPath)
+final case object FlatPathMapper extends PMapper(_.getName)
+class PMapper(val f: File => String) extends PathMapper
{
- def apply(file: File) = f(file)
+ def apply(file: File): String = f(file)
}
object PathMapper
{
- val basic = new PMapper(_.getPath)
- def relativeTo(base: File) = new PMapper(file => FileUtilities.relativize(base, file).getOrElse(file.getPath))
- val flat = new PMapper(_.getName)
- def apply(f: File => String) = new PMapper(f)
+ val basic: PathMapper = BasicPathMapper
+ def relativeTo(base: File): PathMapper = RelativePathMapper(base)
+ val flat = FlatPathMapper
+ def apply(f: File => String): PathMapper = new PMapper(f)
}
trait FileMapper extends NotNull
{
def apply(file: File): File
+ def apply(files: Set[File]): Iterable[(File,File)] = files.projection.map(f => (f,apply(f)))
}
class FMapper(f: File => File) extends FileMapper
{
diff --git a/util/io/Paths.scala b/util/io/Paths.scala
new file mode 100644
index 000000000..c4320d6fc
--- /dev/null
+++ b/util/io/Paths.scala
@@ -0,0 +1,44 @@
+package xsbt
+
+import java.io.File
+
+object Paths
+{
+ implicit def fileToPath(f: File): Path = new Path(f)
+ implicit def pathToFile(p: Path): File = p.asFile
+ implicit def filesToPaths(fs: Set[File]): Paths = new Paths(fs)
+ implicit def filesToPaths(fs: Iterable[File]): Paths =
+ fs match
+ {
+ case s: Set[File] => filesToPaths(s)
+ case _ => new Paths(Set(fs.toSeq : _*))
+ }
+}
+
+import Paths._
+trait PathBase extends NotNull
+{
+ def files: Set[File]
+ def x(mapper: PathMapper): Iterable[(File,String)] = mapper(files)
+ def x(mapper: FileMapper): Iterable[(File,File)] = mapper(files)
+ def *(filter: java.io.FileFilter): Set[File] = files.flatMap(FileUtilities.listFiles(filter))
+ def **(filter: java.io.FileFilter): Set[File] = files.filter(filter.accept) ++ files.flatMap(_ * AllPassFilter ** filter)
+ def *** = **(AllPassFilter)
+ def abs = files.map(_.getAbsoluteFile)
+ def descendentsExcept(include: java.io.FileFilter, intermediateExclude: java.io.FileFilter): Set[File] =
+ (this ** include) -- (this ** intermediateExclude ** include)
+}
+
+final class Paths(val files: Set[File]) extends PathBase
+{
+ def \(subPath: String) = /(subPath)
+ def /(subPath: String): Set[File] = files.flatMap(FileUtilities.listFiles)
+}
+final class Path(val asFile: File) extends PathBase
+{
+ def files = Set(asFile)
+ def \(subPath: String) = /(subPath)
+ def /(subPath: String) = new File(asFile, subPath.replace('/', File.separatorChar).replace('\\', File.separatorChar))
+ def ++(files: Set[File]) = files + asFile
+ def ++(file: File) = Set(file, asFile)
+}
\ No newline at end of file