mirror of https://github.com/sbt/sbt.git
92 lines
3.4 KiB
Scala
92 lines
3.4 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2010 Mark Harrah
|
|
*/
|
|
package sbt
|
|
|
|
import java.io.File
|
|
|
|
/**
|
|
Maintains a set of mappings so that they are uptodate.
|
|
Specifically, 'apply' applies the mappings by creating target directories and copying source files to their destination.
|
|
For each mapping no longer present, the old target is removed.
|
|
Caution: Existing files are overwritten.
|
|
Caution: The removal of old targets assumes that nothing else has written to or modified those files.
|
|
It tries not to obliterate large amounts of data by only removing previously tracked files and empty directories.
|
|
That is, it won't remove a directory with unknown (untracked) files in it.
|
|
Warning: It is therefore inappropriate to use this with anything other than an automatically managed destination or a dedicated target directory.
|
|
Warning: Specifically, don't mix this with a directory containing manually created files, like sources.
|
|
It is safe to use for its intended purpose: copying resources to a class output directory.
|
|
*/
|
|
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) // we don't want to update the last modified time of an existing directory
|
|
{
|
|
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)
|
|
}
|
|
}
|