- Stuart's improvements to triggered execution

- continue splitting original sbt module
 * separated process, testing modules
 * various IO, logging, classpath migration
 * split out javac interface
This commit is contained in:
Mark Harrah 2010-07-05 12:53:37 -04:00
parent 37185c0fb6
commit 6f3c699435
50 changed files with 498 additions and 644 deletions

View File

@ -1,6 +1,9 @@
package xsbt
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package sbt
package compile
import sbt.ComponentManager
import xsbti.{AnalysisCallback, Logger => xLogger}
import java.io.File
import java.net.{URL, URLClassLoader}
@ -9,34 +12,34 @@ package xsbt
* provided by scalaInstance. This class requires a ComponentManager in order to obtain the interface code to scalac and
* the analysis plugin. Because these call Scala code for a different Scala version than the one used for this class, they must
* be compiled for the version of Scala being used.*/
class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: ComponentManager, val cp: ClasspathOptions, log: CompileLogger) extends NotNull
class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: ComponentManager, val cp: ClasspathOptions, log: Logger) extends NotNull
{
def this(scalaInstance: ScalaInstance, manager: ComponentManager, log: CompileLogger) = this(scalaInstance, manager, ClasspathOptions.auto, log)
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger)
def this(scalaInstance: ScalaInstance, manager: ComponentManager, log: Logger) = this(scalaInstance, manager, ClasspathOptions.auto, log)
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
{
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
compile(arguments, callback, maximumErrors, log)
}
def compile(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: CompileLogger)
def compile(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
{
call("xsbt.CompilerInterface", log)(
classOf[Array[String]], classOf[AnalysisCallback], classOf[Int], classOf[xLogger] ) (
arguments.toArray[String] : Array[String], callback, maximumErrors: java.lang.Integer, log )
}
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: CompileLogger): Unit =
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger): Unit =
{
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options)
call("xsbt.ScaladocInterface", log) (classOf[Array[String]], classOf[Int], classOf[xLogger]) (arguments.toArray[String] : Array[String], maximumErrors: java.lang.Integer, log)
}
def console(classpath: Seq[File], options: Seq[String], initialCommands: String, log: CompileLogger): Unit =
def console(classpath: Seq[File], options: Seq[String], initialCommands: String, log: Logger): Unit =
{
val arguments = new CompilerArguments(scalaInstance, cp)
val classpathString = CompilerArguments.absString(arguments.finishClasspath(classpath))
val bootClasspath = if(cp.autoBoot) arguments.createBootClasspath else ""
call("xsbt.ConsoleInterface", log) (classOf[Array[String]], classOf[String], classOf[String], classOf[String], classOf[xLogger]) (options.toArray[String]: Array[String], bootClasspath, classpathString, initialCommands, log)
}
def force(log: CompileLogger): Unit = getInterfaceJar(log)
private def call(interfaceClassName: String, log: CompileLogger)(argTypes: Class[_]*)(args: AnyRef*)
def force(log: Logger): Unit = getInterfaceJar(log)
private def call(interfaceClassName: String, log: Logger)(argTypes: Class[_]*)(args: AnyRef*)
{
val interfaceClass = getInterfaceClass(interfaceClassName, log)
val interface = interfaceClass.newInstance.asInstanceOf[AnyRef]
@ -50,20 +53,20 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader) // this goes to scalaLoader for scala classes and sbtLoader for xsbti classes
new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
}
private def getInterfaceClass(name: String, log: CompileLogger) = Class.forName(name, true, loader)
private def getInterfaceJar(log: CompileLogger) =
private def getInterfaceClass(name: String, log: Logger) = Class.forName(name, true, loader)
private def getInterfaceJar(log: Logger) =
{
// this is the instance used to compile the interface component
val componentCompiler = newComponentCompiler(log)
log.debug("Getting " + ComponentCompiler.compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version)
componentCompiler(ComponentCompiler.compilerInterfaceID)
}
def newComponentCompiler(log: CompileLogger) = new ComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager)
def newComponentCompiler(log: Logger) = new ComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager)
protected def createDualLoader(scalaLoader: ClassLoader, sbtLoader: ClassLoader): ClassLoader =
{
val xsbtiFilter = (name: String) => name.startsWith("xsbti.")
val notXsbtiFilter = (name: String) => !xsbtiFilter(name)
new DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
new classpath.DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
}
override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")"
}

View File

@ -1,10 +0,0 @@
package xsbt
trait CompileLogger extends xsbti.Logger with NotNull
{
def info(msg: => String)
def debug(msg: => String)
def warn(msg: => String)
def error(msg: => String)
def trace(t: => Throwable)
}

View File

@ -1,4 +1,8 @@
package xsbt
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package sbt
package compile
import java.io.File
import CompilerArguments.{abs, absString}

View File

@ -1,7 +1,10 @@
package xsbt
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package sbt
package compile
import java.io.File
import sbt.{ComponentManager, IfMissing, InvalidComponent}
object ComponentCompiler
{

View File

@ -0,0 +1,66 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah, Seth Tisue
*/
package sbt
package compile
import java.io.File
trait JavaCompiler
{
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger): Unit
}
object JavaCompiler
{
def construct(f: (Seq[String], Logger) => Int, cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaCompiler =
new JavaCompiler {
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger) {
val augmentedClasspath = if(cp.autoBoot) classpath ++ Seq(scalaInstance.libraryJar) else classpath
val javaCp = new ClasspathOptions(false, cp.compiler, false)
val arguments = (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, outputDirectory, options)
log.debug("running javac with arguments:\n\t" + arguments.mkString("\n\t"))
val code: Int = f(arguments, log)
log.debug("javac returned exit code: " + code)
if( code != 0 ) throw new CompileFailed(arguments.toArray, "javac returned nonzero exit code")
}
}
def directOrFork(cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaCompiler = construct(directOrForkJavac, cp, scalaInstance)
def direct(cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaCompiler = construct(directJavac, cp, scalaInstance)
def fork(cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaCompiler = construct(forkJavac, cp, scalaInstance)
val directOrForkJavac = (arguments: Seq[String], log: Logger) =>
try { directJavac(arguments, log) }
catch { case e: ClassNotFoundException =>
log.debug("com.sun.tools.javac.Main not found; forking javac instead")
forkJavac(arguments, log)
}
val forkJavac = (arguments: Seq[String], log: Logger) =>
{
def externalJavac(argFile: File) = Process("javac", ("@" + normalizeSlash(argFile.getAbsolutePath)) :: Nil) ! log
withArgumentFile(arguments)(externalJavac)
}
val directJavac = (arguments: Seq[String], log: Logger) =>
{
val writer = new java.io.PrintWriter(new LoggerWriter(log, Level.Error))
val argsArray = arguments.toArray
val javac = Class.forName("com.sun.tools.javac.Main")
log.debug("Calling javac directly.")
javac.getDeclaredMethod("compile", classOf[Array[String]], classOf[java.io.PrintWriter])
.invoke(null, argsArray, writer)
.asInstanceOf[java.lang.Integer]
.intValue
}
def withArgumentFile[T](args: Seq[String])(f: File => T): T =
{
import IO.{Newline, withTemporaryDirectory, write}
withTemporaryDirectory { tmp =>
val argFile = new File(tmp, "argfile")
write(argFile, args.map(escapeSpaces).mkString(Newline))
f(argFile)
}
}
// javac's argument file seems to allow naive space escaping with quotes. escaping a quote with a backslash does not work
def escapeSpaces(s: String): String = '\"' + normalizeSlash(s) + '\"'
def normalizeSlash(s: String) = s.replace(File.separatorChar, '/')
}

View File

@ -1,3 +1,3 @@
Simple Build Tool: Compile Component
Copyright 2009, 2010 Mark Harrah, Jason Zaugg
Copyright 2009, 2010 Mark Harrah, Seth Tisue, Jason Zaugg
Licensed under BSD-style license (see LICENSE)

View File

@ -1,4 +1,8 @@
package xsbt
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package sbt
package compile
import java.io.File
@ -6,7 +10,7 @@ package xsbt
* is used, for example, to compile the interface/plugin code.
* If `explicitClasspath` is true, the bootclasspath and classpath are not augmented. If it is false,
* the scala-library.jar from `scalaInstance` is put on bootclasspath and the scala-compiler jar goes on the classpath.*/
class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: CompileLogger)
class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: Logger)
{
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])
{

3
compile/discover/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Simple Build Tool: Discovery Component
Copyright 2010 Mark Harrah
Licensed under BSD-style license (see LICENSE)

3
compile/inc/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Simple Build Tool: Incremental Logic Component
Copyright 2010 Mark Harrah
Licensed under BSD-style license (see LICENSE)

View File

@ -4,8 +4,18 @@ Tradiationally, there has been a main group and a test group.
Each Analysis is associated with one or more classpath entries. Typically, it will be associated with the output directory and/or any artifacts produced from that output directory.
Need to be able to process Source and extract classes {C} s.t. C <: D for D in {D} or C or one of its non-private methods is annotated with annotation A in {A} or C.main(args: Array[String]) exists
For Java sources, need to write a (File, Set[File]) => Source function that reads an API from a class file. The compile function passed to IncrementalCompile needs to handle compiling Java sources in the proper order
Need to handle entries removed from classpath. Could be done similarly to how Locate is used for getting the API for a dependency. In this case, we'd get the Stamp for a binary dependency.
Post-analysis
- need to handle inherited definitions
Need builder component:
Processor[D]
def process(command: String, arg: String, current: State[D]): Result[State[D]]
Initial[D]
def init: State[D]
State[D]
value: D
processors: Map[String, Processor[D]]

View File

@ -300,7 +300,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
}
def isTopLevel(sym: Symbol): Boolean =
(sym ne null) && (sym != NoSymbol) && !sym.isImplClass && !sym.isNestedClass && sym.isStatic &&
!sym.hasFlag(Flags.SYNTHETIC) && !sym.hasFlag(Flags.JAVA)
!sym.hasFlag(Flags.SYNTHETIC)// && !sym.hasFlag(Flags.JAVA)
}
// In 2.8, attributes is renamed to annotations

3
compile/persist/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Simple Build Tool: Analysis Store Component
Copyright 2010 Mark Harrah
Licensed under BSD-style license (see LICENSE)

View File

@ -1,10 +1,8 @@
package sbt
import xsbt.{BufferedLogger, FileUtilities}
import java.io.File
import org.specs._
import FileUtilities.{createDirectory, delete, touch, withTemporaryDirectory}
import IO.{createDirectory, delete, touch, withTemporaryDirectory}
import org.apache.ivy.util.ChecksumHelper
import IfMissing.Fail
@ -76,7 +74,7 @@ object ComponentManagerTest extends Specification
manager.define(id, files)
f(files)
}
private def writeRandomContent(file: File) = FileUtilities.write(file, randomString)
private def writeRandomContent(file: File) = IO.write(file, randomString)
private def randomString = "asdf"
private def withManager[T](f: ComponentManager => T): T =
TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp), logger)) } )

View File

@ -1,8 +1,6 @@
package sbt
import xsbt.{BufferedLogger, ConsoleLogger, Level}
class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger { def verbose(msg: => String) = info(msg) }
class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger
object TestIvyLogger
{
def apply[T](f: IvyLogger => T): T =

View File

@ -6,7 +6,7 @@ package sbt
import inc._
import java.io.File
import xsbt.{AnalyzingCompiler, CompileLogger, CompilerArguments}
import sbt.compile.{AnalyzingCompiler, CompilerArguments}
import xsbti.api.Source
import xsbti.AnalysisCallback
import CompileSetup._
@ -18,7 +18,7 @@ final class CompileConfiguration(val sources: Seq[File], val classpath: Seq[File
class AggressiveCompile(cacheDirectory: File)
{
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], compiler: AnalyzingCompiler, log: CompileLogger): Analysis =
def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], compiler: AnalyzingCompiler, log: Logger): Analysis =
{
val setup = new CompileSetup(outputDirectory, new CompileOptions(options), compiler.scalaInstance.actualVersion, CompileOrder.Mixed)
compile1(sources, classpath, setup, store, Map.empty, compiler, log)
@ -27,7 +27,7 @@ class AggressiveCompile(cacheDirectory: File)
def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] =
args.bootClasspath ++ classpath
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], compiler: AnalyzingCompiler, log: CompileLogger): Analysis =
def compile1(sources: Seq[File], classpath: Seq[File], setup: CompileSetup, store: AnalysisStore, analysis: Map[File, Analysis], compiler: AnalyzingCompiler, log: Logger): Analysis =
{
val (previousAnalysis, previousSetup) = extract(store.get())
val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, analysis.get _, 100, compiler)
@ -35,7 +35,7 @@ class AggressiveCompile(cacheDirectory: File)
store.set(result, setup)
result
}
def compile2(config: CompileConfiguration, log: CompileLogger)(implicit equiv: Equiv[CompileSetup]): Analysis =
def compile2(config: CompileConfiguration, log: Logger)(implicit equiv: Equiv[CompileSetup]): Analysis =
{
import config._
import currentSetup._

View File

@ -3,7 +3,7 @@
*/
package sbt
import xsbt.{AnalyzingCompiler, CompileLogger, ScalaInstance}
import sbt.compile.AnalyzingCompiler
import java.io.File
import System.{currentTimeMillis => now}
import Path._
@ -23,13 +23,13 @@ class AggressiveCompiler extends xsbti.AppMain
def run(command: String, args: List[String], cwd: Path, app: xsbti.AppProvider): Boolean =
{
val launcher = app.scalaProvider.launcher
val sources = cwd ** "*.scala"
val sources = cwd ** ("*.scala" | "*.java")
val target = cwd / "target"
val outputDirectory = target / "classes"
val classpath = outputDirectory +++ (cwd * "*.jar") +++(cwd * (-"project")).descendentsExcept( "*.jar", "project" || HiddenFileFilter)
val cacheDirectory = target / "cache"
val options = args.tail.toSeq
val log = new ConsoleLogger with CompileLogger with sbt.IvyLogger
val log = new ConsoleLogger with Logger with sbt.IvyLogger
val componentManager = new ComponentManager(launcher.globalLock, app.components, log)
val compiler = new AnalyzingCompiler(ScalaInstance(args.head, launcher), componentManager, log)

View File

@ -12,26 +12,30 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
val interfaceSub = project("interface", "Interface", new InterfaceProject(_))
val apiSub = baseProject(compilePath / "api", "API", interfaceSub)
// util
val controlSub = baseProject(utilPath / "control", "Control")
val collectionSub = testedBase(utilPath / "collection", "Collections")
val ioSub = testedBase(utilPath / "io", "IO", controlSub)
val classpathSub = baseProject(utilPath / "classpath", "Classpath")
val classpathSub = baseProject(utilPath / "classpath", "Classpath", launchInterfaceSub, ioSub)
val classfileSub = testedBase(utilPath / "classfile", "Classfile", ioSub, interfaceSub)
val completeSub = testedBase(utilPath / "complete", "Completion", ioSub)
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub, launchInterfaceSub)
val logSub = project(utilPath / "log", "Logging", new LogProject(_), interfaceSub)
val datatypeSub = baseProject("util" /"datatype", "Datatype Generator", ioSub)
val datatypeSub = baseProject(utilPath /"datatype", "Datatype Generator", ioSub)
val processSub = project(utilPath /"process", "Process", new Base(_) with TestWithIO, ioSub, logSub)
// intermediate-level modules
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub, launchInterfaceSub, logSub)
val testingSub = project("testing", "Testing", new TestingProject(_), ioSub, classpathSub, logSub)
val taskSub = testedBase(tasksPath, "Tasks", controlSub, collectionSub)
val cacheSub = project(cachePath, "Cache", new CacheProject(_), ioSub, collectionSub)
// compilation/discovery related modules
val compileInterfaceSub = project(compilePath / "interface", "Compiler Interface", new CompilerInterfaceProject(_), interfaceSub)
val compileIncrementalSub = testedBase(compilePath / "inc", "Incremental Compiler", collectionSub, apiSub, ioSub)
val discoverySub = testedBase(compilePath / "discover", "Discovery", compileIncrementalSub, apiSub)
val compilePersistSub = project(compilePath / "persist", "Persist", new PersistProject(_), compileIncrementalSub, apiSub)
val compilerSub = project(compilePath, "Compile", new CompileProject(_),
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
val taskSub = testedBase(tasksPath, "Tasks", controlSub, collectionSub)
val cacheSub = project(cachePath, "Cache", new CacheProject(_), ioSub, collectionSub)
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub, logSub, processSub)
val altCompilerSub = baseProject("main", "Alternate Compiler Test", compileIncrementalSub, compilerSub, ioSub, logSub, discoverySub, compilePersistSub)
@ -40,7 +44,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
val trackingSub = baseProject(cachePath / "tracking", "Tracking", cacheSub)
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {}, compilerSub, launchInterfaceSub)
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {}, compilerSub, launchInterfaceSub, testingSub, cacheSub, taskSub)
val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub)
lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal)
@ -123,6 +127,10 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
def seePaths(noticeString: String): List[Path] = seeRegex.findAllIn(noticeString).matchData.map(d => licensePath(d.group(1))).toList
def extractLicenses = if(!notice.exists) Nil else FileUtilities.readString(notice asFile, log).fold(_ => { log.warn("Could not read NOTICE"); Nil} , seePaths _)
}
class TestingProject(info: ProjectInfo) extends Base(info)
{
val testInterface = "org.scala-tools.testing" % "test-interface" % "0.5"
}
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch
{
override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`)

View File

@ -6,10 +6,6 @@ package sbt
import java.io.File
import xsbt.{AnalyzingCompiler, CompileFailed, CompilerArguments, ScalaInstance}
object CompileOrder extends Enumeration
{
val Mixed, JavaThenScala, ScalaThenJava = Value
}
sealed abstract class CompilerCore
{
@ -75,50 +71,6 @@ final class Compile(maximumErrors: Int, compiler: AnalyzingCompiler, analysisCal
val callbackInterface = new AnalysisInterface(analysisCallback, baseDirectory, outputDirectory)
compiler(Set() ++ sources, Set() ++ classpath, outputDirectory, options, callbackInterface, maximumErrors, log)
}
protected def processJava(sources: Set[File], classpath: Set[File], outputDirectory: File, options: Seq[String], log: Logger)
{
val augmentedClasspath = if(compiler.cp.autoBoot) classpath + compiler.scalaInstance.libraryJar else classpath
val cp = new xsbt.ClasspathOptions(false, compiler.cp.compiler, false)
val arguments = (new CompilerArguments(compiler.scalaInstance, cp))(sources, augmentedClasspath, outputDirectory, options)
log.debug("running javac with arguments:\n\t" + arguments.mkString("\n\t"))
val code: Int =
try { directJavac(arguments, log) }
catch { case e: ClassNotFoundException => forkJavac(arguments, log) }
log.debug("javac returned exit code: " + code)
if( code != 0 ) throw new CompileFailed(arguments.toArray, "javac returned nonzero exit code")
}
private def forkJavac(arguments: Seq[String], log: Logger): Int =
{
log.debug("com.sun.tools.javac.Main not found; forking javac instead")
def externalJavac(argFile: File) = Process("javac", ("@" + normalizeSlash(argFile.getAbsolutePath)) :: Nil) ! log
withArgumentFile(arguments)(externalJavac)
}
private def directJavac(arguments: Seq[String], log: Logger): Int =
{
val writer = new java.io.PrintWriter(new LoggerWriter(log, Level.Error))
val argsArray = arguments.toArray
val javac = Class.forName("com.sun.tools.javac.Main")
log.debug("Calling javac directly.")
javac.getDeclaredMethod("compile", classOf[Array[String]], classOf[java.io.PrintWriter])
.invoke(null, argsArray, writer)
.asInstanceOf[java.lang.Integer]
.intValue
}
}
trait WithArgumentFile extends NotNull
{
def withArgumentFile[T](args: Seq[String])(f: File => T): T =
{
import xsbt.FileUtilities._
withTemporaryDirectory { tmp =>
val argFile = new File(tmp, "argfile")
write(argFile, args.map(escapeSpaces).mkString(FileUtilities.Newline))
f(argFile)
}
}
// javac's argument file seems to allow naive space escaping with quotes. escaping a quote with a backslash does not work
def escapeSpaces(s: String): String = '\"' + normalizeSlash(s) + '\"'
def normalizeSlash(s: String) = s.replace(File.separatorChar, '/')
}
final class Scaladoc(maximumErrors: Int, compiler: AnalyzingCompiler) extends CompilerCore
{
@ -142,40 +94,3 @@ final class Console(compiler: AnalyzingCompiler) extends NotNull
JLine.withJLine( Run.executeTrapExit(console0, log) )
}
}
private final class AnalysisInterface(delegate: AnalysisCallback, basePath: Path, outputDirectory: File) extends xsbti.AnalysisCallback with NotNull
{
val outputPath = Path.fromFile(outputDirectory)
def superclassNames = delegate.superclassNames.toSeq.toArray[String]
def annotationNames = delegate.annotationNames.toSeq.toArray[String]
def superclassNotFound(superclassName: String) = delegate.superclassNotFound(superclassName)
def beginSource(source: File) = delegate.beginSource(srcPath(source))
def foundSubclass(source: File, subclassName: String, superclassName: String, isModule: Boolean) =
delegate.foundSubclass(srcPath(source), subclassName, superclassName, isModule)
def foundAnnotated(source: File, className: String, annotationName: String, isModule: Boolean) =
delegate.foundAnnotated(srcPath(source), className, annotationName, isModule)
def foundApplication(source: File, className: String) = delegate.foundApplication(srcPath(source), className)
def sourceDependency(dependsOn: File, source: File) =
delegate.sourceDependency(srcPath(dependsOn), srcPath(source))
def jarDependency(jar: File, source: File) = delegate.jarDependency(jar, srcPath(source))
def generatedClass(source: File, clazz: File) = delegate.generatedClass(srcPath(source), classPath(clazz))
def endSource(source: File) = delegate.endSource(srcPath(source))
def classDependency(clazz: File, source: File) =
{
val sourcePath = srcPath(source)
Path.relativize(outputPath, clazz) match
{
case None => // dependency is a class file outside of the output directory
delegate.classDependency(clazz, sourcePath)
case Some(relativeToOutput) => // dependency is a product of a source not included in this compilation
delegate.productDependency(relativeToOutput, sourcePath)
}
}
def relativizeOrAbs(base: Path, file: File) = Path.relativize(base, file).getOrElse(Path.fromFile(file))
def classPath(file: File) = relativizeOrAbs(outputPath, file)
def srcPath(file: File) = relativizeOrAbs(basePath, file)
def api(file: File, source: xsbti.api.Source) = delegate.api(srcPath(file), source)
}

View File

@ -5,9 +5,6 @@ package sbt
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
trait Logger extends AbstractLogger with xsbt.CompileLogger with IvyLogger
/** A logger that can buffer the logging done on it by currently executing Thread and
* then can flush the buffer to the delegate logger provided in the constructor. Use
* 'startRecording' to start buffering and then 'play' from to flush the buffer for the
@ -19,7 +16,7 @@ trait Logger extends AbstractLogger with xsbt.CompileLogger with IvyLogger
*
* This logger is thread-safe.
* */
final class BufferedLogger(delegate: AbstractLogger) extends Logger
final class BufferedLogger(delegate: Logger) extends AbstractLogger
{
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
private[this] val buffers = wrap.Wrappers.weakMap[Thread, Buffer[LogEvent]]

View File

@ -740,7 +740,8 @@ class xMain extends xsbti.AppMain
if(actionValid)
{
var count = 0
SourceModificationWatch.watchUntil(project, ContinuousCompilePollDelaySeconds)(shouldTerminate)
val sourcesFinder: PathFinder = (Path.emptyPathFinder /: project.topologicalSort)(_ +++ _.watchPaths)
SourceModificationWatch.watchUntil(sourcesFinder, ContinuousCompilePollDelaySeconds)(shouldTerminate)
{
count += 1
handleAction(project, action)

View File

@ -1,87 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package sbt
import java.io.{File, FileOutputStream}
import java.util.jar.{JarEntry, JarFile, JarOutputStream, Pack200}
import scala.collection.Map
import FileUtilities._
object Pack
{
def pack(jarPath: Path, out: Path, log: Logger): Option[String] = pack(jarPath, out, defaultPackerOptions, log)
def pack(jarPath: Path, out: Path, options: Map[String, String], log: Logger): Option[String] =
{
val packer = Pack200.newPacker
val properties = new wrap.MutableMapWrapper(packer.properties)
properties ++= options
OpenResource.jarFile(false).ioOption(jarPath.asFile, "applying pack200 compression to jar", log) { f =>
writeStream(out.asFile, log) { stream =>
packer.pack(f, stream)
None
}
}
}
def unpack(packedPath: Path, toJarPath: Path, log: Logger): Option[String] =
{
val unpacker = Pack200.newUnpacker
writeStream(toJarPath.asFile, log) { fileStream =>
val jarOut = new JarOutputStream(fileStream)
Control.trapUnitAndFinally("Error unpacking '" + packedPath + "': ", log)
{ unpacker.unpack(packedPath.asFile, jarOut); None }
{ jarOut.close() }
}
}
def defaultPackerOptions: Map[String, String] = scala.collection.immutable.Map()
}
import java.net.URL
/** This is somewhat of a mess and is not entirely correct. jarsigner doesn't work properly
* on scalaz and it is difficult to determine whether a jar is both signed and valid. */
object SignJar
{
final class SignOption private[SignJar](val toList: List[String], val signOnly: Boolean) extends NotNull
{
override def toString = toList.mkString(" ")
}
def keyStore(url: URL) = new SignOption("-keystore" :: url.toExternalForm :: Nil, true)
def signedJar(p: Path) = new SignOption("-signedjar" :: p.asFile.getAbsolutePath :: Nil, true)
def verbose = new SignOption("-verbose" :: Nil, false)
def sigFile(name: String) = new SignOption("-sigfile" :: name :: Nil, true)
def storeType(t: String) = new SignOption("-storetype" :: t :: Nil, false)
def provider(p: String) = new SignOption("-provider" :: p :: Nil, false)
def providerName(p: String) = new SignOption("-providerName" :: p :: Nil, false)
def storePassword(p: String) = new SignOption("-storepass" :: p :: Nil, true)
def keyPassword(p: String) = new SignOption("-keypass" :: p :: Nil, true)
private def VerifyOption = "-verify"
/** Uses jarsigner to sign the given jar. */
def sign(jarPath: Path, alias: String, options: Seq[SignOption], log: Logger): Option[String] =
{
require(!alias.trim.isEmpty, "Alias cannot be empty")
val arguments = options.toList.flatMap(_.toList) ::: jarPath.asFile.getAbsolutePath :: alias :: Nil
execute("Signed " + jarPath, "signing", arguments, log)
}
/** Uses jarsigner to verify the given jar.*/
def verify(jarPath: Path, options: Seq[SignOption], log: Logger): Option[String] =
{
val arguments = options.filter(!_.signOnly).toList.flatMap(_.toList) ::: VerifyOption :: jarPath.asFile.getAbsolutePath :: Nil
execute("Verified " + jarPath, "verifying", arguments, log)
}
private def execute(successMessage: String, action: String, arguments: List[String], log: Logger): Option[String] =
{
val exitCode = Process(CommandName, arguments) ! log
if(exitCode == 0)
{
log.debug(successMessage)
None
}
else
Some("Error " + action + " jar (exit code was " + exitCode + ".)")
}
private val CommandName = "jarsigner"
}

View File

@ -1,31 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mikko Peltonen, Mark Harrah
*/
package sbt
object SourceModificationWatch
{
def watchUntil(project: Project, pollDelaySec: Int)(terminationCondition: => Boolean)(onSourcesModified: => Unit)
{
def sourceFiles: Iterable[java.io.File] = sourcesFinder.getFiles
def sourcesFinder: PathFinder = (Path.emptyPathFinder /: project.topologicalSort)(_ +++ _.watchPaths)
def loop(lastCallbackCallTime: Long, previousFileCount: Int)
{
val (lastModifiedTime, fileCount) = sourceFiles.foldLeft((0L, 0)){(acc, file) => (Math.max(acc._1, file.lastModified), acc._2 + 1)}
val newCallbackCallTime =
// check if sources are modified
if (lastModifiedTime > lastCallbackCallTime || previousFileCount != fileCount)
{
val now = System.currentTimeMillis
onSourcesModified
now
}
else
lastCallbackCallTime
Thread.sleep(pollDelaySec * 1000)
if(!terminationCondition)
loop(newCallbackCallTime, fileCount)
}
loop(0L, 0)
}
}

View File

@ -1,84 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package sbt
import org.scalacheck._
import java.io.File
object WriteContentSpecification extends Properties("Write content")
{
val log = new ConsoleLogger
log.setLevel(Level.Warn)
specify("Roundtrip string", writeAndCheckString _)
specify("Roundtrip bytes", writeAndCheckBytes _)
specify("Write string overwrites", overwriteAndCheckStrings _)
specify("Write bytes overwrites", overwriteAndCheckBytes _)
specify("Append string appends", appendAndCheckStrings _)
specify("Append bytes appends", appendAndCheckBytes _)
property("Unzip doesn't stack overflow") = largeUnzip() match { case Some(msg) => error(msg); case None => true }
private def largeUnzip() =
testUnzip[ScalaObject] orElse
testUnzip[scala.tools.nsc.Global]
private def testUnzip[T](implicit mf: scala.reflect.Manifest[T]) =
unzipFile(FileUtilities.classLocationFile(mf.erasure))
private def unzipFile(jar: File) =
FileUtilities.withTemporaryDirectory(log) { tmp =>
FileUtilities.unzip(jar, Path.fromFile(tmp), log).left.toOption
}
// make the test independent of underlying platform and allow any unicode character in Strings to be encoded
val charset = java.nio.charset.Charset.forName("UTF-8")
import FileUtilities._
private def writeAndCheckString(s: String) =
{
val result = withTemporaryFile( file => writeThen(file, s)( readString(file, charset, log) ) )
handleResult[String](result, _ == s)
}
private def writeAndCheckBytes(b: Array[Byte]) =
{
val result = withTemporaryFile( file => writeThen(file, b)( readBytes(file, log) ) )
handleResult[Array[Byte]](result, _ deepEquals b)
}
private def overwriteAndCheckStrings(a: String, b: String) =
{
val result = withTemporaryFile( file => writeThen(file, a)( writeThen(file, b)( readString(file, charset, log) ) ) )
handleResult[String](result, _ == b)
}
private def overwriteAndCheckBytes(a: Array[Byte], b: Array[Byte]) =
{
val result = withTemporaryFile( file => writeThen(file, a)( writeThen(file, b)( readBytes(file, log) ) ) )
handleResult[Array[Byte]](result, _ deepEquals b)
}
private def appendAndCheckStrings(a: String, b: String) =
{
val result = withTemporaryFile( file => appendThen(file, a)( appendThen(file, b)( readString(file, charset, log) ) ) )
handleResult[String](result, _ == (a+b))
}
private def appendAndCheckBytes(a: Array[Byte], b: Array[Byte]) =
{
val result = withTemporaryFile( file => appendThen(file, a)( appendThen(file, b)( readBytes(file, log) ) ) )
handleResult[Array[Byte]](result, _ deepEquals (a++b))
}
private def withTemporaryFile[T](f: File => Either[String, T]): Either[String, T] =
doInTemporaryDirectory(log) { dir => f(new java.io.File(dir, "out")) }
private def handleResult[T](result: Either[String, T], check: T => Boolean): Boolean =
result match
{
case Left(err) => log.trace(new RuntimeException(err)); log.error(err); false
case Right(x) => check(x)
}
private def writeThen[T](file: File, content: String)(action: => Either[String, T]) =
write(file, content, charset, log).toLeft(()).right.flatMap { x =>action }
private def writeThen[T](file: File, content: Array[Byte])(action: => Either[String, T]) =
write(file, content, log).toLeft(()).right.flatMap { x =>action }
private def appendThen[T](file: File, content: String)(action: => Either[String, T]) =
append(file, content, charset, log).toLeft(()).right.flatMap { x =>action }
private def appendThen[T](file: File, content: Array[Byte])(action: => Either[String, T]) =
append(file, content, log).toLeft(()).right.flatMap { x =>action }
}

View File

@ -1,182 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2008 Mark Harrah
*/
package sbt
// Still TODO:
// inheritance- hierarchy, overriding
import org.scalacheck._
import scala.reflect.Manifest
// specify members
// specify classes
// map members to classes
// compile, instantiate base
// get all vals
// validate vals
object ReflectiveSpecification extends Properties("ReflectUtilities")
{
import ReflectiveArbitrary._
// pick other modifiers, any name, any type for the member, any type to find,
// pick a class hierarchy, select any class from that hierarchy, add member
// instantiate instance, perform reflection and verify it is empty
specify("Public found", publicFound _)
specify("Private hidden", privateHidden _)
private def publicFound(isFinal: Boolean, decl: DeclarationType, name: Identifier, rType: ReturnType,
findType: ReturnType, container: ConcreteContainer) =
{
val modifiers: Set[Modifier] = if(isFinal) Set(Final) else Set()
val member = Member(Public, modifiers, decl, name.toString, rType, true)
val shouldFind = decl != Def && rType.manifest <:< findType.manifest
allVals(Map(container -> List(member)), container, findType.manifest).isEmpty == !shouldFind
}
private def privateHidden(isFinal: Boolean, decl: DeclarationType, name: Identifier, rType: ReturnType,
findType: ReturnType, container: ConcreteContainer) =
{
val scope = Private(None)
val modifiers: Set[Modifier] = if(isFinal) Set(Final) else Set()
val member = Member(scope, modifiers, decl, name.toString, rType, true)
allVals(Map(container -> List(member)), container, findType.manifest).isEmpty
}
private def allVals(classes: Map[Container, List[Member]], check: Container, manifest: Manifest[_]) =
{
val instance = ReflectiveCreate.compileInstantiate(classes, check)
ReflectUtilities.allVals(instance)(manifest)
}
}
object ReflectiveArbitrary
{
implicit val arbIdentifier: Arbitrary[Identifier] = Arbitrary { for(id <- Gen.identifier) yield Identifier(id) }
implicit val arbDeclarationType: Arbitrary[DeclarationType] = Arbitrary { Gen.elements(Val, Def, LazyVal) }
implicit val arbModifier: Arbitrary[Modifier] = Arbitrary { Gen.elements(Final, Abstract, Override) }
// TODO: parameterize
implicit val arbType: Arbitrary[ReturnType] =
Arbitrary { Gen.elements(classes: _*) }
implicit val arbConcrete: Arbitrary[ConcreteContainer] = Arbitrary(genConcrete)
//implicit val arbContainer: Arbitrary[Container] = Arbitrary { Gen.oneOf(arbConcreteContainer, arbTrait) }
//implicit val arbTrait: Arbitrary[Trait] = Arbitrary { }
//TODO: inheritance
def genConcrete = for(name <- Gen.identifier) yield ConcreteClass(name, None, Nil)
val classes = List[ReturnType](
typ[String]("String", "null"),
typ[Object]("Object", "null"),
typ[Int]("Int", "0"),
typ[List[String]]("List[String]", "Nil"),
typ[Option[Int]]("Option[Int]", "None") )
def typ[T](name: String, defaultValue: String)(implicit mf: Manifest[T]) =
BasicReturnType(name, Nil, mf, defaultValue)
}
object ReflectiveCreate
{
import scala.collection.mutable
def compileInstantiate(classes: Map[Container, List[Member]], instantiate: Container): AnyRef =
{
val log = new ConsoleLogger
log.setLevel(Level.Warn)
val code = new StringBuilder
def addMember(m: Member)
{
code.append(m.toString)
code.append("\n")
}
def addClass(c: Container, m: List[Member])
{
code.append(c.signature)
code.append(" {\n")
m.foreach(addMember)
code.append(" }\n")
}
for((c, members) <- classes) addClass(c, members)
val codeString = code.toString
def doCompileInstantiate(dir: java.io.File): Either[String, AnyRef] =
{
val basePath = new ProjectDirectory(dir)
val source = basePath / "a.scala"
val sourceFile = source.asFile
val outputDirectory = basePath / "target"
for(writeOK <- FileUtilities.write(sourceFile, codeString, log).toLeft("").right;
compileOK <- Left("").right)//(new RawCompile(100))("reflect", source :: Nil, "", outputDirectory, Nil, log).toLeft("").right)
yield
{
error("Reintegrate")
val loader = new java.net.URLClassLoader(Array(outputDirectory.asURL), getClass.getClassLoader)
val c = Class.forName(instantiate.name, true, loader)
c.newInstance.asInstanceOf[AnyRef]
}
}
FileUtilities.doInTemporaryDirectory(log)(doCompileInstantiate) match
{
case Left(err) => log.error(err); log.error(codeString); throw new RuntimeException(err)
case Right(x) => x
}
}
}
final case class Identifier(override val toString: String) extends NotNull
sealed abstract class Modifier(override val toString: String) extends NotNull
object Final extends Modifier("final")
object Abstract extends Modifier("abstract")
object Override extends Modifier("override")
sealed trait Scope extends NotNull
sealed abstract class QualifiedScope(label: String, qualifier: Option[String]) extends Scope
{
override def toString = label + qualifier.map("[" + _ + "]").getOrElse("")
}
final case class Private(qualifier: Option[String]) extends QualifiedScope("private", qualifier)
final case class Protected(qualifier: Option[String]) extends QualifiedScope("protected", qualifier)
final object Public extends Scope { override def toString = "" }
sealed abstract class DeclarationType(override val toString: String) extends NotNull
object LazyVal extends DeclarationType("lazy val")
object Val extends DeclarationType("val")
object Def extends DeclarationType("def")
sealed abstract class Container(prefix: String) extends NotNull
{
def signature: String = prefix + " " + name + parents.map(_.name).mkString(" extends ", " with ", "")
def name: String
def mixins: List[Trait]
def parents: List[Container] = mixins
}
sealed abstract class ClassContainer(prefix: String) extends Container(prefix)
{
def base: Option[ClassContainer]
override def parents = base.toList ::: mixins
}
sealed abstract class ConcreteContainer(prefix: String) extends ClassContainer(prefix)
final case class AbstractClass(name: String, base: Option[ClassContainer], mixins: List[Trait]) extends ClassContainer("abstract class")
final case class ConcreteClass(name: String, base: Option[ClassContainer], mixins: List[Trait]) extends ConcreteContainer("class")
final case class Module(name: String, base: Option[ClassContainer], mixins: List[Trait]) extends ConcreteContainer("object")
final case class Trait(name: String, mixins: List[Trait]) extends Container("trait")
trait ReturnType
{
def name: String
def parameters: List[ReturnType]
def manifest: Manifest[_]
def defaultValue: String
override def toString = name
}
sealed case class BasicReturnType(name: String, parameters: List[ReturnType],
manifest: Manifest[_], defaultValue: String) extends ReturnType
case class Member(scope: Scope, modifiers: Set[Modifier], declaration: DeclarationType,
name: String, mType: ReturnType, valueSpecified: Boolean) extends NotNull
{
override def toString = scope.toString + modifiers.mkString(" ", " "," ") +
declaration.toString + " " + name + " : " + mType.toString +
(if(valueSpecified) " = " + mType.defaultValue else "")
}

3
testing/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Simple Build Tool: Testing Component
Copyright 2008, 2009, 2010 Steven Blundy, Mark Harrah, Josh Cough
Licensed under BSD-style license (see LICENSE)

View File

@ -6,7 +6,7 @@ package sbt
import java.net.URLClassLoader
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, SubclassFingerprint, TestFingerprint}
import org.scalatools.testing.{Event, EventHandler, Framework, Runner, Runner2, Logger=>TLogger}
import xsbt.ScalaInstance
import classpath.{ClasspathUtilities, DualLoader, FilteredLoader}
object Result extends Enumeration
{
@ -44,7 +44,7 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint) exten
}
}
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger) extends NotNull
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger)
{
private[this] val delegate = framework.testRunner(loader, listeners.flatMap(_.contentLogger).toArray)
private[this] def run(testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
@ -89,7 +89,7 @@ final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq
TestFramework.safeForeach(listeners, log)(call)
}
final class NamedTestTask(val name: String, action: => Option[String]) extends NotNull { def run() = action }
final class NamedTestTask(val name: String, action: => Unit) { def run() = action }
object TestFramework
{
@ -107,7 +107,7 @@ object TestFramework
private val TestFinishName = "test-finish"
private[sbt] def safeForeach[T](it: Iterable[T], log: Logger)(f: T => Unit): Unit =
it.foreach(i => Control.trapAndLog(log){ f(i) } )
it.foreach(i => try f(i) catch { case e: Exception => log.trace(e); log.error(e.toString) })
def matches(a: Fingerprint, b: Fingerprint) =
(a, b) match
@ -126,8 +126,8 @@ object TestFramework
log: Logger,
listeners: Seq[TestReportListener],
endErrorsEnabled: Boolean,
setup: Iterable[() => Option[String]],
cleanup: Iterable[() => Option[String]],
setup: Iterable[() => Unit],
cleanup: Iterable[() => Unit],
testArgsByFramework: Map[TestFramework, Seq[String]]):
(Iterable[NamedTestTask], Iterable[NamedTestTask], Iterable[NamedTestTask]) =
{
@ -135,7 +135,7 @@ object TestFramework
val arguments = immutable.Map() ++
( for(framework <- frameworks; created <- framework.create(loader, log)) yield
(created, testArgsByFramework.getOrElse(framework, Nil)) )
val cleanTmp = () => FileUtilities.clean(tempDir, log)
val cleanTmp = () => IO.delete(tempDir)
val mappedTests = testMap(arguments.keys.toList, tests, arguments)
if(mappedTests.isEmpty)
@ -162,12 +162,12 @@ object TestFramework
assignTests()
(immutable.Map() ++ map) transform { (framework, tests) => (tests, args(framework)) }
}
private def createTasks(work: Iterable[() => Option[String]], baseName: String) =
private def createTasks(work: Iterable[() => Unit], baseName: String) =
work.toList.zipWithIndex.map{ case (work, index) => new NamedTestTask(baseName + " " + (index+1), work()) }
private def createTestTasks(loader: ClassLoader, tests: Map[Framework, (Set[TestDefinition], Seq[String])], log: Logger,
listeners: Seq[TestReportListener], endErrorsEnabled: Boolean, setup: Iterable[() => Option[String]],
cleanup: Iterable[() => Option[String]]) =
listeners: Seq[TestReportListener], endErrorsEnabled: Boolean, setup: Iterable[() => Unit],
cleanup: Iterable[() => Unit]) =
{
val testsListeners = listeners.filter(_.isInstanceOf[TestsListener]).map(_.asInstanceOf[TestsListener])
def foreachListenerSafe(f: TestsListener => Unit): Unit = safeForeach(testsListeners, log)(f)
@ -228,7 +228,7 @@ object TestFramework
val filterCompilerLoader = new FilteredLoader(scalaInstance.loader, ScalaCompilerJarPackages)
val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.")
val notInterfaceFilter = (name: String) => !interfaceFilter(name)
val dual = new xsbt.DualLoader(filterCompilerLoader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
val dual = new DualLoader(filterCompilerLoader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
ClasspathUtilities.makeLoader(classpath, dual, scalaInstance)
}
}

View File

@ -1,7 +1,8 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
package sbt
package classpath
import java.io.File
import java.net.{URI, URL, URLClassLoader}

View File

@ -2,14 +2,14 @@
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
package classpath
import java.io.File
import java.net.{URI, URL, URLClassLoader}
import java.util.Collections
import scala.collection.Set
import scala.collection.mutable.{HashSet, ListBuffer}
import xsbt.ScalaInstance
import xsbt.FileUtilities.{createTemporaryDirectory, withTemporaryDirectory, write}
import scala.collection.mutable.{HashSet, LinkedHashSet, ListBuffer}
import IO.{createTemporaryDirectory, write}
object ClasspathUtilities
{
@ -57,7 +57,7 @@ object ClasspathUtilities
}
private[sbt] def printSource(c: Class[_]) =
println(c.getName + " loader=" +c.getClassLoader + " location=" + FileUtilities.classLocationFile(c))
println(c.getName + " loader=" +c.getClassLoader + " location=" + IO.classLocationFile(c))
def isArchive(path: Path): Boolean = isArchive(path.asFile)
def isArchive(file: File): Boolean = isArchiveName(file.getName)
@ -66,16 +66,16 @@ object ClasspathUtilities
private[sbt] def separate(paths: Iterable[File]): (Iterable[File], Iterable[File]) = paths.partition(isArchive)
// Partitions the given classpath into (jars, directories)
private[sbt] def separatePaths(paths: Iterable[Path]) = separate(paths.map(_.asFile.getCanonicalFile))
private[sbt] def buildSearchPaths(classpath: Iterable[Path]): (wrap.Set[File], wrap.Set[File]) =
private[sbt] def buildSearchPaths(classpath: Iterable[Path]): (collection.Set[File], collection.Set[File]) =
{
val (jars, dirs) = separatePaths(classpath)
(linkedSet(jars ++ extraJars.toList), linkedSet(dirs ++ extraDirs.toList))
}
private[sbt] def onClasspath(classpathJars: wrap.Set[File], classpathDirectories: wrap.Set[File], file: File): Boolean =
private[sbt] def onClasspath(classpathJars: collection.Set[File], classpathDirectories: collection.Set[File], file: File): Boolean =
{
val f = file.getCanonicalFile
if(ClasspathUtilities.isArchive(f))
classpathJars.contains(f)
classpathJars(f)
else
classpathDirectories.toList.find(Path.relativize(_, f).isDefined).isDefined
}
@ -83,8 +83,9 @@ object ClasspathUtilities
/** Returns all entries in 'classpath' that correspond to a compiler plugin.*/
private[sbt] def compilerPlugins(classpath: Iterable[Path]): Iterable[File] =
{
import collection.JavaConversions._
val loader = new URLClassLoader(Path.getURLs(classpath))
wrap.Wrappers.toList(loader.getResources("scalac-plugin.xml")).flatMap(asFile(true))
loader.getResources("scalac-plugin.xml").toList.flatMap(asFile(true))
}
/** Converts the given URL to a File. If the URL is for an entry in a jar, the File for the jar is returned. */
private[sbt] def asFile(url: URL): List[File] = asFile(false)(url)
@ -94,7 +95,7 @@ object ClasspathUtilities
{
url.getProtocol match
{
case "file" if !jarOnly=> FileUtilities.toFile(url) :: Nil
case "file" if !jarOnly=> IO.toFile(url) :: Nil
case "jar" =>
val path = url.getPath
val end = path.indexOf('!')
@ -109,7 +110,7 @@ object ClasspathUtilities
{
import scala.tools.nsc.GenericRunnerCommand
val settings = (new GenericRunnerCommand(Nil, message => error(message))).settings
val bootPaths = FileUtilities.pathSplit(settings.bootclasspath.value).map(p => new File(p)).toList
val bootPaths = IO.pathSplit(settings.bootclasspath.value).map(p => new File(p)).toList
val (bootJars, bootDirs) = separate(bootPaths)
val extJars =
{
@ -120,41 +121,21 @@ object ClasspathUtilities
for(dir <- dir.listFiles(DirectoryFilter))
findJars(dir)
}
for(path <- FileUtilities.pathSplit(settings.extdirs.value); val dir = new File(path) if dir.isDirectory)
for(path <- IO.pathSplit(settings.extdirs.value); val dir = new File(path) if dir.isDirectory)
findJars(dir)
buffer.readOnly.map(_.getCanonicalFile)
}
(linkedSet(extJars ++ bootJars), linkedSet(bootDirs))
}
private def linkedSet[T](s: Iterable[T]): wrap.Set[T] =
private def linkedSet[T](s: Iterable[T]): Set[T] =
{
val set = new wrap.MutableSetWrapper(new java.util.LinkedHashSet[T])
val set = new LinkedHashSet[T]
set ++= s
set.readOnly
set
}
}
private abstract class LoaderBase(urls: Array[URL], parent: ClassLoader) extends URLClassLoader(urls, parent) with NotNull
{
require(parent != null) // included because a null parent is legitimate in Java
@throws(classOf[ClassNotFoundException])
override final def loadClass(className: String, resolve: Boolean): Class[_] =
{
val loaded = findLoadedClass(className)
val found =
if(loaded == null)
doLoadClass(className)
else
loaded
if(resolve)
resolveClass(found)
found
}
protected def doLoadClass(className: String): Class[_]
protected final def selfLoadClass(className: String): Class[_] = super.loadClass(className, false)
}
private class IntermediateLoader(urls: Array[URL], parent: ClassLoader) extends LoaderBase(urls, parent) with NotNull
private class IntermediateLoader(urls: Array[URL], parent: ClassLoader) extends LoaderBase(urls, parent)
{
def doLoadClass(className: String): Class[_] =
{
@ -163,12 +144,12 @@ private class IntermediateLoader(urls: Array[URL], parent: ClassLoader) extends
if(className.startsWith(Loaders.SbtPackage))
findClass(className)
else
selfLoadClass(className)
defaultLoadClass(className)
}
}
/** Delegates class loading to `parent` for all classes included by `filter`. An attempt to load classes excluded by `filter`
* results in a `ClassNotFoundException`.*/
private class FilteredLoader(parent: ClassLoader, filter: ClassFilter) extends ClassLoader(parent) with NotNull
class FilteredLoader(parent: ClassLoader, filter: ClassFilter) extends ClassLoader(parent)
{
require(parent != null) // included because a null parent is legitimate in Java
def this(parent: ClassLoader, excludePackages: Iterable[String]) = this(parent, new ExcludePackagesFilter(excludePackages))
@ -182,7 +163,7 @@ private class FilteredLoader(parent: ClassLoader, filter: ClassFilter) extends C
throw new ClassNotFoundException(className)
}
}
private class SelectiveLoader(urls: Array[URL], parent: ClassLoader, filter: ClassFilter) extends URLClassLoader(urls, parent) with NotNull
private class SelectiveLoader(urls: Array[URL], parent: ClassLoader, filter: ClassFilter) extends URLClassLoader(urls, parent)
{
require(parent != null) // included because a null parent is legitimate in Java
def this(urls: Array[URL], parent: ClassLoader, includePackages: Iterable[String]) = this(urls, parent, new IncludePackagesFilter(includePackages))
@ -201,26 +182,26 @@ private class SelectiveLoader(urls: Array[URL], parent: ClassLoader, filter: Cla
}
}
}
private trait ClassFilter
trait ClassFilter
{
def include(className: String): Boolean
}
private abstract class PackageFilter(packages: Iterable[String]) extends ClassFilter
abstract class PackageFilter(packages: Iterable[String]) extends ClassFilter
{
require(packages.forall(_.endsWith(".")))
protected final def matches(className: String): Boolean = packages.exists(className.startsWith)
}
private class ExcludePackagesFilter(exclude: Iterable[String]) extends PackageFilter(exclude)
class ExcludePackagesFilter(exclude: Iterable[String]) extends PackageFilter(exclude)
{
def include(className: String): Boolean = !matches(className)
}
private class IncludePackagesFilter(include: Iterable[String]) extends PackageFilter(include)
class IncludePackagesFilter(include: Iterable[String]) extends PackageFilter(include)
{
def include(className: String): Boolean = matches(className)
}
private class LazyFrameworkLoader(runnerClassName: String, urls: Array[URL], parent: ClassLoader, grandparent: ClassLoader)
extends LoaderBase(urls, parent) with NotNull
extends LoaderBase(urls, parent)
{
def doLoadClass(className: String): Class[_] =
{

View File

@ -1,7 +1,8 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
package sbt
package classpath
import java.net.URL
import java.util.Enumeration

View File

@ -1,3 +1,3 @@
Simple Build Tool: Classpath Component
Copyright 2008, 2009 Mark Harrah
Copyright 2008, 2009, 2010 Mark Harrah, Stuart Roebuck
Licensed under BSD-style license (see LICENSE)

View File

@ -1,7 +1,7 @@
/* sbt -- Simple Build Tool
* Copyright 2008 David MacIver, Mark Harrah
*/
package sbt;
package sbt
import scala.collection._

View File

@ -1,4 +1,7 @@
package xsbt
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package sbt
import java.io.File

3
util/complete/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Simple Build Tool: Completion Component
Copyright 2010 Mark Harrah
Licensed under BSD-style license (see LICENSE)

View File

@ -6,7 +6,7 @@ package sbt
import Using._
import ErrorHandling.translate
import java.io.{ByteArrayOutputStream, BufferedWriter, File, FileInputStream, InputStream, OutputStream}
import java.io.{BufferedReader, ByteArrayOutputStream, BufferedWriter, File, FileInputStream, InputStream, OutputStream}
import java.net.{URI, URISyntaxException, URL}
import java.nio.charset.Charset
import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest}
@ -395,10 +395,11 @@ object IO
}
def copyLastModified(sourceFile: File, targetFile: File) = targetFile.setLastModified( sourceFile.lastModified )
def defaultCharset = utf8
def write(file: File, content: String, charset: Charset = defaultCharset, append: Boolean = false): Unit =
writeCharset(file, content, charset, append) { _.write(content) }
def writeCharset[T](file: File, content: String, charset: Charset, append: Boolean)(f: BufferedWriter => T): T =
def write(file: File, content: String, charset: Charset = defaultCharset, append: Boolean = false): Unit =
writer(file, content, charset, append) { _.write(content) }
def writer[T](file: File, content: String, charset: Charset, append: Boolean = false)(f: BufferedWriter => T): T =
{
if(charset.newEncoder.canEncode(content))
fileWriter(charset, append)(file) { f }
@ -406,6 +407,9 @@ object IO
error("String cannot be encoded by charset " + charset.name)
}
def reader[T](file: File, charset: Charset = defaultCharset)(f: BufferedReader => T): T =
fileReader(charset)(file) { f }
def read(file: File, charset: Charset = defaultCharset): String =
{
val out = new ByteArrayOutputStream(file.length.toInt)
@ -428,6 +432,17 @@ object IO
out.toByteArray
}
def append(file: File, content: String, charset: Charset = defaultCharset): Unit =
write(file, content, charset, true)
def append(file: File, bytes: Array[Byte]): Unit =
writeBytes(file, bytes, true)
def write(file: File, bytes: Array[Byte]): Unit =
writeBytes(file, bytes, false)
private def writeBytes(file: File, bytes: Array[Byte], append: Boolean): Unit =
fileOutputStream(append)(file) { _.write(bytes) }
// Not optimized for large files
def readLines(file: File, charset: Charset = defaultCharset): List[String] =
{
@ -441,7 +456,7 @@ object IO
}
}
def writeLines(file: File, lines: Seq[String], charset: Charset = defaultCharset, append: Boolean = false): Unit =
writeCharset(file, lines.headOption.getOrElse(""), charset, append) { w =>
writer(file, lines.headOption.getOrElse(""), charset, append) { w =>
lines.foreach { line => w.write(line); w.newLine() }
}

79
util/io/Pack.scala Normal file
View File

@ -0,0 +1,79 @@
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package sbt
import java.io.{File, FileOutputStream}
import java.util.jar.{JarEntry, JarFile, JarOutputStream, Pack200}
import IO._
object Pack
{
def pack(jarPath: File, out: File): Unit = pack(jarPath, out, defaultPackerOptions)
def pack(jarPath: File, out: File, options: Iterable[(String, String)])
{
val packer = Pack200.newPacker
import collection.JavaConversions._
packer.properties ++= options
Using.jarFile(false)(jarPath) { f =>
Using.fileOutputStream()(out) { stream =>
packer.pack(f, stream)
}
}
}
def unpack(packedPath: File, toJarPath: File)
{
val unpacker = Pack200.newUnpacker
Using.fileOutputStream()(toJarPath) { fileStream =>
Using.jarOutputStream(fileStream) { jarOut =>
unpacker.unpack(packedPath, jarOut)
}
}
}
def defaultPackerOptions = scala.collection.immutable.Map()
}
import java.net.URL
/** This is somewhat of a mess and is not entirely correct. jarsigner doesn't work properly
* on scalaz and it is difficult to determine whether a jar is both signed and valid. */
object SignJar
{
final class SignOption private[SignJar](val toList: List[String], val signOnly: Boolean)
{
override def toString = toList.mkString(" ")
}
def keyStore(url: URL) = new SignOption("-keystore" :: url.toExternalForm :: Nil, true)
def signedJar(p: Path) = new SignOption("-signedjar" :: p.asFile.getAbsolutePath :: Nil, true)
def verbose = new SignOption("-verbose" :: Nil, false)
def sigFile(name: String) = new SignOption("-sigfile" :: name :: Nil, true)
def storeType(t: String) = new SignOption("-storetype" :: t :: Nil, false)
def provider(p: String) = new SignOption("-provider" :: p :: Nil, false)
def providerName(p: String) = new SignOption("-providerName" :: p :: Nil, false)
def storePassword(p: String) = new SignOption("-storepass" :: p :: Nil, true)
def keyPassword(p: String) = new SignOption("-keypass" :: p :: Nil, true)
private def VerifyOption = "-verify"
/** Uses jarsigner to sign the given jar. */
def sign(jarPath: File, alias: String, options: Seq[SignOption])(fork: (String, List[String]) => Int)
{
require(!alias.trim.isEmpty, "Alias cannot be empty")
val arguments = options.toList.flatMap(_.toList) ::: jarPath.getAbsolutePath :: alias :: Nil
execute("signing", arguments)(fork)
}
/** Uses jarsigner to verify the given jar.*/
def verify(jarPath: File, options: Seq[SignOption])(fork: (String, List[String]) => Int)
{
val arguments = options.filter(!_.signOnly).toList.flatMap(_.toList) ::: VerifyOption :: jarPath.getAbsolutePath :: Nil
execute("verifying", arguments)(fork)
}
private def execute(action: String, arguments: List[String])(fork: (String, List[String]) => Int)
{
val exitCode = fork(CommandName, arguments)
if(exitCode != 0)
error("Error " + action + " jar (exit code was " + exitCode + ".)")
}
private val CommandName = "jarsigner"
}

View File

@ -246,7 +246,8 @@ 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: Traversable[File]): Traversable[Path] = files.map(fromFile)
import collection.generic.{CanBuildFrom, FilterMonadic}
def fromFiles[Repr, That](files: FilterMonadic[File, Repr])(implicit bf: CanBuildFrom[Repr, Path, That]): That = 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

View File

@ -0,0 +1,37 @@
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mikko Peltonen, Stuart Roebuck, Mark Harrah
*/
package sbt
object SourceModificationWatch
{
def watchUntil(sourcesFinder: PathFinder, pollDelaySec: Int)(terminationCondition: => Boolean)(onSourcesModified: => Unit)
{
def sourceFiles: Iterable[java.io.File] = sourcesFinder.getFiles
def loop(lastCallbackCallTime: Long, previousFileCount: Int, awaitingQuietPeriod:Boolean)
{
val (lastModifiedTime, fileCount) =
( (0L, 0) /: sourceFiles) {(acc, file) => (math.max(acc._1, file.lastModified), acc._2 + 1)}
val sourcesModified =
lastModifiedTime > lastCallbackCallTime ||
previousFileCount != fileCount
val newCallbackCallTime =
if (sourcesModified && !awaitingQuietPeriod)
System.currentTimeMillis
else if (!sourcesModified && awaitingQuietPeriod)
{
onSourcesModified
lastCallbackCallTime
}
else
lastCallbackCallTime
Thread.sleep(pollDelaySec * 1000)
if(!terminationCondition)
loop(newCallbackCallTime, fileCount, sourcesModified)
}
loop(0L, 0, false)
}
}

View File

@ -0,0 +1,78 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package sbt
import org.scalacheck._
import Prop._
import Arbitrary.{arbString => _, arbChar => _, _}
import java.io.{File, IOException}
object WriteContentSpecification extends Properties("Write content")
{
property("Roundtrip string") = forAll( writeAndCheckString _)
property("Roundtrip bytes") = forAll(writeAndCheckBytes _)
property("Write string overwrites") = forAll(overwriteAndCheckStrings _)
property("Write bytes overwrites") = forAll( overwriteAndCheckBytes _)
property("Append string appends") = forAll( appendAndCheckStrings _)
property("Append bytes appends") = forAll( appendAndCheckBytes _)
property("Unzip doesn't stack overflow") = largeUnzip()
implicit lazy val validChar: Arbitrary[Char] = Arbitrary( for(i <- Gen.choose(0, 0xd7ff)) yield i.toChar )
implicit lazy val validString: Arbitrary[String] = Arbitrary(arbitrary[List[Char]] map (_.mkString))
private def largeUnzip() =
{
testUnzip[ScalaObject]
testUnzip[scala.tools.nsc.Global]
true
}
private def testUnzip[T](implicit mf: scala.reflect.Manifest[T]) =
unzipFile(IO.classLocationFile(mf.erasure))
private def unzipFile(jar: File) =
IO.withTemporaryDirectory { tmp =>
IO.unzip(jar, tmp)
}
// make the test independent of underlying platform and allow any unicode character in Strings to be encoded
val charset = java.nio.charset.Charset.forName("UTF-8")
import IO._
private def writeAndCheckString(s: String) =
withTemporaryFile { file =>
write(file, s, charset)
read(file, charset) == s
}
private def writeAndCheckBytes(b: Array[Byte]) =
withTemporaryFile { file =>
write(file, b)
readBytes(file) sameElements b
}
private def overwriteAndCheckStrings(a: String, b: String) =
withTemporaryFile { file =>
write(file, a, charset)
write(file, b, charset)
read(file, charset) == b
}
private def overwriteAndCheckBytes(a: Array[Byte], b: Array[Byte]) =
withTemporaryFile { file =>
write(file, a)
write(file, b)
readBytes(file) sameElements b
}
private def appendAndCheckStrings(a: String, b: String) =
withTemporaryFile { file =>
append(file, a, charset)
append(file, b, charset)
read(file, charset) == (a+b)
}
private def appendAndCheckBytes(a: Array[Byte], b: Array[Byte]) =
withTemporaryFile { file =>
append(file, a)
append(file, b)
readBytes(file) sameElements (a++b)
}
private def withTemporaryFile[T](f: File => T): T =
withTemporaryDirectory { dir => f(new java.io.File(dir, "out")) }
}

View File

@ -1,7 +1,7 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package xsbt
package sbt
import sbt.{AbstractLogger, ControlEvent, Level, Log, LogEvent, SetLevel, SetTrace, Success, Trace}
import scala.collection.mutable.ListBuffer
@ -50,14 +50,14 @@ class BufferedLogger(delegate: AbstractLogger) extends AbstractLogger
def setLevel(newLevel: Level.Value)
{
buffer += new SetLevel(newLevel)
buffer += new SetLevel(newLevel)
delegate.setLevel(newLevel)
}
def getLevel = delegate.getLevel
def getTrace = delegate.getTrace
def setTrace(level: Int)
{
buffer += new SetTrace(level)
buffer += new SetTrace(level)
delegate.setTrace(level)
}

24
util/log/FullLogger.scala Normal file
View File

@ -0,0 +1,24 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
/** Promotes the simple Logger interface to the full AbstractLogger interface. */
class FullLogger(delegate: Logger, override val ansiCodesSupported: Boolean = false) extends BasicLogger
{
def trace(t: => Throwable)
{
if(traceEnabled)
delegate.trace(t)
}
def log(level: Level.Value, message: => String)
{
if(atLevel(level))
delegate.log(level, message)
}
def success(message: => String): Unit =
info(message)
def control(event: ControlEvent.Value, message: => String): Unit =
info(message)
def logAll(events: Seq[LogEvent]): Unit = events.foreach(log)
}

View File

@ -1,28 +1,20 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
import xsbti.{Logger => xLogger, F0}
abstract class AbstractLogger extends xLogger with NotNull
abstract class AbstractLogger extends Logger
{
def getLevel: Level.Value
def setLevel(newLevel: Level.Value)
def setTrace(flag: Int)
def getTrace: Int
final def traceEnabled = getTrace >= 0
def ansiCodesSupported = false
def atLevel(level: Level.Value) = level.id >= getLevel.id
def trace(t: => Throwable): Unit
final def verbose(message: => String): Unit = debug(message)
final def debug(message: => String): Unit = log(Level.Debug, message)
final def info(message: => String): Unit = log(Level.Info, message)
final def warn(message: => String): Unit = log(Level.Warn, message)
final def error(message: => String): Unit = log(Level.Error, message)
def success(message: => String): Unit
def log(level: Level.Value, message: => String): Unit
def control(event: ControlEvent.Value, message: => String): Unit
def logAll(events: Seq[LogEvent]): Unit
@ -39,11 +31,27 @@ abstract class AbstractLogger extends xLogger with NotNull
case c: ControlEvent => control(c.event, c.msg)
}
}
}
/** This is intended to be the simplest logging interface for use by code that wants to log.
* It does not include configuring the logger. */
trait Logger extends xLogger
{
final def verbose(message: => String): Unit = debug(message)
final def debug(message: => String): Unit = log(Level.Debug, message)
final def info(message: => String): Unit = log(Level.Info, message)
final def warn(message: => String): Unit = log(Level.Warn, message)
final def error(message: => String): Unit = log(Level.Error, message)
def ansiCodesSupported = false
def trace(t: => Throwable): Unit
def log(level: Level.Value, message: => String): Unit
def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
def info(msg: F0[String]): Unit = log(Level.Info, msg)
def error(msg: F0[String]): Unit = log(Level.Error, msg)
def trace(msg: F0[Throwable]) = trace(msg.apply)
def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
}
}

View File

@ -5,9 +5,9 @@ package sbt
/** Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`.
* A line is delimited by `nl`, which is by default the platform line separator.*/
class LoggerWriter(delegate: AbstractLogger, level: Level.Value, nl: String) extends java.io.Writer
class LoggerWriter(delegate: Logger, level: Level.Value, nl: String) extends java.io.Writer
{
def this(delegate: AbstractLogger, level: Level.Value) = this(delegate, level, System.getProperty("line.separator"))
def this(delegate: Logger, level: Level.Value) = this(delegate, level, System.getProperty("line.separator"))
private[this] val buffer = new StringBuilder

View File

@ -14,8 +14,6 @@ object Process
implicit def apply(command: String): ProcessBuilder = apply(command, None)
implicit def apply(command: Seq[String]): ProcessBuilder = apply (command.toArray, None)
def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command :: arguments.toList, None)
/** create ProcessBuilder with working dir set to path and extra environment variables */
def apply(command: String, cwd: Path, extraEnv: (String,String)*): ProcessBuilder = apply(command, cwd.asFile, extraEnv : _*)
/** create ProcessBuilder with working dir set to File and extra environment variables */
def apply(command: String, cwd: File, extraEnv: (String,String)*): ProcessBuilder =
apply(command, Some(cwd), extraEnv : _*)

View File

@ -41,7 +41,7 @@ private object Future
}
}
private object BasicIO
object BasicIO
{
def apply(buffer: StringBuffer, log: Option[Logger], withIn: Boolean) = new ProcessIO(input(withIn), processFully(buffer), getErr(log))
def apply(log: Logger, withIn: Boolean) = new ProcessIO(input(withIn), processFully(log, Level.Info), processFully(log, Level.Error))
@ -50,7 +50,7 @@ private object BasicIO
def ignoreOut = (i: OutputStream) => ()
final val BufferSize = 8192
final val Newline = FileUtilities.Newline
final val Newline = System.getProperty("line.separator")
def close(c: java.io.Closeable) = try { c.close() } catch { case _: java.io.IOException => () }
def processFully(log: Logger, level: Level.Value): InputStream => Unit = processFully(line => log.log(level, line))
@ -161,8 +161,8 @@ private abstract class AbstractProcessBuilder extends ProcessBuilder with SinkPa
def !<(log: Logger) = runBuffered(log, true)
private[this] def runBuffered(log: Logger, connectInput: Boolean) =
{
val log2 = new BufferedLogger(log)
log2.bufferAll { run(log2, connectInput).exitValue() }
val log2 = new BufferedLogger(new FullLogger(log))
log2.buffer { run(log2, connectInput).exitValue() }
}
def !(io: ProcessIO) = run(io).exitValue()

View File

@ -12,13 +12,13 @@ object ProcessSpecification extends Properties("Process I/O")
implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary(Gen.choose(0, 10) flatMap { size => Gen.resize(size, Arbitrary.arbArray[Byte].arbitrary) })
specify("Correct exit code", (exitCode: Byte) => checkExit(exitCode))
specify("#&& correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #&& _)(_ && _))
specify("#|| correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _))
specify("### correct", (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))
specify("Pipe to output file", (data: Array[Byte]) => checkFileOut(data))
specify("Pipe to input file", (data: Array[Byte]) => checkFileIn(data))
specify("Pipe to process", (data: Array[Byte]) => checkPipe(data))
/*property("Correct exit code") = forAll( (exitCode: Byte) => checkExit(exitCode))
property("#&& correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #&& _)(_ && _))
property("#|| correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _))
property("### correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))*/
property("Pipe to output file") = forAll( (data: Array[Byte]) => checkFileOut(data))
property("Pipe to input file") = forAll( (data: Array[Byte]) => checkFileIn(data))
property("Pipe to process") = forAll( (data: Array[Byte]) => checkPipe(data))
private def checkBinary(codes: Array[Byte])(reduceProcesses: (ProcessBuilder, ProcessBuilder) => ProcessBuilder)(reduceExit: (Boolean, Boolean) => Boolean) =
{
@ -64,14 +64,13 @@ object ProcessSpecification extends Properties("Process I/O")
val temporaryFile2 = temp()
try
{
FileUtilities.write(temporaryFile1, data, log)
IO.write(temporaryFile1, data)
val process = f(temporaryFile1, temporaryFile2)
( process ! ) == 0 &&
{
val result =
for(b1 <- FileUtilities.readBytes(temporaryFile1, log).right; b2 <- FileUtilities.readBytes(temporaryFile2, log).right) yield
b1 deepEquals b2
result.fold(error, x => x)
val b1 = IO.readBytes(temporaryFile1)
val b2 = IO.readBytes(temporaryFile2)
b1 sameElements b2
}
}
finally
@ -85,10 +84,10 @@ object ProcessSpecification extends Properties("Process I/O")
{
val ignore = echo // just for the compile dependency so that this test is rerun when TestedProcess.scala changes, not used otherwise
val thisClasspath = List(getSource[ScalaObject], getSource[sbt.Logger], getSource[sbt.SourceTag]).mkString(File.pathSeparator)
val thisClasspath = List(getSource[ScalaObject], getSource[IO.type], getSource[SourceTag]).mkString(File.pathSeparator)
"java -cp " + thisClasspath + " " + command
}
private def getSource[T](implicit mf: scala.reflect.Manifest[T]): String =
(FileUtilities.toFile(mf.erasure.getProtectionDomain.getCodeSource.getLocation)).getAbsolutePath
private def getSource[T : Manifest]: String =
IO.classLocationFile[T].getAbsolutePath
}
private trait SourceTag

View File

@ -1,6 +1,6 @@
package sbt
import java.io.File
import java.io.{File, FileNotFoundException, IOException}
object exit
{
@ -13,18 +13,19 @@ object cat
{
def main(args: Array[String])
{
val result =
try {
if(args.length == 0)
FileUtilities.transfer(System.in, System.out, log)
IO.transfer(System.in, System.out)
else
catFiles(args.toList)
result match
{
case Some(err) => System.err.println("Error: " + err); System.exit(1)
case None => System.exit(0)
System.exit(0)
} catch {
case e =>
e.printStackTrace()
System.err.println("Error: " + e.toString)
System.exit(1)
}
}
private val log = new ConsoleLogger
private def catFiles(filenames: List[String]): Option[String] =
{
filenames match
@ -32,16 +33,16 @@ object cat
case head :: tail =>
val file = new File(head)
if(file.isDirectory)
Some("Is directory: " + file)
throw new IOException("Is directory: " + file)
else if(file.exists)
{
FileUtilities.readStream(file, log) { stream =>
FileUtilities.transfer(stream, System.out, log)
Using.fileInputStream(file) { stream =>
IO.transfer(stream, System.out)
}
catFiles(tail)
}
else
Some("No such file or directory: " + file)
throw new FileNotFoundException("No such file or directory: " + file)
case Nil => None
}
}