diff --git a/build.sbt b/build.sbt index 8ac5ce87..4559b277 100644 --- a/build.sbt +++ b/build.sbt @@ -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") ) @@ -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 } -) diff --git a/io/src/main/contraband-scala/sbt/io/CopyOptions.scala b/io/src/main/contraband-scala/sbt/io/CopyOptions.scala new file mode 100644 index 00000000..5a7fda40 --- /dev/null +++ b/io/src/main/contraband-scala/sbt/io/CopyOptions.scala @@ -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) +} diff --git a/io/src/main/contraband/io.contra b/io/src/main/contraband/io.contra new file mode 100644 index 00000000..2ec3134b --- /dev/null +++ b/io/src/main/contraband/io.contra @@ -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") + +} diff --git a/io/src/main/scala/sbt/io/IO.scala b/io/src/main/scala/sbt/io/IO.scala index 1d8fdba3..b4fcafb7 100644 --- a/io/src/main/scala/sbt/io/IO.scala +++ b/io/src/main/scala/sbt/io/IO.scala @@ -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.") @@ -639,7 +673,12 @@ 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 @@ -647,6 +686,13 @@ object IO { // 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 diff --git a/project/house.sbt b/project/house.sbt deleted file mode 100644 index a447518e..00000000 --- a/project/house.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.3") diff --git a/project/nightlies.sbt b/project/nightlies.sbt deleted file mode 100644 index 02b4628a..00000000 --- a/project/nightlies.sbt +++ /dev/null @@ -1 +0,0 @@ -resolvers += Resolver.url("bintray-sbt-ivy-releases", url("https://dl.bintray.com/sbt/ivy-snapshots"))(Resolver.ivyStylePatterns) diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 00000000..47fb6857 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.3") +addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0-M6")