Fix run's support of directories in the classpath

Fixes #3504
This commit is contained in:
Dale Wijnand 2017-09-12 14:30:24 +01:00
parent 53e41a0045
commit 103e40882f
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
5 changed files with 52 additions and 12 deletions

View File

@ -2,15 +2,17 @@ package sbt
package internal
import java.util.concurrent.atomic.AtomicLong
import java.io.Closeable
import Def.{ ScopedKey, Setting, Classpath }
import java.io.{ Closeable, File, FileInputStream, IOException }
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.{ FileVisitResult, Files, Path, SimpleFileVisitor }
import java.security.{ DigestInputStream, MessageDigest }
import Def.{ Classpath, ScopedKey, Setting }
import scala.concurrent.ExecutionContext
import scala.util.Try
import Scope.GlobalScope
import java.io.File
import sbt.io.{ IO, Hash }
import sbt.io.{ Hash, IO }
import sbt.io.syntax._
import sbt.util.{ Logger, LogExchange }
import sbt.util.{ LogExchange, Logger }
import sbt.internal.util.{ Attributed, ManagedLogger }
/**
@ -167,20 +169,25 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
override def toString(): String = s"BackgroundJobService(jobs=${jobs.map(_.id).mkString})"
/**
* Copies products to the workind directory, and the rest to the serviceTempDir of this service,
* Copies products to the working directory, and the rest to the serviceTempDir of this service,
* both wrapped in SHA-1 hash of the file contents.
* This is intended to mimize the file copying and accumulation of the unused JAR file.
* This is intended to minimize the file copying and accumulation of the unused JAR file.
* Since working directory is wiped out when the background job ends, the product JAR is deleted too.
* Meanwhile, the rest of the dependencies are cached for the duration of this service.
*/
override def copyClasspath(products: Classpath,
full: Classpath,
workingDirectory: File): Classpath = {
override def copyClasspath(
products: Classpath,
full: Classpath,
workingDirectory: File
): Classpath = {
def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] = {
val source = source0.data
val hash8 = Hash.toHex(Hash(source)).take(8)
val hash8 = Hash.toHex(hash(source)).take(8)
val dest = dir / hash8 / source.getName
if (!dest.exists) { IO.copyFile(source, dest) }
if (!dest.exists) {
if (source.isDirectory) IO.copyDirectory(source, dest)
else IO.copyFile(source, dest)
}
Attributed.blank(dest)
}
val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++
@ -188,6 +195,27 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
Thread.sleep(100)
xs
}
/** An alternative to sbt.io.Hash that handles java.io.File being a directory. */
private def hash(f: File) = {
val digest = MessageDigest.getInstance("SHA")
val buffer = new Array[Byte](8192)
Files.walkFileTree(
f.toPath,
new SimpleFileVisitor[Path]() {
override def visitFile(file: Path, attrs: BasicFileAttributes) = {
val dis = new DigestInputStream(new FileInputStream(file.toFile), digest)
try {
while (dis.read(buffer) >= 0) ()
FileVisitResult.CONTINUE
} catch {
case _: IOException => FileVisitResult.TERMINATE
} finally dis.close()
}
}
)
digest.digest
}
}
private[sbt] object BackgroundThreadPool {

View File

@ -0,0 +1,9 @@
package t
import java.nio._, charset._, file._
object Main {
def main(args: Array[String]): Unit = {
println(new String(Files.readAllBytes(Paths.get(getClass().getResource("/a.txt").toURI()))))
}
}

View File

@ -0,0 +1 @@
externalDependencyClasspath in Runtime += file("conf")

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1 @@
> run