diff --git a/cache/Cache.scala b/cache/Cache.scala index 90677f29a..d6f15290a 100644 --- a/cache/Cache.scala +++ b/cache/Cache.scala @@ -2,6 +2,7 @@ package xsbt import sbinary.{CollectionTypes, Format, JavaFormats, Operations} import java.io.File +import scala.reflect.Manifest trait Cache[I,O] { diff --git a/cache/DependencyTracking.scala b/cache/DependencyTracking.scala index bd14ef998..107c88fc8 100644 --- a/cache/DependencyTracking.scala +++ b/cache/DependencyTracking.scala @@ -1,138 +1,5 @@ package xsbt -import java.io.File -import CacheIO.{fromFile, toFile} -import sbinary.Format -import scala.reflect.Manifest - -trait Tracked extends NotNull -{ - def clear: Task[Unit] - def clean: Task[Unit] -} - -class Changed[O](val task: Task[O], val file: File)(implicit input: InputCache[O]) extends Tracked -{ - def clean = Task.empty - def clear = Clean(file) - def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): 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) - } - } -} -class Difference[F <: FileInfo](val filesTask: Task[Set[File]], val style: FilesInfo.Style[F], val cache: File, val shouldClean: Boolean)(implicit mf: Manifest[F]) extends Tracked -{ - def this(filesTask: Task[Set[File]], style: FilesInfo.Style[F], cache: File)(implicit mf: Manifest[F]) = this(filesTask, style, cache, false) - def this(files: Set[File], style: FilesInfo.Style[F], cache: File, shouldClean: Boolean)(implicit mf: Manifest[F]) = this(Task(files), style, cache) - def this(files: Set[File], style: FilesInfo.Style[F], cache: File)(implicit mf: Manifest[F]) = this(Task(files), style, cache, false) - - val clear = Clean(cache) - val clean = if(shouldClean) cleanTask else Task.empty - def cleanTask = Clean(Task(raw(cachedFilesInfo))) - - private def cachedFilesInfo = fromFile(style.formats)(cache).files - private def raw(fs: Set[F]): Set[File] = fs.map(_.file) - - def apply[T](f: ChangeReport[File] => Task[T]): Task[T] = - filesTask bind { files => - val lastFilesInfo = cachedFilesInfo - val lastFiles = raw(lastFilesInfo) - val currentFiles = files.map(_.getAbsoluteFile) - val currentFilesInfo = style(files) - - val report = new ChangeReport[File] - { - lazy val allInputs = currentFiles - lazy val removed = lastFiles -- allInputs - lazy val added = allInputs -- lastFiles - lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) - lazy val unmodified = allInputs -- modified - } - - f(report) map { result => - toFile(style.formats)(currentFilesInfo)(cache) - result - } - } -} -object InvalidateFiles -{ - def apply(cacheDirectory: File): Invalidate[File] = apply(cacheDirectory, true) - def apply(cacheDirectory: File, translateProducts: Boolean): Invalidate[File] = - { - import sbinary.DefaultProtocol.FileFormat - new Invalidate[File](cacheDirectory, translateProducts, FileUtilities.delete) - } -} -class Invalidate[T](val cacheDirectory: File, val translateProducts: Boolean, cleanT: T => Unit) - (implicit format: Format[T], mf: Manifest[T]) extends Tracked -{ - def this(cacheDirectory: File, translateProducts: Boolean)(implicit format: Format[T], mf: Manifest[T]) = - this(cacheDirectory, translateProducts, x => ()) - - private val trackFormat = new TrackingFormat[T](cacheDirectory, translateProducts) - private def cleanAll(fs: Set[T]) = fs.foreach(cleanT) - - def clear = Clean(cacheDirectory) - def clean = Task(cleanAll(trackFormat.read.allProducts)) - 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 => - val tracker = trackFormat.read - def invalidatedBy(file: T) = tracker.products(file) ++ tracker.sources(file) ++ tracker.usedBy(file) ++ tracker.dependsOn(file) - - import scala.collection.mutable.HashSet - val invalidated = new HashSet[T] - val invalidatedProducts = new HashSet[T] - def invalidate(files: Iterable[T]): Unit = - for(file <- files if !invalidated(file)) - { - invalidated += file - if(!tracker.sources(file).isEmpty) invalidatedProducts += file - invalidate(invalidatedBy(file)) - } - - invalidate(changes.modified) - tracker.removeAll(invalidated) - - val report = new InvalidationReport[T] - { - val invalid = Set(invalidated.toSeq : _*) - val invalidProducts = Set(invalidatedProducts.toSeq : _*) - val valid = changes.unmodified -- invalid - } - cleanAll(report.invalidProducts) - - f(report, tracker) map { result => - trackFormat.write(tracker) - result - } - } - } -} -class BasicTracked[F <: FileInfo](filesTask: Task[Set[File]], style: FilesInfo.Style[F], cacheDirectory: File)(implicit mf: Manifest[F]) extends Tracked -{ - private val changed = new Difference(filesTask, style, new File(cacheDirectory, "files")) - private val invalidation = InvalidateFiles(cacheDirectory) - val clean = invalidation.clean - val clear = Clean(cacheDirectory) - - def apply[R](f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => Task[R]): Task[R] = - changed { sourceChanges => - invalidation(sourceChanges) { (report, tracking) => - f(sourceChanges, report, tracking) - } - } -} private object DependencyTracking { import scala.collection.mutable.{Set, HashMap, Map, MultiMap} @@ -150,12 +17,6 @@ trait UpdateTracking[T] extends NotNull def tag(source: T, t: Array[Byte]): Unit def read: ReadTracking[T] } -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)) -} import scala.collection.Set trait ReadTracking[T] extends NotNull { diff --git a/cache/FileInfo.scala b/cache/FileInfo.scala index 0271ad0ec..435201049 100644 --- a/cache/FileInfo.scala +++ b/cache/FileInfo.scala @@ -4,6 +4,7 @@ import java.io.{File, IOException} import sbinary.{DefaultProtocol, Format} import DefaultProtocol._ import Function.tupled +import scala.reflect.Manifest sealed trait FileInfo extends NotNull { @@ -25,30 +26,39 @@ private final case class FileHashModified(file: File, hash: List[Byte], lastModi object FileInfo { - sealed trait Style[F <: FileInfo] extends NotNull + sealed trait Style extends NotNull { + type F <: FileInfo implicit def apply(file: File): F implicit def unapply(info: F): File = info.file implicit val format: Format[F] + /*val manifest: Manifest[F] + def formatManifest: Manifest[Format[F]] = CacheIO.manifest[Format[F]]*/ import Cache._ implicit def infoInputCache: InputCache[File] = wrapInputCache[File,F] implicit def infoOutputCache: OutputCache[File] = wrapOutputCache[File,F] } - object full extends Style[HashModifiedFileInfo] + object full extends Style { + type F = HashModifiedFileInfo + //val manifest: Manifest[F] = CacheIO.manifest[HashModifiedFileInfo] implicit def apply(file: File): HashModifiedFileInfo = make(file, Hash(file).toList, file.lastModified) def make(file: File, hash: List[Byte], lastModified: Long): HashModifiedFileInfo = FileHashModified(file.getAbsoluteFile, hash, lastModified) implicit val format: Format[HashModifiedFileInfo] = wrap(f => (f.file, f.hash, f.lastModified), tupled(make _)) } - object hash extends Style[HashFileInfo] + object hash extends Style { + type F = HashFileInfo + //val manifest: Manifest[F] = CacheIO.manifest[HashFileInfo] implicit def apply(file: File): HashFileInfo = make(file, computeHash(file).toList) def make(file: File, hash: List[Byte]): HashFileInfo = FileHash(file.getAbsoluteFile, hash) implicit val format: Format[HashFileInfo] = wrap(f => (f.file, f.hash), tupled(make _)) private def computeHash(file: File) = try { Hash(file) } catch { case e: Exception => Nil } } - object lastModified extends Style[ModifiedFileInfo] + object lastModified extends Style { + type F = ModifiedFileInfo + //val manifest: Manifest[F] = CacheIO.manifest[ModifiedFileInfo] implicit def apply(file: File): ModifiedFileInfo = make(file, file.lastModified) def make(file: File, lastModified: Long): ModifiedFileInfo = FileModified(file.getAbsoluteFile, lastModified) implicit val format: Format[ModifiedFileInfo] = wrap(f => (f.file, f.lastModified), tupled(make _)) @@ -58,21 +68,27 @@ object FileInfo final case class FilesInfo[F <: FileInfo] private(files: Set[F]) extends NotNull object FilesInfo { - sealed trait Style[F <: FileInfo] extends NotNull + sealed trait Style extends NotNull { + val fileStyle: FileInfo.Style + type F = fileStyle.F + //def manifest: Manifest[F] = fileStyle.manifest 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]] + val manifest: Manifest[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] + private final class BasicStyle[FI <: FileInfo](val fileStyle: FileInfo.Style { type F = FI }) + (implicit val manifest: Manifest[Format[FilesInfo[FI]]]) extends Style { + private implicit val infoFormat: Format[FI] = fileStyle.format 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) - lazy val lastModified: Style[ModifiedFileInfo] = new BasicStyle(FileInfo.lastModified)(FileInfo.lastModified.format) + lazy val full: Style = new BasicStyle(FileInfo.full) + lazy val hash: Style = new BasicStyle(FileInfo.hash) + lazy val lastModified: Style = new BasicStyle(FileInfo.lastModified) } \ No newline at end of file diff --git a/cache/Tracked.scala b/cache/Tracked.scala new file mode 100644 index 000000000..530e292fc --- /dev/null +++ b/cache/Tracked.scala @@ -0,0 +1,141 @@ +package xsbt + +import java.io.File +import CacheIO.{fromFile, toFile} +import sbinary.Format +import scala.reflect.Manifest + +trait Tracked extends NotNull +{ + def clear: Task[Unit] + def clean: Task[Unit] +} +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 file: File)(implicit input: InputCache[O]) extends Tracked +{ + def clean = Task.empty + def clear = Clean(file) + def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): 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) + } + } +} +class Difference(val filesTask: Task[Set[File]], val style: FilesInfo.Style, val cache: File, val shouldClean: Boolean) extends Tracked +{ + def this(filesTask: Task[Set[File]], style: FilesInfo.Style, cache: File) = this(filesTask, style, cache, false) + def this(files: Set[File], style: FilesInfo.Style, cache: File, shouldClean: Boolean) = this(Task(files), style, cache) + def this(files: Set[File], style: FilesInfo.Style, cache: File) = this(Task(files), style, cache, false) + + val clear = Clean(cache) + val clean = if(shouldClean) cleanTask else Task.empty + def cleanTask = Clean(Task(raw(cachedFilesInfo))) + + private def cachedFilesInfo = fromFile(style.formats)(cache)(style.manifest).files + private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file) + + def apply[T](f: ChangeReport[File] => Task[T]): Task[T] = + filesTask bind { files => + val lastFilesInfo = cachedFilesInfo + val lastFiles = raw(lastFilesInfo) + val currentFiles = files.map(_.getAbsoluteFile) + val currentFilesInfo = style(files) + + val report = new ChangeReport[File] + { + lazy val allInputs = currentFiles + lazy val removed = lastFiles -- allInputs + lazy val added = allInputs -- lastFiles + lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) + lazy val unmodified = allInputs -- modified + } + + f(report) map { result => + toFile(style.formats)(currentFilesInfo)(cache)(style.manifest) + result + } + } +} +object InvalidateFiles +{ + def apply(cacheDirectory: File): Invalidate[File] = apply(cacheDirectory, true) + def apply(cacheDirectory: File, translateProducts: Boolean): Invalidate[File] = + { + import sbinary.DefaultProtocol.FileFormat + new Invalidate[File](cacheDirectory, translateProducts, FileUtilities.delete) + } +} +class Invalidate[T](val cacheDirectory: File, val translateProducts: Boolean, cleanT: T => Unit) + (implicit format: Format[T], mf: Manifest[T]) extends Tracked +{ + def this(cacheDirectory: File, translateProducts: Boolean)(implicit format: Format[T], mf: Manifest[T]) = + this(cacheDirectory, translateProducts, x => ()) + + private val trackFormat = new TrackingFormat[T](cacheDirectory, translateProducts) + private def cleanAll(fs: Set[T]) = fs.foreach(cleanT) + + def clear = Clean(cacheDirectory) + def clean = Task(cleanAll(trackFormat.read.allProducts)) + 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 => + val tracker = trackFormat.read + def invalidatedBy(file: T) = tracker.products(file) ++ tracker.sources(file) ++ tracker.usedBy(file) ++ tracker.dependsOn(file) + + import scala.collection.mutable.HashSet + val invalidated = new HashSet[T] + val invalidatedProducts = new HashSet[T] + def invalidate(files: Iterable[T]): Unit = + for(file <- files if !invalidated(file)) + { + invalidated += file + if(!tracker.sources(file).isEmpty) invalidatedProducts += file + invalidate(invalidatedBy(file)) + } + + invalidate(changes.modified) + tracker.removeAll(invalidated) + + val report = new InvalidationReport[T] + { + val invalid = Set(invalidated.toSeq : _*) + val invalidProducts = Set(invalidatedProducts.toSeq : _*) + val valid = changes.unmodified -- invalid + } + cleanAll(report.invalidProducts) + + f(report, tracker) map { result => + trackFormat.write(tracker) + result + } + } + } +} +class BasicTracked(filesTask: Task[Set[File]], style: FilesInfo.Style, cacheDirectory: File) extends Tracked +{ + private val changed = new Difference(filesTask, style, new File(cacheDirectory, "files")) + private val invalidation = InvalidateFiles(cacheDirectory) + val clean = invalidation.clean + val clear = Clean(cacheDirectory) + + def apply[R](f: (ChangeReport[File], InvalidationReport[File], UpdateTracking[File]) => Task[R]): Task[R] = + changed { sourceChanges => + invalidation(sourceChanges) { (report, tracking) => + f(sourceChanges, report, tracking) + } + } +} \ No newline at end of file diff --git a/tasks/standard/Sync.scala b/tasks/standard/Sync.scala index 937163e28..e6f212ffb 100644 --- a/tasks/standard/Sync.scala +++ b/tasks/standard/Sync.scala @@ -1,25 +1,33 @@ package xsbt -import java.io.File -import Task._ + import java.io.File + import scala.reflect.Manifest + import Task._ object Sync { def sources(inputDirectory: Task[File], outputDirectory: Task[File]) = { - import Paths._ + import Paths._ (inputDirectory, outputDirectory) map { (in, out) => FileUtilities.assertDirectories(in, out) (in ***) x FileMapper.rebase(in, out) } } + def apply(inputDirectory: Task[File], outputDirectory: Task[File], cacheFile: File): Sync = + apply(sources(inputDirectory, outputDirectory), cacheFile) + def apply(inputDirectory: Task[File], outputDirectory: Task[File], style: FilesInfo.Style, cacheFile: File): Sync = + apply(sources(inputDirectory, outputDirectory), style, cacheFile) + def apply(sources: Task[Iterable[(File,File)]], cacheDirectory: File): Sync = + apply(sources, FilesInfo.hash, cacheDirectory) + def apply(sources: Task[Iterable[(File,File)]], style: FilesInfo.Style, cacheDirectory: File): Sync = + new Sync(sources, style, cacheDirectory) } -class Sync(val sources: Task[Iterable[(File,File)]], val cacheDirectory: File) extends TrackedTaskDefinition[Set[File]] +class Sync(val sources: Task[Iterable[(File,File)]], val style: FilesInfo.Style, val cacheDirectory: File) extends TrackedTaskDefinition[Set[File]] { - val tracking = new BasicTracked(sources.map(Set() ++ _.map(_._1)), FilesInfo.hash, cacheFile("sources")) + val tracking = new BasicTracked(sources.map(Set() ++ _.map(_._1)), style, cacheFile("sources")) val tracked = Seq(tracking) - def this(inputDirectory: Task[File], outputDirectory: Task[File], cacheFile: File) = this(Sync.sources(inputDirectory, outputDirectory), cacheFile) lazy val task = sources bind { srcs => val sourcesTargets = srcs.toSeq