From e5961909c0775682d840aa18baabea9755c82822 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Wed, 14 Dec 2011 17:44:51 -0500 Subject: Native SHA1 calculations. * SHA1 sum calculations are now done in pure Scala. * Cache jar SHAs are checked for validity. --- .gitignore | 4 ++++ project/ShaResolve.scala | 60 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d392f0e82c..8b8b6978b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ *.jar +*.obj +*.log +*~ +target/ diff --git a/project/ShaResolve.scala b/project/ShaResolve.scala index 5cc99fd9cf..f54e96c0c6 100644 --- a/project/ShaResolve.scala +++ b/project/ShaResolve.scala @@ -5,7 +5,7 @@ import Keys._ import Project.Initialize import scala.collection.{ mutable, immutable } import scala.collection.parallel.CompositeThrowable - +import java.security.MessageDigest /** Helpers to resolve SHA artifacts from typesafe repo. */ @@ -30,29 +30,73 @@ object ShaResolve { jar = dir / uri if !jar.exists || !isValidSha(file) sha = getShaFromShafile(file) - } pullFile(jar, sha + "/" + uri, cacheDir, s) + } pullFile(jar, sha + "/" + uri, cacheDir, sha, s) } @inline final def loggingParallelExceptions[U](s: TaskStreams)(f: => U): U = try f catch { case t: CompositeThrowable => - s.log.error("Error during parallel execution, GET READ FOR STACK TRACES!!") + s.log.error("Error during parallel execution, GET READY FOR STACK TRACES!!") t.throwables foreach (t2 => s.log.trace(t2)) throw t } - def getShaFromShafile(file: File): String = (IO read file split "\\s" headOption) getOrElse error("No SHA found for " + file) + def getShaFromShafile(file: File): String = parseShaFile(file)._2 + + // This should calculate the SHA sum of a file the same as the linux process. + def calculateSha(file: File): String = { + val digest = MessageDigest.getInstance("SHA1") + val in = new java.io.FileInputStream(file); + val buffer = new Array[Byte](8192) + try { + def read(): Unit = in.read(buffer) match { + case x if x <= 0 => () + case size => digest.update(buffer, 0, size); read() + } + read() + } finally in.close() + val sha = convertToHex(digest.digest()) + sha + } + + // TODO - Prettier way of doing this... + private def convertToHex(data: Array[Byte]): String = { + val buf = new StringBuffer + for (i <- 0 until data.length) { + var halfbyte = (data(i) >>> 4) & 0x0F; + var two_halfs = 0; + while(two_halfs < 2) { + if ((0 <= halfbyte) && (halfbyte <= 9)) + buf.append(('0' + halfbyte).toChar) + else + buf.append(('a' + (halfbyte - 10)).toChar); + halfbyte = data(i) & 0x0F; + two_halfs += 1 + } + } + return buf.toString + } + // Parses a sha file into a file and a sha. + def parseShaFile(file: File): (File, String) = + IO.read(file).split("\\s") match { + case Array(sha, filename) if filename.startsWith("?") => (new File(file.getParentFile, filename.drop(1)), sha) + case Array(sha, filename) => (new File(file.getParentFile, filename), sha) + case _ => error(file.getAbsolutePath + " is an invalid sha file") + } + def isValidSha(file: File): Boolean = - try (Process(Seq("shasum", "-p", "--check", file.getAbsolutePath), Some(file.getParentFile)).!! contains "OK") - catch { + try { + val (jar, sha) = parseShaFile(file) + jar.exists && calculateSha(jar) == sha + } catch { case t: Exception => false } - def pullFile(file: File, uri: String, cacheDir: File, s: TaskStreams): Unit = { + def pullFile(file: File, uri: String, cacheDir: File, sha: String, s: TaskStreams): Unit = { val cachedFile = cacheDir / uri - if (!cachedFile.exists) { + if (!cachedFile.exists || calculateSha(cachedFile) != sha) { // Ensure the directory for the cache exists. cachedFile.getParentFile.mkdirs() val url = remote_urlbase + "/" + uri -- cgit v1.2.3