Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option preserveExecutable to IO.copyFile and IO.copyDirectory #53

Merged
merged 4 commits into from
Jun 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")