From 94c068d2b979c49a23e1b1c87a660b6f364c4f59 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 28 Oct 2010 21:52:25 -0400 Subject: [PATCH] modified version of patch from Viktor and Ross --- sbt/LICENSE | 2 +- sbt/NOTICE | 2 +- sbt/src/main/scala/sbt/FileUtilities.scala | 85 +++++++++++++++++----- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/sbt/LICENSE b/sbt/LICENSE index 98023c493e..d4bf6b28f0 100644 --- a/sbt/LICENSE +++ b/sbt/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, David MacIver, Mikko Peltonen, Tony Sloane, Seth Tisue, Vesa Vilhonen +Copyright (c) 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, Viktor Klang, David MacIver, Ross McDonald, Mikko Peltonen, Tony Sloane, Seth Tisue, Vesa Vilhonen All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/sbt/NOTICE b/sbt/NOTICE index 7d09e81616..77b40ec4b5 100644 --- a/sbt/NOTICE +++ b/sbt/NOTICE @@ -1,5 +1,5 @@ Simple Build Tool (sbt) -Copyright 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, David MacIver, Mikko Peltonen, Tony Sloane, Vesa Vilhonen +Copyright 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, Viktor Klang, David MacIver, Ross McDonald, Mikko Peltonen, Tony Sloane, Seth Tisue, Vesa Vilhonen Licensed under BSD-style license (see LICENSE) diff --git a/sbt/src/main/scala/sbt/FileUtilities.scala b/sbt/src/main/scala/sbt/FileUtilities.scala index 6ab479ff24..3faf5e8b78 100644 --- a/sbt/src/main/scala/sbt/FileUtilities.scala +++ b/sbt/src/main/scala/sbt/FileUtilities.scala @@ -1,5 +1,5 @@ /* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah, Nathan Hamblen, Justin Caballero + * Copyright 2008, 2009, 2010 Mark Harrah, Nathan Hamblen, Justin Caballero, Viktor Klang, Ross McDonald */ package sbt @@ -11,7 +11,9 @@ import java.net.{URL, URISyntaxException} import java.nio.charset.{Charset, CharsetDecoder, CharsetEncoder} import java.nio.channels.FileChannel import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest} -import java.util.zip.{GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream} +import java.util.zip.{CRC32, GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream} + +import scala.collection.immutable.TreeSet import OpenResource._ @@ -132,31 +134,78 @@ object FileUtilities private def writeZip(sources: Iterable[Path], output: ZipOutputStream, recursive: Boolean, log: Logger)(createEntry: String => ZipEntry) = { - def add(source: Path) + import Path.{lazyPathFinder => pf} + // Expand if recursive and filter directories, which will be handled automatically + val inputs = if(recursive) (pf(sources) ***).get else sources + val files = inputs.filter(!_.isDirectory) + val now = System.currentTimeMillis + // The CRC32 for an empty value, needed to store directories in zip files + val emptyCRC = new CRC32().getValue() + + def addDirectoryEntry(relativePath: String) { - val sourceFile = source.asFile - if(sourceFile.isDirectory) - { - if(recursive) - wrapNull(sourceFile.listFiles).foreach(file => add(source / file.getName)) - } - else if(sourceFile.exists) + output putNextEntry makeDirectoryEntry(relativePath) + output.closeEntry() + } + + def makeDirectoryEntry(relativePath: String) = + { + log.debug("\tAdding directory " + relativePath + " ...") + val e = createEntry(relativePath) + e setTime now + e setSize 0 + e setMethod ZipEntry.STORED + e setCrc emptyCRC + e + } + + def makeFileEntry(path: Path) = + { + val relativePath = path.relativePathString("/") + log.debug("\tAdding " + path + " as " + relativePath + " ...") + + val e = createEntry(relativePath) + e setTime path.lastModified + e + } + def addFileEntry(path: Path) + { + val file = path.asFile + if(file.exists) { - val relativePath = source.relativePathString("/") - log.debug("\tAdding " + source + " as " + relativePath + " ...") - val nextEntry = createEntry(relativePath) - nextEntry.setTime(sourceFile.lastModified) - output.putNextEntry(nextEntry) - transferAndClose(new FileInputStream(sourceFile), output, log) + output putNextEntry makeFileEntry(path) + transferAndClose(new FileInputStream(file), output, log) output.closeEntry() } else - log.warn("\tSource " + source + " does not exist.") + log.warn("\tFile " + file + " does not exist.") } - sources.foreach(add) + + //Calculate directories and add them to the generated Zip + allDirectoryPaths(files) foreach addDirectoryEntry + + //Add all files to the generated Zip + files foreach addFileEntry + None } + // map a path a/b/c to List("a", "b") + private def relativeComponents(path: Path): List[String] = + path.relativePathString("/").split("/").toList.dropRight(1) + + // map components List("a", "b", "c") to List("a/b/c/", "a/b/", "a/", "") + private def directories(path: List[String]): List[String] = + path.foldLeft(List(""))( (e,l) => (e.head + l + "/") :: e ) + + // map a path a/b/c to List("a/b/", "a/") + private def directoryPaths(path: Path): List[String] = + directories(relativeComponents(path)).filter(_.length > 1) + + // produce a sorted list of all the subdirectories of all provided files + private def allDirectoryPaths(files: Iterable[Path]) = + TreeSet[String]() ++ (files flatMap directoryPaths) + private def withZipOutput(file: File, manifest: Option[Manifest], log: Logger)(f: ZipOutputStream => Option[String]): Option[String] = { writeStream(file, log)