Skip to content

Commit

Permalink
Merge pull request #53 from dwijnand/preserve-exec
Browse files Browse the repository at this point in the history
Add option preserveExecutable to IO.copyFile and IO.copyDirectory
  • Loading branch information
eed3si9n authored Jun 30, 2017
2 parents 177ba25 + c0fd090 commit 34c28cf
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 37 deletions.
13 changes: 4 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ def baseVersion: String = "1.0.0-M12"
def commonSettings: Seq[Setting[_]] = Seq(
scalaVersion := scala212,
javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"),
scalacOptions in Compile in compile += "-Xfatal-warnings",
scalacOptions --= ifScala210Minus("-Ywarn-unused").value.toList, // WORKAROUND sbt/sbt-houserules#14
scalacOptions --= ifScala210Minus("-Ywarn-unused-import").value.toList, // WORKAROUND sbt/sbt-houserules#14
crossScalaVersions := Seq(scala210, scala211, scala212, scala213),
// mimaPreviousArtifacts := Set.empty // Set(organization.value %% moduleName.value % "1.0.0")
)
Expand All @@ -29,14 +26,12 @@ lazy val ioRoot = (project in file(".")).
)

// Path, IO (formerly FileUtilities), NameFilter and other I/O utility classes
lazy val io = (project in file("io")).
settings(
val io = (project in file("io"))
.enablePlugins(ContrabandPlugin)
.settings(
commonSettings,
name := "IO",
libraryDependencies ++= Seq(scalaCompiler.value % Test, scalaCheck % Test, scalatest % Test),
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
initialCommands in console += "\nimport sbt.io._, syntax._"
)

def ifScala210Minus[T](x: T) = Def.setting(
CrossVersion partialVersion scalaVersion.value collect { case (2, v) if v <= 10 => x }
)
50 changes: 50 additions & 0 deletions io/src/main/contraband-scala/sbt/io/CopyOptions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/

// DO NOT EDIT MANUALLY
package sbt.io
/** The options for the copy operation in `IO`. */
final class CopyOptions private (
/**
* A source file is always copied if `overwrite` is true.
* If `overwrite` is false, the source is only copied if the target is missing or is older than the
* source file according to last modified times.
* If the source is a directory, the corresponding directory is created.
*/
val overwrite: Boolean,
/** If `true` the last modified times are copied. */
val preserveLastModified: Boolean,
/** If `true` the executable properties are copied. */
val preserveExecutable: Boolean) extends Serializable {

private def this() = this(false, false, true)

override def equals(o: Any): Boolean = o match {
case x: CopyOptions => (this.overwrite == x.overwrite) && (this.preserveLastModified == x.preserveLastModified) && (this.preserveExecutable == x.preserveExecutable)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (17 + "CopyOptions".##) + overwrite.##) + preserveLastModified.##) + preserveExecutable.##)
}
override def toString: String = {
"CopyOptions(" + overwrite + ", " + preserveLastModified + ", " + preserveExecutable + ")"
}
protected[this] def copy(overwrite: Boolean = overwrite, preserveLastModified: Boolean = preserveLastModified, preserveExecutable: Boolean = preserveExecutable): CopyOptions = {
new CopyOptions(overwrite, preserveLastModified, preserveExecutable)
}
def withOverwrite(overwrite: Boolean): CopyOptions = {
copy(overwrite = overwrite)
}
def withPreserveLastModified(preserveLastModified: Boolean): CopyOptions = {
copy(preserveLastModified = preserveLastModified)
}
def withPreserveExecutable(preserveExecutable: Boolean): CopyOptions = {
copy(preserveExecutable = preserveExecutable)
}
}
object CopyOptions {

def apply(): CopyOptions = new CopyOptions(false, false, true)
def apply(overwrite: Boolean, preserveLastModified: Boolean, preserveExecutable: Boolean): CopyOptions = new CopyOptions(overwrite, preserveLastModified, preserveExecutable)
}
19 changes: 19 additions & 0 deletions io/src/main/contraband/io.contra
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sbt.io
@target(Scala)

## The options for the copy operation in `IO`.
type CopyOptions {

## A source file is always copied if `overwrite` is true.
## If `overwrite` is false, the source is only copied if the target is missing or is older than the
## source file according to last modified times.
## If the source is a directory, the corresponding directory is created.
overwrite: boolean! = false @since("0.0.1")

## If `true` the last modified times are copied.
preserveLastModified: boolean! = false @since("0.0.1")

## If `true` the executable properties are copied.
preserveExecutable: boolean! = true @since("0.0.1")

}
98 changes: 72 additions & 26 deletions io/src/main/scala/sbt/io/IO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -570,52 +570,86 @@ object IO {
} else None
}

def copy(sources: Traversable[(File, File)]): Set[File] = copy(sources, CopyOptions())

/**
* For each pair in `sources`, copies the contents of the first File (the source) to the location of the second File (the target).
* For each pair in `sources`, copies the contents of the first File (the source) to the location
* of the second File (the target).
*
* A source file is always copied if `overwrite` is true.
* If `overwrite` is false, the source is only copied if the target is missing or is older than the source file according to last modified times.
* If the source is a directory, the corresponding directory is created.
* See [[sbt.io.CopyOptions]] for docs on the options available.
*
* If `preserveLastModified` is `true`, the last modified times are transferred as well.
* Any parent directories that do not exist are created.
* The set of all target files is returned, whether or not they were updated by this method.
*/
def copy(sources: Traversable[(File, File)], overwrite: Boolean = false, preserveLastModified: Boolean = false): Set[File] =
sources.map(tupled(copyImpl(overwrite, preserveLastModified))).toSet

private def copyImpl(overwrite: Boolean, preserveLastModified: Boolean)(from: File, to: File): File =
{
if (overwrite || !to.exists || from.lastModified > to.lastModified) {
if (from.isDirectory)
createDirectory(to)
else {
createDirectory(to.getParentFile)
copyFile(from, to, preserveLastModified)
}
def copy(sources: Traversable[(File, File)], options: CopyOptions): Set[File] =
copy(sources, options.overwrite, options.preserveLastModified, options.preserveExecutable)

def copy(
sources: Traversable[(File, File)],
overwrite: Boolean,
preserveLastModified: Boolean,
preserveExecutable: Boolean
): Set[File] =
sources.map(tupled(copyImpl(overwrite, preserveLastModified, preserveExecutable))).toSet

private def copyImpl(overwrite: Boolean, preserveLastModified: Boolean, preserveExecutable: Boolean)(
from: File,
to: File
): File = {
if (overwrite || !to.exists || from.lastModified > to.lastModified) {
if (from.isDirectory)
createDirectory(to)
else {
createDirectory(to.getParentFile)
copyFile(from, to, preserveLastModified, preserveExecutable)
}
to
}
to
}

def copyDirectory(source: File, target: File): Unit = copyDirectory(source, target, CopyOptions())

/**
* Copies the contents of each file in the `source` directory to the corresponding file in the `target` directory.
* A source file is always copied if `overwrite` is true.
* If `overwrite` is false, the source is only copied if the target is missing or is older than the source file according to last modified times.
* Copies the contents of each file in the `source` directory to the corresponding file in the
* `target` directory.
*
* See [[sbt.io.CopyOptions]] for docs on the options available.
*
* Files in `target` without a corresponding file in `source` are left unmodified in any case.
* If `preserveLastModified` is `true`, the last modified times are transferred as well.
* Any parent directories that do not exist are created.
*/
def copyDirectory(source: File, target: File, overwrite: Boolean = false, preserveLastModified: Boolean = false): Unit = {
def copyDirectory(source: File, target: File, options: CopyOptions): Unit =
copyDirectory(source, target, options.overwrite, options.preserveLastModified, options.preserveExecutable)

def copyDirectory(
source: File,
target: File,
overwrite: Boolean = false,
preserveLastModified: Boolean = false,
preserveExecutable: Boolean = true
): Unit = {
val sources = PathFinder(source).allPaths pair Path.rebase(source, target)
copy(sources, overwrite, preserveLastModified)
copy(sources, overwrite, preserveLastModified, preserveExecutable)
()
}

def copyFile(sourceFile: File, targetFile: File): Unit =
copyFile(sourceFile, targetFile, CopyOptions())

/**
* Copies the contents of `sourceFile` to the location of `targetFile`, overwriting any existing content.
* If `preserveLastModified` is `true`, the last modified time is transferred as well.
*
* See [[sbt.io.CopyOptions]] for docs on the options available.
*/
def copyFile(sourceFile: File, targetFile: File, preserveLastModified: Boolean = false): Unit = {
def copyFile(sourceFile: File, targetFile: File, options: CopyOptions): Unit =
copyFile(sourceFile, targetFile, options.preserveLastModified, options.preserveExecutable)

def copyFile(
sourceFile: File,
targetFile: File,
preserveLastModified: Boolean = false,
preserveExecutable: Boolean = true
): Unit = {
// NOTE: when modifying this code, test with larger values of CopySpec.MaxFileSizeBits than default

require(sourceFile.exists, "Source file '" + sourceFile.getAbsolutePath + "' does not exist.")
Expand All @@ -639,14 +673,26 @@ object IO {
copyLastModified(sourceFile, targetFile)
()
}
if (preserveExecutable) {
copyExecutable(sourceFile, targetFile)
()
}
}

/** Transfers the last modified time of `sourceFile` to `targetFile`. */
def copyLastModified(sourceFile: File, targetFile: File) = {
val last = sourceFile.lastModified
// lastModified can return a negative number, but setLastModified doesn't accept it
// see Java bug #6791812
targetFile.setLastModified(math.max(last, 0L))
}

/** Transfers the executable property of `sourceFile` to `targetFile`. */
def copyExecutable(sourceFile: File, targetFile: File) = {
val executable = sourceFile.canExecute
if (executable) targetFile.setExecutable(true)
}

/** The default Charset used when not specified: UTF-8. */
def defaultCharset = utf8

Expand Down
1 change: 0 additions & 1 deletion project/house.sbt

This file was deleted.

1 change: 0 additions & 1 deletion project/nightlies.sbt

This file was deleted.

2 changes: 2 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.3")
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0-M6")

0 comments on commit 34c28cf

Please sign in to comment.