mirror of https://github.com/sbt/sbt.git
- 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:
parent
37185c0fb6
commit
6f3c699435
|
|
@ -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 + ")"
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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, '/')
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Simple Build Tool: Discovery Component
|
||||
Copyright 2010 Mark Harrah
|
||||
Licensed under BSD-style license (see LICENSE)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Simple Build Tool: Incremental Logic Component
|
||||
Copyright 2010 Mark Harrah
|
||||
Licensed under BSD-style license (see LICENSE)
|
||||
|
|
@ -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]]
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Simple Build Tool: Analysis Store Component
|
||||
Copyright 2010 Mark Harrah
|
||||
Licensed under BSD-style license (see LICENSE)
|
||||
|
|
@ -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)) } )
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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._
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
|
@ -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 "")
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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[_] =
|
||||
{
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008 David MacIver, Mark Harrah
|
||||
*/
|
||||
package sbt;
|
||||
package sbt
|
||||
|
||||
import scala.collection._
|
||||
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
package xsbt
|
||||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Simple Build Tool: Completion Component
|
||||
Copyright 2010 Mark Harrah
|
||||
Licensed under BSD-style license (see LICENSE)
|
||||
|
|
@ -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() }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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")) }
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 : _*)
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue