package sbt
import java.io.File
object Sync
{
def apply(cacheFile: File, inStyle: FileInfo.Style = FileInfo.lastModified, outStyle: FileInfo.Style = FileInfo.exists): Traversable[(File,File)] => Relation[File,File] =
mappings =>
{
val relation = Relation.empty ++ mappings
noDuplicateTargets(relation)
val currentInfo = relation._1s.map(s => (s, inStyle(s)) ).toMap
val (previousRelation, previousInfo) = readInfo(cacheFile)(inStyle.format)
val removeTargets = previousRelation._2s -- relation._2s
def outofdate(source: File, target: File): Boolean =
!previousRelation.contains(source, target) ||
(previousInfo get source) != (currentInfo get source) ||
!target.exists ||
target.isDirectory != source.isDirectory
val updates = relation filter outofdate
val (cleanDirs, cleanFiles) = (updates._2s ++ removeTargets).partition(_.isDirectory)
IO.delete(cleanFiles)
IO.deleteIfEmpty(cleanDirs)
updates.all.foreach((copy _).tupled)
writeInfo(cacheFile, relation, currentInfo)(inStyle.format)
relation
}
def copy(source: File, target: File): Unit =
if(source.isFile)
IO.copyFile(source, target, true)
else if(!target.exists)
{
IO.createDirectory(target)
IO.copyLastModified(source, target)
}
def noDuplicateTargets(relation: Relation[File, File])
{
val dups = relation.reverseMap.filter { case (target, srcs) =>
srcs.size >= 2 && srcs.exists(!_.isDirectory)
} map { case (target, srcs) =>
"\n\t" + target + "\nfrom\n\t" + srcs.mkString("\n\t\t")
}
if(!dups.isEmpty)
error("Duplicate mappings:" + dups.mkString)
}
import java.io.{File, IOException}
import sbinary._
import Operations.{read, write}
import DefaultProtocol.{FileFormat => _, _}
import inc.AnalysisFormats._
def writeInfo[F <: FileInfo](file: File, relation: Relation[File, File], info: Map[File, F])(implicit infoFormat: Format[F]): Unit =
IO.gzipFileOut(file) { out =>
write(out, (relation, info) )
}
type RelationInfo[F] = (Relation[File,File], Map[File, F])
def readInfo[F <: FileInfo](file: File)(implicit infoFormat: Format[F]): RelationInfo[F] =
try { readUncaught(file)(infoFormat) }
catch { case e: IOException => (Relation.empty, Map.empty) }
def readUncaught[F <: FileInfo](file: File)(implicit infoFormat: Format[F]): RelationInfo[F] =
IO.gzipFileIn(file) { in =>
read[RelationInfo[F]](in)
}
}