more 2.8 updates, launcher compiles and runs with 2.8

This commit is contained in:
Mark Harrah 2010-06-15 20:38:18 -04:00
parent 1585d805bd
commit b2077ce60c
32 changed files with 154 additions and 119 deletions

View File

@ -22,7 +22,7 @@ class CompilerArguments(scalaInstance: ScalaInstance, cp: ClasspathOptions) exte
def finishClasspath(classpath: Set[File]): Set[File] =
classpath ++ include(cp.compiler, scalaInstance.compilerJar) ++ include(cp.extra, scalaInstance.extraJars : _*)
private def include(flag: Boolean, jars: File*) = if(flag) jars else Nil
protected def abs(files: Set[File]) = files.map(_.getAbsolutePath).toList.sort(_ < _)
protected def abs(files: Set[File]) = files.map(_.getAbsolutePath).toList.sortWith(_ < _)
protected def checkScalaHomeUnset()
{
val scalaHome = System.getProperty("scala.home")

View File

@ -18,7 +18,7 @@ object ComponentCompiler
class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
{
import ComponentCompiler._
import FileUtilities.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
import sbt.IO.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
def apply(id: String): File =
try { getPrecompiled(id) }
catch { case _: InvalidComponent => getLocallyCompiled(id) }
@ -55,7 +55,6 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
val isSource = (f: File) => isSourceName(f.getName)
def keepIfSource(files: Set[File]): Set[File] = if(files.exists(isSource)) files else Set()
import Paths._
withTemporaryDirectory { dir =>
val extractedSources = (Set[File]() /: sourceJars) { (extracted, sourceJar)=> extracted ++ keepIfSource(unzip(sourceJar, dir)) }
val (sourceFiles, resources) = extractedSources.partition(isSource)
@ -69,8 +68,9 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager)
manager.log.info(" Compilation completed in " + (System.currentTimeMillis - start) / 1000.0 + " s")
}
catch { case e: xsbti.CompileFailed => throw new CompileFailed(e.arguments, "Error compiling sbt component '" + id + "'") }
copy(resources x (FileMapper.rebase(dir, outputDirectory)))
zip((outputDirectory ***) x (PathMapper.relativeTo(outputDirectory)), targetJar)
import sbt.Path._
copy(resources x rebase(dir, outputDirectory))
zip((outputDirectory ***) x relativeTo(outputDirectory), targetJar)
}
}
}

View File

@ -19,7 +19,7 @@ class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: C
log.debug("Plain interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader)
val process = mainClass.getMethod("process", classOf[Array[String]])
process.invoke(null, toJavaArray(arguments))
process.invoke(null, arguments.toArray)
checkForFailure(mainClass, arguments.toArray)
}
def compilerArguments = new CompilerArguments(scalaInstance, cp)
@ -29,12 +29,6 @@ class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: C
val failed = reporter.getClass.getMethod("hasErrors").invoke(reporter).asInstanceOf[Boolean]
if(failed) throw new CompileFailed(args, "Plain compile failed")
}
protected def toJavaArray(arguments: Seq[String]): Array[String] =
{
val realArray: Array[String] = arguments.toArray
assert(realArray.getClass eq classOf[Array[String]])
realArray
}
}
class CompileFailed(val arguments: Array[String], override val toString: String) extends xsbti.CompileFailed
{

View File

@ -48,7 +48,7 @@ object SameAPI
}
def separateDefinitions(s: Seq[Definition]): (Seq[Definition], Seq[Definition]) =
s.toArray.partition(isValueDefinition)
s.partition(isValueDefinition)
def isValueDefinition(d: Definition): Boolean =
d match
{
@ -59,12 +59,12 @@ object SameAPI
def isValue(d: DefinitionType): Boolean =
d == DefinitionType.Module || d == DefinitionType.PackageModule
/** Puts the given definitions in a map according to their names.*/
def byName(s: Seq[Definition]): scala.collection.Map[String, List[Definition]] =
def byName(s: Seq[Definition]): Map[String, List[Definition]] =
{
val map = new mutable.HashMap[String, List[Definition]]
var map = Map[String, List[Definition]]()
for(d <- s; name = d.name)
map(name) = d :: map.getOrElse(name, Nil)
map.readOnly
map = map.updated(name, d :: map.getOrElse(name, Nil) )
map
}
}
/** Used to implement API equality. All comparisons must be done between constructs in source files `a` and `b`. For example, when doing:
@ -358,7 +358,5 @@ private class SameAPI(a: Source, b: Source, includePrivate: Boolean)
def sameStrings(a: scala.collection.Set[String], b: scala.collection.Set[String]): Boolean =
a == b
final def sameSeq[T](a: Seq[T], b: Seq[T])(eq: (T,T) => Boolean): Boolean =
sameArray(a.toArray, b.toArray)(eq)
final def sameArray[T](a: Array[T], b: Array[T])(eq: (T,T) => Boolean): Boolean =
(a.length == b.length) && (a zip b).forall(tupled(eq))
}

View File

@ -9,13 +9,16 @@ import io.{AbstractFile, PlainFile, ZipArchive}
import plugins.{Plugin, PluginComponent}
import symtab.Flags
import scala.collection.mutable.{HashMap, HashSet, ListBuffer}
//import xsbti.api.{ClassLike, DefinitionType, PathComponent, SimpleType}
import xsbti.api.{ClassLike, DefinitionType, PathComponent, SimpleType}
object API
{
val name = "xsbt-api"
// for 2.7 compatibility: this class was removed in 2.8
type ImplicitMethodType = AnyRef
}
final class API(val global: Global, val callback: xsbti.AnalysisCallback) extends NotNull
import API._ // imports ImplicitMethodType, which will preserve source compatibility in 2.7 for defDef
final class API(val global: Global, val callback: xsbti.AnalysisCallback) extends Compat
{
import global._
def error(msg: String) = throw new RuntimeException(msg)
@ -35,7 +38,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
println("API phase took : " + ((stop - start)/1000.0) + " s")
}
}
/*def processUnit(unit: CompilationUnit)
def processUnit(unit: CompilationUnit)
{
val sourceFile = unit.source.file.file
val traverser = new TopLevelHandler(sourceFile)
@ -43,9 +46,9 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
val packages = traverser.packages.toArray[String].map(p => new xsbti.api.Package(p))
val source = new xsbti.api.Source(packages, traverser.definitions.toArray[xsbti.api.Definition])
callback.api(sourceFile, source)
}*/
}
}
/*private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil))
private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil))
private def path(components: List[PathComponent]) = new xsbti.api.Path(components.toArray[PathComponent])
private def pathComponents(sym: Symbol, postfix: List[PathComponent]): List[PathComponent] =
{
@ -64,7 +67,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
if(pre == NoPrefix)
{
if(sym.isLocalClass) Constants.emptyType
else if(sym.isTypeParameterOrSkolem || sym.isExistential) new xsbti.api.ParameterRef(sym.id)
else if(sym.isTypeParameterOrSkolem || isExistential(sym)) new xsbti.api.ParameterRef(sym.id)
else error("Unknown prefixless type: " + sym)
}
else if(sym.isRoot || sym.isRootPackage) Constants.emptyType
@ -178,7 +181,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
{
if(sym.isClass) classLike(sym)
else if(sym.isMethod) defDef(sym)
else if(sym.isTypeMember) typeDef(sym)
else if(isNonClassType(sym)) typeDef(sym)
else if(sym.isVariable) fieldDef(sym, new xsbti.api.Var(_,_,_,_,_))
else fieldDef(sym, new xsbti.api.Val(_,_,_,_,_))
}
@ -316,6 +319,6 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
val annots = at.attributes
if(annots.isEmpty) processType(at.underlying) else annotated(annots, at.underlying)
}
private def fullName(s: Symbol): String = s.fullNameString
private def simpleName(s: Symbol): String = s.simpleName.toString.trim*/
private def fullName(s: Symbol): String = nameString(s)
private def simpleName(s: Symbol): String = s.simpleName.toString.trim
}

View File

@ -17,10 +17,9 @@ object Analyzer
{
def name = "xsbt-analyzer"
}
final class Analyzer(val global: Global, val callback: AnalysisCallback) extends NotNull
final class Analyzer(val global: Global, val callback: AnalysisCallback) extends Compat
{
import global._
import Compat.{archive, hasAnnotation, linkedClass, nameString}
def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev)
private class AnalyzerPhase(prev: Phase) extends Phase(prev)
@ -216,35 +215,42 @@ final class Analyzer(val global: Global, val callback: AnalysisCallback) extends
if(entry eq null) None else Some(entry.classFile)
}
}
private object Compat
}
abstract class Compat
{
val global: Global
import global._
def archive(s: ZipArchive#Entry): ZipFile = s.getArchive
def nameString(s: Symbol): String = s.fullNameString
def nameString(s: Symbol, sep: Char): String = s.fullNameString(sep)
def isExistential(s: Symbol): Boolean = s.isExistential
def isNonClassType(s: Symbol): Boolean = s.isTypeMember
def linkedClass(s: Symbol): Symbol = s.linkedClassOfModule
/** After 2.8.0.Beta1, fullNameString was renamed fullName.
* linkedClassOfModule was renamed companionClass. */
private implicit def symCompat(sym: Symbol): SymCompat = new SymCompat(sym)
private final class SymCompat(s: Symbol)
{
def archive(s: ZipArchive#Entry): ZipFile = s.getArchive
def nameString(s: Symbol): String = s.fullNameString
def nameString(s: Symbol, sep: Char): String = s.fullNameString(sep)
def linkedClass(s: Symbol): Symbol = s.linkedClassOfModule
/** After 2.8.0.Beta1, fullNameString was renamed fullName.
* linkedClassOfModule was renamed companionClass. */
private implicit def symCompat(sym: Symbol): SymCompat = new SymCompat(sym)
private final class SymCompat(s: Symbol)
{
def fullNameString = s.fullName; def fullName = sourceCompatibilityOnly
def fullNameString(sep: Char) = s.fullName(sep); def fullName(sep: Char) = sourceCompatibilityOnly
def linkedClassOfModule = s.companionClass; def companionClass = sourceCompatibilityOnly
// In 2.8, hasAttribute is renamed to hasAnnotation
def hasAnnotation(a: Symbol) = s.hasAttribute(a); def hasAttribute(a: Symbol) = sourceCompatibilityOnly
}
def hasAnnotation(s: Symbol)(ann: Symbol) = atPhase(currentRun.typerPhase) { s.hasAnnotation(ann) }
/** After 2.8.0.Beta1, getArchive was renamed archive.*/
private implicit def zipCompat(z: ZipArchive#Entry): ZipCompat = new ZipCompat(z)
private final class ZipCompat(z: ZipArchive#Entry)
{
def getArchive = z.archive; def archive = sourceCompatibilityOnly
}
private def sourceCompatibilityOnly = error("For source compatibility only: should not get here.")
def fullNameString = s.fullName; def fullName = sourceCompatibilityOnly
def fullNameString(sep: Char) = s.fullName(sep); def fullName(sep: Char) = sourceCompatibilityOnly
def isExistential: Boolean = s.isExistentiallyBound; def isExistentiallyBound = sourceCompatibilityOnly
def isTypeMember: Boolean = s.isNonClassType; def isNonClassType = sourceCompatibilityOnly
def linkedClassOfModule = s.companionClass; def companionClass = sourceCompatibilityOnly
// In 2.8, hasAttribute is renamed to hasAnnotation
def hasAnnotation(a: Symbol) = s.hasAttribute(a); def hasAttribute(a: Symbol) = sourceCompatibilityOnly
}
def hasAnnotation(s: Symbol)(ann: Symbol) = atPhase(currentRun.typerPhase) { s.hasAnnotation(ann) }
/** After 2.8.0.Beta1, getArchive was renamed archive.*/
private implicit def zipCompat(z: ZipArchive#Entry): ZipCompat = new ZipCompat(z)
private final class ZipCompat(z: ZipArchive#Entry)
{
def getArchive = z.archive; def archive = sourceCompatibilityOnly
}
private def sourceCompatibilityOnly = error("For source compatibility only: should not get here.")
}

View File

@ -25,7 +25,7 @@ class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentPr
try { update(id); getOrElse(createAndCache) }
catch { case e: NotInCache => createAndCache }
}
def getOrElse(orElse: => Iterable[File]) =
def getOrElse(orElse: => Iterable[File]): Iterable[File] =
{
val existing = provider.component(id)
if(existing.isEmpty) orElse else existing

View File

@ -14,10 +14,10 @@ import plugins.repository.Resource
import plugins.repository.url.URLResource
/** Subclasses the default Ivy file parser in order to provide access to protected methods.*/
private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotNull
private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser
{
import XmlModuleDescriptorParser.Parser
class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings) with NotNull
class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings)
{
if(defaultConfig.isDefined) setDefaultConfMapping("*->default(compile)")

View File

@ -257,13 +257,7 @@ private object IvySbt
javaMap(ea.extraAttributes)
}
private def javaMap(map: Map[String,String]) =
if(map.isEmpty) null
else
{
val wrap = scala.collection.jcl.Map(new java.util.HashMap[String,String])
wrap ++= map
wrap.underlying
}
if(map.isEmpty) null else scala.collection.JavaConversions.asMap(map)
private object javaMap
{
@ -337,7 +331,7 @@ private object IvySbt
lazy val allConfigurations = moduleID.getPublicConfigurationsNames
for(artifact <- artifacts)
{
val configurationStrings =
val configurationStrings: Iterable[String] =
{
val artifactConfigurations = artifact.configurations
if(artifactConfigurations.isEmpty)

View File

@ -60,8 +60,6 @@ object IvyActions
}
/** Creates a Maven pom from the given Ivy configuration*/
@deprecated def makePom(module: IvySbt#Module, extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], extra: NodeSeq, output: File): Unit =
makePom(module, MakePomConfiguration(extraDependencies, configurations, extra), output)
def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, output: File)
{
import configuration.{configurations, extra, extraDependencies, filterRepositories, process}
@ -127,7 +125,7 @@ object IvyActions
resolveOptions.setLog(ivyLogLevel(logging))
val resolveReport = ivy.resolve(module, resolveOptions)
if(resolveReport.hasError)
throw new ResolveException(resolveReport.getAllProblemMessages.toArray.map(_.toString).toList.removeDuplicates)
throw new ResolveException(resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct)
}
import UpdateLogging.{Quiet, Full, DownloadOnly}
@ -140,4 +138,4 @@ object IvyActions
case Full => LOG_DEFAULT
}
}
final class ResolveException(messages: List[String]) extends RuntimeException(messages.mkString("\n"))
final class ResolveException(messages: Seq[String]) extends RuntimeException(messages.mkString("\n"))

View File

@ -27,7 +27,7 @@ object Boot
catch
{
case b: BootException => errorAndExit(b.toString)
case r: xsbti.RetrieveException =>errorAndExit("Error: " + r.getMessage)
case r: xsbti.RetrieveException => errorAndExit("Error: " + r.getMessage)
case r: xsbti.FullReload => r.arguments
case e =>
e.printStackTrace

View File

@ -6,6 +6,7 @@ package xsbt.boot
import Pre._
import java.io.{File, FileInputStream, InputStreamReader}
import java.net.{MalformedURLException, URI, URL}
import scala.collection.immutable.List
object Configuration
{
@ -21,7 +22,7 @@ object Configuration
}
def configurationOnClasspath: URL =
{
resourcePaths.elements.map(getClass.getResource).find(_ ne null) getOrElse
resourcePaths.iterator.map(getClass.getResource).find(_ ne null) getOrElse
( multiPartError("Could not finder sbt launch configuration. Searched classpath for:", resourcePaths))
}
def directConfiguration(path: String, baseDirectory: File): URL =
@ -39,7 +40,7 @@ object Configuration
}
val against = resolveAgainst(baseDirectory)
// use Iterators so that resolution occurs lazily, for performance
val resolving = against.elements.flatMap(e => resolve(e).toList.elements)
val resolving = against.iterator.flatMap(e => resolve(e).toList.iterator)
if(!resolving.hasNext) multiPartError("Could not find configuration file '" + path + "'. Searched:", against)
resolving.next()
}
@ -49,9 +50,9 @@ object Configuration
val JarBasePath = "/sbt/"
def userConfigurationPath = "/" + ConfigurationName
def defaultConfigurationPath = JarBasePath + ConfigurationName
def resourcePaths: List[String] = List(userConfigurationPath, defaultConfigurationPath)
def resolveAgainst(baseDirectory: File): List[URI] = List(baseDirectory toURI, new File(System.getProperty("user.home")) toURI,
toDirectory(classLocation(getClass).toURI))
def resourcePaths: List[String] = userConfigurationPath :: defaultConfigurationPath :: Nil
def resolveAgainst(baseDirectory: File): List[URI] = (baseDirectory toURI) :: (new File(System.getProperty("user.home")) toURI) ::
toDirectory(classLocation(getClass).toURI) :: Nil
def classLocation(cl: Class[_]): URL =
{

View File

@ -9,6 +9,7 @@ import java.lang.Character.isWhitespace
import java.io.{BufferedReader, File, FileInputStream, InputStreamReader, Reader, StringReader}
import java.net.{MalformedURLException, URL}
import java.util.regex.Pattern
import scala.collection.immutable.List
class ConfigurationParser extends NotNull
{

View File

@ -6,6 +6,7 @@ package xsbt.boot
import Pre._
import java.io.{File, FileInputStream, FileOutputStream}
import java.util.Properties
import scala.collection.immutable.List
object Initialize
{
@ -41,7 +42,7 @@ object Initialize
if(!uninitialized.isEmpty)
{
file.getParentFile.mkdirs()
Using(new FileOutputStream(file))( out => properties.save(out, "") )
Using(new FileOutputStream(file))( out => properties.store(out, "") )
}
}
def initialize(properties: Properties, name: String, init: PropertyInit)

View File

@ -4,6 +4,7 @@
package xsbt.boot
import Pre._
import scala.collection.immutable.List
class Enumeration extends NotNull
{
@ -25,6 +26,5 @@ class Enumeration extends NotNull
def value(s: String) = new Value(s, 0)
def value(s: String, i: Int) = new Value(s, i)
class Value(override val toString: String, val id: Int) extends NotNull
def toValue(s: String): Value = elements.find(_.toString == s).getOrElse(error("Expected one of " + elements.mkString(",") + " (got: " + s + ")"))
}

View File

@ -4,10 +4,11 @@
package xsbt.boot
import BootConfiguration.{IvyPackage, JLinePackagePath, SbtBootPackage, ScalaPackage}
import scala.collection.immutable.Stream
/** A custom class loader to ensure the main part of sbt doesn't load any Scala or
* Ivy classes from the jar containing the loader. */
private[boot] final class BootFilteredLoader(parent: ClassLoader) extends ClassLoader(parent) with NotNull
private[boot] final class BootFilteredLoader(parent: ClassLoader) extends ClassLoader(parent)
{
@throws(classOf[ClassNotFoundException])
override final def loadClass(className: String, resolve: Boolean): Class[_] =

View File

@ -6,6 +6,7 @@ package xsbt.boot
import Pre._
import java.io.File
import java.net.URI
import scala.collection.immutable.List
object Find { def apply(config: LaunchConfiguration, currentDirectory: File) = (new Find(config))(currentDirectory) }
class Find(config: LaunchConfiguration) extends NotNull

View File

@ -7,6 +7,7 @@ import Pre._
import BootConfiguration.{CompilerModuleName, LibraryModuleName}
import java.io.File
import java.net.URL
import scala.collection.immutable.List
object Launch
{

View File

@ -6,6 +6,7 @@ package xsbt.boot
import Pre._
import java.io.File
import java.net.URL
import scala.collection.immutable.List
final case class LaunchConfiguration(scalaVersion: Version, ivyConfiguration: IvyOptions, app: Application, boot: BootSetup, logging: Logging, appProperties: List[AppProperty]) extends NotNull
{

View File

@ -4,9 +4,11 @@
package xsbt.boot
import Pre._
import scala.collection.{Iterable, Iterator}
import scala.collection.immutable.List
// preserves iteration order
sealed class ListMap[K,V] private(backing: List[(K,V)]) extends Iterable[(K,V)] with NotNull
sealed class ListMap[K,V] private(backing: List[(K,V)]) extends Traversable[(K,V)] with NotNull
{
import ListMap.remove
def update(k: K, v: V) = this.+( (k,v) )
@ -16,7 +18,7 @@ sealed class ListMap[K,V] private(backing: List[(K,V)]) extends Iterable[(K,V)]
def keys: List[K] = backing.reverse.map(_._1)
def apply(k: K): V = get(k).getOrElse(error("Key " + k + " not found"))
def contains(k: K): Boolean = get(k).isDefined
def elements: Iterator[(K,V)] = backing.reverse.elements
def foreach[T](f: ((K,V)) => T) = backing.reverse.foreach(f)
override def isEmpty: Boolean = backing.isEmpty
override def toList = backing.reverse
override def toSeq = toList
@ -30,7 +32,7 @@ sealed class ListMap[K,V] private(backing: List[(K,V)]) extends Iterable[(K,V)]
}
object ListMap
{
def apply[K,V](pairs: (K,V)*) = new ListMap[K,V](pairs.toList.removeDuplicates)
def apply[K,V](pairs: (K,V)*) = new ListMap[K,V](pairs.toList.distinct)
def empty[K,V] = new ListMap[K,V](Nil)
private def remove[K,V](backing: List[(K,V)], k: K) = backing.filter(_._1 != k)
}

View File

@ -6,6 +6,7 @@ package xsbt.boot
import java.io.{File, FileOutputStream}
import java.nio.channels.FileChannel
import java.util.concurrent.Callable
import scala.collection.immutable.List
object GetLocks
{

View File

@ -2,6 +2,7 @@
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package xsbt.boot
import scala.collection.immutable.List
object Pre
{
@ -19,7 +20,7 @@ object Pre
def declined(msg: String): Nothing = throw new BootException(msg)
def prefixError(msg: String): String = "Error during sbt execution: " + msg
def toBoolean(s: String) = java.lang.Boolean.parseBoolean(s)
def toArray[T](list: List[T]) =
def toArray[T : ClassManifest](list: List[T]) =
{
val arr = new Array[T](list.length)
def copy(i: Int, rem: List[T]): Unit =

View File

@ -309,7 +309,7 @@ import SbtIvyLogger.{acceptError, acceptMessage}
/** A custom logger for Ivy to ignore the messages about not finding classes
* intentionally filtered using proguard and about 'unknown resolver'. */
private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO) with NotNull
private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO)
{
override def log(msg: String, level: Int)
{

View File

@ -35,5 +35,4 @@ object ListMapEmpty extends Properties("ListMap.empty")
property("toSeq.isEmpty") = empty.toSeq.isEmpty
property("toStream.isEmpty") = empty.toStream.isEmpty
property("keys.isEmpty") = empty.keys.isEmpty
property("elements.isEmpty") = !empty.elements.hasNext
}

View File

@ -3,20 +3,21 @@ package xsbt.boot
import org.scalacheck._
import Prop._
import java.io.File
import sbt.IO.withTemporaryDirectory
/** These mainly test that things work in the uncontested case and that no OverlappingFileLockExceptions occur.
* There is no real locking testing, just the coordination of locking.*/
object LocksTest extends Properties("Locks")
{
property("Lock in nonexisting directory") = spec {
FileUtilities.withTemporaryDirectory { dir =>
withTemporaryDirectory { dir =>
val lockFile = new File(dir, "doesntexist/lock")
Locks(lockFile, callTrue)
}
}
property("Uncontested re-entrant lock") = spec {
FileUtilities.withTemporaryDirectory { dir =>
withTemporaryDirectory { dir =>
val lockFile = new File(dir, "lock")
Locks(lockFile, callLocked(lockFile)) &&
Locks(lockFile, callLocked(lockFile))
@ -24,7 +25,7 @@ object LocksTest extends Properties("Locks")
}
property("Uncontested double lock") = spec {
FileUtilities.withTemporaryDirectory { dir =>
withTemporaryDirectory { dir =>
val lockFileA = new File(dir, "lockA")
val lockFileB = new File(dir, "lockB")
Locks(lockFileA, callLocked(lockFileB)) &&
@ -33,7 +34,7 @@ object LocksTest extends Properties("Locks")
}
property("Contested single lock") = spec {
FileUtilities.withTemporaryDirectory { dir =>
withTemporaryDirectory { dir =>
val lockFile = new File(dir, "lock")
forkFold(2000){i => Locks(lockFile, callTrue) }
}

View File

@ -5,6 +5,7 @@ import java.util.Properties
import xsbti._
import org.specs._
import LaunchTest._
import sbt.IO.{createDirectory, touch,withTemporaryDirectory}
object ScalaProviderTest extends Specification
{
@ -34,7 +35,7 @@ object ScalaProviderTest extends Specification
def checkLoad(arguments: List[String], mainClassName: String): MainResult =
checkLoad(arguments, mainClassName, _ => Array[File]())
def checkLoad(arguments: List[String], mainClassName: String, extra: File => Array[File]): MainResult =
FileUtilities.withTemporaryDirectory { currentDirectory =>
withTemporaryDirectory { currentDirectory =>
withLauncher { launcher =>
Launch.run(launcher)(
new RunConfiguration(mapScalaVersion(LaunchTest.getScalaVersion), LaunchTest.testApp(mainClassName, extra(currentDirectory)).toID, currentDirectory, arguments)
@ -45,8 +46,8 @@ object ScalaProviderTest extends Specification
private def createExtra(currentDirectory: File) =
{
val resourceDirectory = new File(currentDirectory, "resources")
FileUtilities.createDirectory(resourceDirectory)
testResources.foreach(resource => FileUtilities.touch(new File(resourceDirectory, resource.replace('/', File.separatorChar))))
createDirectory(resourceDirectory)
testResources.foreach(resource => touch(new File(resourceDirectory, resource.replace('/', File.separatorChar))))
Array(resourceDirectory)
}
private def checkScalaLoader(version: String): Unit = withLauncher( checkLauncher(version, scalaVersionMap(version)) )
@ -67,7 +68,7 @@ object LaunchTest
import Repository.Predefined._
def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply)
def withLauncher[T](f: xsbti.Launcher => T): T =
FileUtilities.withTemporaryDirectory { bootDirectory =>
withTemporaryDirectory { bootDirectory =>
f(Launcher(bootDirectory, testRepositories))
}

10
notes
View File

@ -1,3 +1,13 @@
Issues moving Launcher to 2.8:
creating an Array requires a Manifest, which brings in 40k of classes in scala.reflect
scala.package$ is 3k (import scala.collection.immutable.List) and brings in scala.xml
RichInt 2k. scala.collection.mutable.StringBuilder.ensureCapacity uses it (max), which is needed for string concat
scala.xml brings in 50k of classes
scala.math brought in 30k, can't remove because RichInt apparently uses it
100k additional size adds 50ms to startup time (on my faster machine)
difference between classpaths a) just launcher jar b) current directory + launcher jar is that b) is 50 ms slower than a)
was able to return to old size by enabling optimize/obfuscate phases of ProGuard
- script tasks (in 'scripts' branch). To use:
1) add implementation of jsr223 to project/build/lib, declare in project/plugins, or add to sbt startup classpath
2) Mix sbt.scripts.Scripts into your project definition

View File

@ -3,20 +3,27 @@ import java.io.File
trait ProguardLaunch extends ProguardProject
{
override def optimize = 2
override def basicOptions = super.basicOptions ++ Seq(keepJLine)
def outputJar = rootProject.outputPath / ("sbt-launch-" + version + ".jar")
override def keepClasses =
override def keepFullClasses =
"xsbti.**" ::
"jline.**" ::
Nil
override def keepClasses =
"org.apache.ivy.plugins.resolver.URLResolver" ::
"org.apache.ivy.plugins.resolver.IBiblioResolver" ::
"xsbti.**" ::
Nil
override def mapInJars(inJars: Seq[File]) =
{
val inputJar = jarPath.asFile.getAbsolutePath
val jlineJars = runClasspath.getFiles.filter(isJLineJar)
// pull out Ivy in order to exclude resources inside
val (ivyJars, notIvy) = inJars.filter(jar => !isJLineJar(jar)).partition(isIvyJar)
val otherJars = notIvy.filter(jar => !isJarX(jar, "scala-compiler"))
val (libraryJar, remaining) = notIvy.partition(isScalaJar)
val otherJars = remaining.filter(jar => !isJarX(jar, "scala-compiler"))
log.debug("proguard configuration:")
log.debug("\tJLline jar location: " + jlineJars.mkString(", "))
@ -24,8 +31,9 @@ trait ProguardLaunch extends ProguardProject
log.debug("\tOther jars:\n\t" + otherJars.mkString("\n\t"))
val excludeIvyResourcesString = excludeString(excludeIvyResources)
((withJar(ivyJars.toSeq, "Ivy") + excludeIvyResourcesString) ::
(withJar(jlineJars, "JLine") + "(!META-INF/**)" ) ::
otherJars.map(jar => mkpath(jar) + "(!META-INF/**,!*.properties)").toList) map { "-injars " + _ }
(withJar(jlineJars, "JLine") + jlineFilter ) ::
(withJar(libraryJar, "Scala library") + libraryFilter) ::
otherJars.map(jar => mkpath(jar) + generalFilter).toList) map { "-injars " + _ }
}
private def excludeString(s: List[String]) = s.map("!" + _).mkString("(",",",")")
@ -41,9 +49,14 @@ trait ProguardLaunch extends ProguardProject
"org/apache/ivy/plugins/report/ivy-report-*" ::
Nil
private def libraryFilter = "(!META-INF/**,!*.properties,!scala/actors/**.!scala/util/parsing/*.class,!scala/xml/**.class,!scala/package$.class,**.class)"
private def jlineFilter = "(!META-INF/**)"
private def generalFilter = "(!META-INF/**,!*.properties)"
private def withJar[T](files: Iterable[File], name: String) = mkpath(files.toSeq.firstOption.getOrElse(error(name + " not present (try running update)")))
private def isJLineJar(file: File) = isJarX(file, "jline")
private def isIvyJar(file: File) = isJarX(file, "ivy")
private def isScalaJar(file: File) = isJarX(file, "scala-library")
private def isJarX(file: File, x: String) =
{
val name = file.getName

View File

@ -18,7 +18,7 @@ trait ProguardProject extends BasicScalaProject
def rootProjectDirectory = rootProject.info.projectPath
val toolsConfig = config("tools") hide
val proguardJar = "net.sf.proguard" % "proguard" % "4.3" % "tools"
val proguardJar = "net.sf.proguard" % "proguard" % "4.4" % "tools"
lazy val proguard = proguardAction
def proguardAction = proguardTask dependsOn(writeProguardConfiguration) describedAs(ProguardDescription)
@ -27,12 +27,17 @@ trait ProguardProject extends BasicScalaProject
def basicOptions: Seq[String] =
Seq(
"-dontoptimize",
"-dontobfuscate",
"-keep,allowoptimization,allowshrinking class * { *; }",
"-keepattributes SourceFile,LineNumberTable",
"-dontnote",
"-dontwarn",
"-ignorewarnings")
"-ignorewarnings") ++
optimizeOptions
def keepFullClasses: Seq[String] = Nil
def keepClasses: Seq[String] = Nil
def optimize: Int = 0
def optimizeOptions = if(optimize <= 0) Seq("-dontoptimize") else Seq( "-optimizationpasses 2", "-optimizations !code/allocation/variable")
def mapInJars(inJars: Seq[File]): Seq[String] = inJars.map(f => "-injars " + mkpath(f))
def mapLibraryJars(libraryJars: Seq[File]): Seq[String] = libraryJars.map(f => "-libraryjars " + mkpath(f))
@ -47,7 +52,8 @@ trait ProguardProject extends BasicScalaProject
val lines =
options ++
keepClasses.map("-keep public class " + _ + " {\n public * ;\n}") ++
keepFullClasses.map("-keep public class " + _ + " {\n public protected * ;\n}") ++
keepClasses.map("-keep class " + _ + " {}") ++
mapInJars(inJars) ++
Seq("-injars " + mkpath(rawJarPath.asFile),
mapOutJar(outJar)) ++

View File

@ -113,7 +113,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
{
override def scratch = true
override def consoleClasspath = testClasspath
override def compileOptions = super.compileOptions ++ compileOptions("-Xelide-below", "3000")
override def compileOptions = super.compileOptions ++ compileOptions("-Xelide-below", "0")
}
trait Licensed extends BasicScalaProject
{
@ -128,7 +128,6 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
{
override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`)
override def testClasspath = super.testClasspath +++ compileInterfaceSub.packageSrcJar +++ interfaceSub.jarPath --- compilerInterfaceClasspath --- interfaceSub.mainCompilePath
override def compileOptions = super.compileOptions ++ Seq(CompileOption("-Xno-varargs-conversion")) //needed for invoking nsc.scala.tools.Main.process(Array[String])
}
class IvyProject(info: ProjectInfo) extends Base(info) with TestWithIO with TestWithLog with TestWithLaunch
{
@ -240,4 +239,4 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
override def testCompileAction = super.testCompileAction dependsOn((testWithTestClasspath.map(_.testCompile) ++ testWithCompileClasspath.map(_.compile)) : _*)
override def testClasspath = (super.testClasspath /: (testWithTestClasspath.map(_.testClasspath) ++ testWithCompileClasspath.map(_.compileClasspath) ))(_ +++ _)
}
}
}

View File

@ -68,7 +68,7 @@ object IO
(name, "")
}
def touch(files: Iterable[File]): Unit = files.foreach(touch)
def touch(files: Traversable[File]): Unit = files.foreach(touch)
/** Creates a file at the given location.*/
def touch(file: File)
{
@ -79,7 +79,7 @@ object IO
else if(!file.setLastModified(System.currentTimeMillis))
error("Could not update last modified time for file " + file)
}
def createDirectories(dirs: Iterable[File]): Unit =
def createDirectories(dirs: Traversable[File]): Unit =
dirs.foreach(createDirectory)
def createDirectory(dir: File): Unit =
{
@ -262,12 +262,12 @@ object IO
* @param sources The files to include in the jar file paired with the entry name in the jar.
* @param outputJar The file to write the jar to.
* @param manifest The manifest for the jar.*/
def jar(sources: Iterable[(File,String)], outputJar: File, manifest: Manifest): Unit =
def jar(sources: Traversable[(File,String)], outputJar: File, manifest: Manifest): Unit =
archive(sources.toSeq, outputJar, Some(manifest))
/** Creates a zip file.
* @param sources The files to include in the zip file paired with the entry name in the zip.
* @param outputZip The file to write the zip to.*/
def zip(sources: Iterable[(File,String)], outputZip: File): Unit =
def zip(sources: Traversable[(File,String)], outputZip: File): Unit =
archive(sources.toSeq, outputZip, None)
private def archive(sources: Seq[(File,String)], outputFile: File, manifest: Option[Manifest])
@ -360,7 +360,7 @@ object IO
else
None
}
def copy(sources: Iterable[(File,File)], overwrite: Boolean = false, preserveLastModified: Boolean = false): Set[File] =
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 =
{
@ -469,7 +469,7 @@ object IO
for( (file, index) <- files.zipWithIndex) yield
(file, new File(dir, index.toHexString))
def move(files: Iterable[(File, File)]): Unit =
def move(files: Traversable[(File, File)]): Unit =
files.foreach(Function.tupled(move))
def move(a: File, b: File): Unit =

View File

@ -131,6 +131,8 @@ object Path extends Alternatives with Mapper
implicit def pathToFile(path: Path): File = path.asFile
implicit def pathsToFiles[CC[X] <: TraversableLike[X,CC[X]]](cc: CC[Path])(implicit cb: generic.CanBuildFrom[CC[Path], File, CC[File]]): CC[File] =
cc.map(_.asFile)
implicit def filesToFinder(cc: Traversable[File]): PathFinder = finder(cc)
implicit def pathsToFinder(cc: Traversable[Path]): PathFinder = lazyPathFinder(cc)
def fileProperty(name: String) = Path.fromFile(System.getProperty(name))
def userHome = fileProperty("user.home")
@ -161,12 +163,12 @@ object Path extends Alternatives with Mapper
}
/** A <code>PathFinder</code> that selects the paths provided by the <code>paths</code> argument, which is
* reevaluated on each call to the <code>PathFinder</code>'s <code>get</code> method. */
def lazyPathFinder(paths: => Iterable[Path]): PathFinder =
def lazyPathFinder(paths: => Traversable[Path]): PathFinder =
new PathFinder
{
private[sbt] def addTo(pathSet: mutable.Set[Path]) = pathSet ++= paths
}
def finder(files: => Iterable[File]): PathFinder = lazyPathFinder { fromFiles(files) }
def finder(files: => Traversable[File]): PathFinder = lazyPathFinder { fromFiles(files) }
/** The separator character of the platform.*/
val sep = java.io.File.separatorChar
@ -244,7 +246,7 @@ object Path extends Alternatives with Mapper
}
def fromFile(file: String): Path = fromFile(new File(file))
def fromFile(file: File): Path = new FilePath(file)
def fromFiles(files: Iterable[File]): Iterable[Path] = files.map(fromFile)
def fromFiles(files: Traversable[File]): Traversable[Path] = files.map(fromFile)
def getFiles(files: Traversable[Path]): immutable.Set[File] = files.map(_.asFile).toSet
def getURLs(files: Traversable[Path]): Array[URL] = files.map(_.asURL).toArray