Introduces scala.tools.cmd providing command line tool

infrastructure.  For a quick look at what can be done, see

  scala.tools.cmd.Demo

For a more involved, potentially eye-straining look, see

  scala.tools.partest.PartestSpec

To experience it through the eyes of Joe Partest User, run

  test/partest

Review by community.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@21438 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
extempore 2010-04-11 00:32:00 +00:00
parent d7cbfa47b5
commit 7db525a076
28 changed files with 911 additions and 494 deletions

View File

@ -0,0 +1,107 @@
/* NEST (New Scala Test)
* Copyright 2007-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import scala.collection.mutable.ListBuffer
/** An instance of a command line, parsed according to a Spec.
*/
class CommandLine(val spec: Reference, val originalArgs: List[String]) {
def this(spec: Reference, line: String) = this(spec, Parser tokenize line)
def this(spec: Reference, args: Array[String]) = this(spec, args.toList)
import spec.{ isAnyOption, isUnaryOption, isBinaryOption, isExpandOption }
def assumeBinary = true
def enforceArity = true
def onlyKnownOptions = false
val Terminator = "--"
val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true
def mapForUnary(opt: String) = Map(opt -> ValueForUnaryOption)
def errorFn(msg: String) = println(msg)
/** argMap is option -> argument (or "" if it is a unary argument)
* residualArgs are what is left after removing the options and their args.
*/
lazy val (argMap, residualArgs) = {
val residualBuffer = new ListBuffer[String]
def isOption(s: String) = isAnyOption(s) || ((s startsWith "-") && !onlyKnownOptions)
def unknownOption(opt: String) =
errorFn("Option '%s' not recognized.".format(opt))
def missingArg(opt: String, what: String) =
errorFn("Option '%s' requires argument, found %s instead.".format(opt, what))
def loop(args: List[String]): Map[String, String] = {
def residual(xs: List[String]) = { residualBuffer ++= xs ; Map[String, String]() }
/** Returns Some(List(args)) if this option expands to an
* argument list and it's not returning only the same arg.
*/
def expand(s1: String) = {
if (isExpandOption(s1)) {
val s2 = spec expandArg s1
if (s2 == List(s1)) None
else Some(s2)
}
else None
}
args match {
case Nil => Map()
case Terminator :: xs => residual(xs)
case x :: Nil =>
expand(x) foreach (exp => return loop(exp))
if (isBinaryOption(x) && enforceArity)
missingArg(x, "EOF")
if (isUnaryOption(x)) mapForUnary(x)
else residual(args)
case x1 :: x2 :: xs =>
expand(x1) foreach (exp => return loop(exp ++ args.tail))
if (x2 == Terminator) mapForUnary(x1) ++ residual(xs)
else if (isUnaryOption(x1)) mapForUnary(x1) ++ loop(args.tail)
else if (isBinaryOption(x1)) Map(x1 -> x2) ++ loop(xs)
else residual(List(x1)) ++ loop(args.tail)
}
}
(loop(originalArgs), residualBuffer map stripQuotes toList)
}
def apply(arg: String) = argMap(arg)
def get(arg: String) = argMap get arg
def isSet(arg: String) = argMap contains arg
def getOrElse(arg: String, orElse: => String) = if (isSet(arg)) apply(arg) else orElse
override def toString() = argMap.toString + " " + residualArgs.toString
}
object CommandLine {
def apply(args: List[String], unary: List[String], binary: List[String]) = {
/** XXX Temporarily assembling a fake spec so we can continue to
* do ad-hoc parsing based on a list of unary and binary args.
* Should either clean this up or do it differently.
*/
object NoSpec extends Reference {
unary foreach (_ --? )
binary foreach (_ --| )
protected def creator(args: List[String]) = error("No Spec")
def programInfo = Spec.Names("", "")
lazy val referenceSpec = this
}
new CommandLine(NoSpec, args)
}
}

View File

@ -0,0 +1,85 @@
/* NEST (New Scala Test)
* Copyright 2007-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
/** A sample command specification for illustrative purposes.
* First take advantage of the meta-options:
*
* // this command creates an executable runner script "demo"
* % scala scala.tools.cmd.Demo --generate-runner demo
*
* // this one creates and sources a completion file - note backticks
* % `./demo --bash`
*
* // and now you have a runner with working completion
* % ./demo --<tab>
* --action --defint --int
* --bash --defstr --str
* --defenv --generate-runner --unary
*
* The normal option configuration is plausibly self-explanatory.
*/
trait DemoSpec extends Spec with Meta.StdOpts with Interpolation {
lazy val referenceSpec = DemoSpec
lazy val programInfo = Spec.Names("demo", "scala.tools.cmd.Demo")
help("""Usage: demo [<options>]""")
heading("Unary options:")
val optIsUnary = "unary" / "a unary option" --? ;
("action" / "a body which may be run") --> println("Hello, I am the --action body.")
heading("Binary options:")
val optopt = "str" / "an optional String" --|
val optoptInt = ("int" / "an optional Int") . --^[Int]
val optEnv = "defenv" / "an optional String" defaultToEnv "PATH"
val optDefault = "defstr" / "an optional String" defaultTo "default"
val optDefaultInt = "defint" / "an optional Int" defaultTo -1
val optExpand = "alias" / "an option which expands" expandTo ("--int", "15")
}
object DemoSpec extends DemoSpec with Property {
lazy val propMapper = new PropertyMapper(DemoSpec)
type ThisCommandLine = SpecCommandLine
def creator(args: List[String]) =
new SpecCommandLine(args) {
override def onlyKnownOptions = true
override def errorFn(msg: String) = { println("Error: " + msg) ; System.exit(0) }
}
}
class Demo(args: List[String]) extends {
val parsed = DemoSpec(args: _*)
} with DemoSpec with Instance {
import java.lang.reflect._
def helpMsg = DemoSpec.helpMsg
def demoSpecMethods = this.getClass.getMethods.toList
private def isDemo(m: Method) = (m.getName startsWith "opt") && !(m.getName contains "$") && (m.getParameterTypes.isEmpty)
def demoString(ms: List[Method]) = {
val longest = ms map (_.getName.length) max
val formatStr = " %-" + longest + "s: %s"
val xs = ms map (m => formatStr.format(m.getName, m.invoke(this)))
xs mkString ("Demo(\n ", "\n ", "\n)\n")
}
override def toString = demoString(demoSpecMethods filter isDemo)
}
object Demo {
def main(args: Array[String]): Unit = {
val runner = new Demo(args.toList)
if (args.isEmpty)
println(runner.helpMsg)
println(runner)
}
}

View File

@ -0,0 +1,72 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import nsc.io.{ Path, File, Directory }
import reflect.OptManifest
/** A general mechanism for defining how a command line argument
* (always a String) is transformed into an arbitrary type. A few
* example instances are in the companion object, but in general
* either IntFromString will suffice or you'll want custom transformers.
*/
abstract class FromString[+T](implicit m: OptManifest[T]) extends PartialFunction[String, T] {
def apply(s: String): T
def isDefinedAt(s: String): Boolean = true
def zero: T = apply("")
def targetString: String = m.toString
}
object FromString {
// We need these because we clash with the String => Path implicits.
private def toFile(s: String) = new File(new java.io.File(s))
private def toDir(s: String) = new Directory(new java.io.File(s))
/** Path related stringifiers.
*/
val ExistingFile: FromString[File] = new FromString[File] {
override def isDefinedAt(s: String) = toFile(s).isFile
def apply(s: String): File =
if (isDefinedAt(s)) toFile(s)
else cmd.runAndExit(println("'%s' is not an existing file." format s))
}
val ExistingDir: FromString[Directory] = new FromString[Directory] {
override def isDefinedAt(s: String) = toDir(s).isDirectory
def apply(s: String): Directory =
if (isDefinedAt(s)) toDir(s)
else cmd.runAndExit(println("'%s' is not an existing directory." format s))
}
def ExistingDirRelativeTo(root: Directory) = new FromString[Directory] {
private def resolve(s: String) = toDir(s) toAbsoluteWithRoot root toDirectory
override def isDefinedAt(s: String) = resolve(s).isDirectory
def apply(s: String): Directory =
if (isDefinedAt(s)) resolve(s)
else cmd.runAndExit(println("'%s' is not an existing directory." format resolve(s)))
}
/** Argument expander, i.e. turns single argument "foo bar baz" into argument
* list "foo", "bar", "baz".
*/
val ArgumentsFromString: FromString[List[String]] = new FromString[List[String]] {
def apply(s: String) = toArgs(s)
}
/** Identity.
*/
implicit val StringFromString: FromString[String] = new FromString[String] {
def apply(s: String): String = s
}
/** Implicit as the most likely to be useful as-is.
*/
implicit val IntFromString: FromString[Int] = new FromString[Int] {
override def isDefinedAt(s: String) = safeToInt(s).isDefined
def apply(s: String) = safeToInt(s).get
def safeToInt(s: String): Option[Int] = try Some(java.lang.Integer.parseInt(s)) catch { case _: NumberFormatException => None }
}
}

View File

@ -0,0 +1,23 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
/** The trait mixed into each instance of a specification.
*
* @see Reference
*/
trait Instance extends Spec {
def parsed: CommandLine
protected def help(str: => String): Unit = ()
def isSet(s: String) = parsed isSet toOpt(s)
def originalArgs = parsed.originalArgs
type OptionMagic = Opt.Instance
protected implicit def optionMagicAdditions(name: String) = new Opt.Instance(programInfo, parsed, name)
}

View File

@ -0,0 +1,59 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
/** Interpolation logic for generated files. The idea is to be
* able to write in terms of @@THIS@@ and @@THAT@@ and the reference
* specification knows enough to perform the substitutions. Warrants
* expansion.
*/
trait Interpolation {
self: Spec =>
private lazy val reference = referenceSpec
import reference._
object interpolate {
def mapper: Map[String, () => String] = Map(
"PROGRAM" -> (() => programInfo.runner),
"ALLOPTIONS" -> (() => options.all mkString " "),
"MAINCLASS" -> (() => programInfo.mainClass)
)
private def mark(key: String) = "@@" + key + "@@"
def apply(template: String) = mapper.foldLeft(template) { case (s, (key, f)) => s.replaceAll(mark(key), f()) }
}
}
object Interpolation {
/** A simple template for generating bash completion functions.
*/
lazy val bashTemplate = """
|_@@PROGRAM@@()
|{
| local cur opts base
| COMPREPLY=()
| cur="${COMP_WORDS[COMP_CWORD]}"
| opts="@@ALLOPTIONS@@"
|
| COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
| _filedir
| return 0
|} && complete -F _@@PROGRAM@@ @@PROGRAM@@
""".stripMargin
/** A simple template for generating a runner script.
*/
val runnerTemplate = """
|#!/bin/sh
|#
|
|scala @@MAINCLASS@@ $*
|
""".stripMargin
}

View File

@ -0,0 +1,62 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import nsc.io.File
import Interpolation._
/** Meta-options for command line tools. We could have all kinds
* of additional goodness here, but for now it's completion and script
* generation. See Demo for example usage.
*/
object Meta {
trait Opt {
def name: String
def action: () => Unit
}
trait StdOpts {
self: Spec with Interpolation =>
Bash.name --> runAndExit(Bash.action())
val runnerFileName = Runner.name --| ;
if (runnerFileName.isDefined)
runAndExit(Runner.action())
/** I think we're as close as we can get to bundling completion with
* the program given the constraints imposed by bash. This outputs
* the completion function to a tempfile and echoes ". /path/to/file"
* to the console. Place it inside backtickes like `partest --bash`
* and voila, you have absorbed command completion.
*/
object Bash extends Opt {
val name = "bash"
val action = () => {
val file = File.makeTemp("scala.cmd.bash")
file writeAll interpolate(bashTemplate)
// Would be nice to print something like this but comments are
// not always comments in bash, and breaking it is worse.
// Console println ("# Run the following line, or issue the --bash command in `backticks`.")
Console println (". " + file.normalize.path)
}
}
/** A very basic runner script.
*/
object Runner extends Opt {
val name = "generate-runner"
val action = () => {
val file = File(runnerFileName.get)
file writeAll interpolate(runnerTemplate)
file setExecutable true
()
}
}
}
}

View File

@ -0,0 +1,91 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import nsc.Properties.envOrElse
import Spec.Names
/** Machinery for what amounts to a command line specification DSL.
* It is designed so the same specification trait can be used for
* two different purposes: generating a singleton specification object
* (trait Reference) and providing well typed vals for every configurable
* option in response to any given set of arguments (trait Instance).
*/
object Opt {
trait Error {
self: Implicit =>
protected def fail(msg: String) = runAndExit(println(programInfo.runner + ": " + msg))
protected def failOption(arg: String, why: String) = fail("%s: '%s' is %s".format(opt, arg, why))
}
trait Implicit {
def name: String
def programInfo: Names
protected def opt = toOpt(name)
def --? : Boolean // --opt is set
def --> (body: => Unit): Unit // if --opt is set, execute body
def --| : Option[String] // --opt <arg: String> is optional, result is Option[String]
def --^[T: FromString] : Option[T] // --opt <arg: T> is optional, result is Option[T]
def optMap[T](f: String => T) = --| map f
/** Names.
*/
def defaultTo[T: FromString](default: T): T
def defaultToEnv(envVar: String): String
def choiceOf[T: FromString](choices: T*): Option[T]
def expandTo(args: String*): Unit
/** Help.
*/
def /(descr: String): String // --opt has help description 'descr'
}
class Reference(val programInfo: Names, val options: Reference.Accumulators, val name: String) extends Implicit {
import options._
def --? = { addUnary(opt) ; false }
def --> (body: => Unit) = { addUnary(opt) }
def --| = { addBinary(opt) ; None }
def --^[T: FromString] = { addBinary(opt) ; None }
def defaultTo[T: FromString](default: T) = { addBinary(opt) ; addHelpDefault(() => default.toString) ; default }
def defaultToEnv(envVar: String) = { addBinary(opt) ; addHelpEnvDefault(envVar) ; "" }
def choiceOf[T: FromString](choices: T*) = { addBinary(opt) ; None }
def expandTo(args: String*) = { addExpand(name, args.toList) ; addHelpAlias(() => args mkString " ") }
def /(descr: String) = returning(name)(_ => addHelp(() => helpFormatStr.format(opt, descr)))
}
class Instance(val programInfo: Names, val parsed: CommandLine, val name: String) extends Implicit with Error {
def --? = parsed isSet opt
def --> (body: => Unit) = if (parsed isSet opt) body
def --| = parsed get opt
def --^[T: FromString] = {
val fs = implicitly[FromString[T]]
--| map { arg =>
if (fs isDefinedAt arg) fs(arg)
else failOption(arg, "not a " + fs.targetString)
}
}
def defaultTo[T: FromString](default: T) = --^[T] getOrElse default
def defaultToEnv(envVar: String) = --| getOrElse envOrElse(envVar, "")
def expandTo(args: String*) = ()
def choiceOf[T: FromString](choices: T*) = {
--^[T] map { arg =>
if (choices contains arg) arg
else failOption(arg.toString, "not a valid choice from " + choices)
}
}
def /(descr: String) = name
}
}

View File

@ -0,0 +1,52 @@
/* NEST (New Scala Test)
* Copyright 2007-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import scala.util.parsing.combinator._
import scala.util.parsing.input.CharArrayReader.EofCh
/** A simple (overly so) command line parser.
* !!! This needs a thorough test suite to make sure quoting is
* done correctly and portably.
*/
trait ParserUtil extends Parsers {
class ParserPlus[+T](underlying: Parser[T]) {
def !~>[U](p: => Parser[U]): Parser[U] = (underlying ~! p) ^^ { case a~b => b }
def <~![U](p: => Parser[U]): Parser[T] = (underlying ~! p) ^^ { case a~b => a }
}
protected implicit def parser2parserPlus[T](p: Parser[T]): ParserPlus[T] = new ParserPlus(p)
}
object Parser extends RegexParsers with ParserUtil {
override def skipWhitespace = false
def elemExcept(xs: Elem*): Parser[Elem] = elem("elemExcept", x => x != EofCh && !(xs contains x))
def elemOf(xs: Elem*): Parser[Elem] = elem("elemOf", xs contains _)
def escaped(ch: Char): Parser[String] = "\\" + ch
def mkQuoted(ch: Char): Parser[String] = (
elem(ch) !~> rep(escaped(ch) | elemExcept(ch)) <~ ch ^^ (_.mkString)
| failure("Unmatched %s in input." format ch)
)
/** Apparently windows can't deal with the quotes sticking around. */
lazy val squoted: Parser[String] = mkQuoted('\'') // ^^ (x => "'%s'" format x)
lazy val dquoted: Parser[String] = mkQuoted('"') // ^^ (x => "\"" + x + "\"")
lazy val token: Parser[String] = """\S+""".r
lazy val argument: Parser[String] = squoted | dquoted | token
lazy val commandLine: Parser[List[String]] = phrase(repsep(argument, whiteSpace))
class ParseException(msg: String) extends RuntimeException(msg)
def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x))
def tokenize(line: String, errorFn: String => Unit): List[String] = {
parse(commandLine, line.trim) match {
case Success(args, _) => args
case NoSuccess(msg, rest) => errorFn(msg) ; Nil
}
}
}

View File

@ -0,0 +1,71 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import nsc.io._
import java.util.Properties
import java.io.FileInputStream
/** Contains logic for translating a property key/value pair into
* equivalent command line arguments. The default settings will
* translate, given programInfo.runner == "foo" :
*
* foo.bar=true to --bar // if --bar is unary
* foo.bar=quux to --bar quux // if --bar is binary
*/
class PropertyMapper(reference: Reference) extends (((String, String)) => List[String]) {
import reference._
lazy val RunnerName = programInfo.runner
// e.g. "partest.shootout" -> "--shootout"
def propNameToOptionName(key: String): Option[String] = (key split '.').toList match {
case List(RunnerName, name) => Some(name)
case _ => None
}
def isPassThrough(key: String): Boolean = false // e.g. "partest.options"
def onError(key: String, value: String): Unit = () // called when translate fails
def translate(key: String, value: String): List[String] = {
val opt = toOpt(key)
if (isUnaryOption(key) && isTrue(value)) List(opt)
else if (isBinaryOption(key)) List(opt, value)
else returning(Nil)(_ => onError(key, value))
}
def isTrue(value: String) = List("yes", "on", "true") contains value.toLowerCase
def apply(kv: (String, String)): List[String] = {
val (k, v) = kv
if (isPassThrough(k)) toArgs(v)
else propNameToOptionName(k) match {
case Some(optName) => translate(optName, v)
case _ => Nil
}
}
}
trait Property extends Reference {
def propMapper: PropertyMapper
override def propertyArgs: List[String] = systemPropertiesToOptions
def loadProperties(file: File): Properties =
returning(new Properties)(_ load new FileInputStream(file.path))
def systemPropertiesToOptions: List[String] =
propertiesToOptions(System.getProperties)
def propertiesToOptions(file: File): List[String] =
propertiesToOptions(loadProperties(file))
def propertiesToOptions(props: java.util.Properties): List[String] = {
import collection.JavaConversions._
propertiesToOptions(props.toList)
}
def propertiesToOptions(props: List[(String, String)]) = props flatMap propMapper
}

View File

@ -0,0 +1,98 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
import collection.mutable.ListBuffer
import nsc.Properties.envOrNone
/** Mixes in the specification trait and uses the vals therein to
* side-effect private accumulators. From this emerges formatted help,
* lists of unary and binary arguments, an apply which can creates
* instances of the specification, and etc.
*
* @see Instance
*/
trait Reference extends Spec {
lazy val options = new Reference.Accumulators()
import options._
def helpMsg = options.helpMsg
def propertyArgs: List[String] = Nil
def isUnaryOption(s: String) = unary contains toOpt(s)
def isBinaryOption(s: String) = binary contains toOpt(s)
def isExpandOption(s: String) = expansionMap contains toOpt(s)
def isAnyOption(s: String) = isUnaryOption(s) || isBinaryOption(s) || isExpandOption(s)
def expandArg(arg: String) = expansionMap.getOrElse(fromOpt(arg), List(arg))
protected def help(str: => String) = addHelp(() => str)
type ThisCommandLine <: SpecCommandLine
class SpecCommandLine(args: List[String]) extends CommandLine(Reference.this, args) { }
protected def creator(args: List[String]): ThisCommandLine
final def apply(args: String*): ThisCommandLine = creator(propertyArgs ++ args flatMap expandArg)
type OptionMagic = Opt.Reference
protected implicit def optionMagicAdditions(name: String) = new Opt.Reference(programInfo, options, name)
}
object Reference {
val MaxLine = 80
class Accumulators() {
private var _help = new ListBuffer[() => String]
private var _unary = List[String]()
private var _binary = List[String]()
private var _expand = Map[String, List[String]]()
def helpFormatStr = " %-" + longestArg + "s %s"
def defaultFormatStr = (" " * (longestArg + 7)) + "%s"
def addUnary(s: String) = _unary +:= s
def addBinary(s: String) = _binary +:= s
def addExpand(opt: String, expanded: List[String]) =
_expand += (opt -> expanded)
def mapHelp(g: String => String) = {
val idx = _help.length - 1
val f = _help(idx)
_help(idx) = () => g(f())
}
def addHelp(f: () => String) = _help += f
def addHelpAlias(f: () => String) = mapHelp { s =>
val str = "alias for '%s'" format f()
def noHelp = (helpFormatStr.format("", "")).length == s.length
val str2 = if (noHelp) str else " (" + str + ")"
s + str2
}
def addHelpDefault(f: () => String) = mapHelp { s =>
val str = "(default: %s)" format f()
if (s.length + str.length < MaxLine) s + " " + str
else defaultFormatStr.format(s, str)
}
def addHelpEnvDefault(name: String) = mapHelp { s =>
val line1 = "%s (default: %s)".format(s, name)
val envNow = envOrNone(name) map ("'" + _ + "'") getOrElse "unset"
val line2 = defaultFormatStr.format("Currently " + envNow)
line1 + "\n" + line2
}
lazy val unary = (_unary ++ _expand.keys).distinct
lazy val binary = _binary.distinct
lazy val all = unary ++ binary
lazy val expansionMap = _expand
lazy val helpMsg = _help map (f => f() + "\n") mkString
lazy val longestArg = all map (_.length) max
}
}

View File

@ -0,0 +1,45 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package cmd
/** This trait works together with others in scala.tools.cmd to allow
* declaratively specifying a command line program, with many attendant
* benefits. See scala.tools.cmd.DemoSpec for an example.
*/
trait Spec {
def referenceSpec: Reference
def programInfo: Spec.Names
protected def help(str: => String): Unit
protected def heading(str: => String): Unit = help("\n " + str)
type OptionMagic <: Opt.Implicit
protected implicit def optionMagicAdditions(s: String): OptionMagic
}
object Spec {
case class Names(runner: String, mainClass: String) { }
class Accumulator[T: FromString]() {
private var _buf: List[T] = Nil
def convert(s: String) = implicitly[FromString[T]] apply s
def apply(s: String): T = returning(convert(s))(_buf +:= _)
lazy val get = _buf
}
class Choices[T: FromString](val xs: List[T]) {
def fs: FromString[T] = implicitly[FromString[T]]
def contains(x: T) = xs contains x
override def toString = xs.mkString("{ ", ", ", " }")
}
class EnvironmentVar(val name: String) {
override def toString = "${%s}" format name
}
}

View File

@ -0,0 +1,28 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools
package object cmd {
def returning[T](x: T)(f: T => Unit): T = { f(x) ; x }
private[cmd] def debug(msg: String) = println(msg)
def runAndExit(body: => Unit): Nothing = {
body
System exit 0
error("unreachable")
}
def toOpt(s: String) = if (s startsWith "--") s else "--" + s
def fromOpt(s: String) = s stripPrefix "--"
def toArgs(line: String) = Parser tokenize line
def fromArgs(args: List[String]) = args mkString " "
def stripQuotes(s: String) = {
def isQuotedBy(c: Char) = s.length > 0 && s.head == c && s.last == c
if (List('"', '\'') exists isQuotedBy) s.tail.init else s
}
}

View File

@ -136,4 +136,16 @@ with Streamable.Chars {
true
}
/** Reflection since we're into the java 6+ API.
*/
def setExecutable(executable: Boolean, ownerOnly: Boolean = true): Boolean = {
type JBoolean = java.lang.Boolean
val method =
try classOf[JFile].getMethod("setExecutable", classOf[Boolean], classOf[Boolean])
catch { case _: NoSuchMethodException => return false }
try method.invoke(jfile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue
catch { case _: Exception => false }
}
}

View File

@ -4,15 +4,13 @@
*/
// $Id$
package scala.tools.nsc
package scala.tools
package nsc
package settings
import io.AbstractFile
import util.{ ClassPath, CommandLineParser }
import annotation.elidable
import scala.tools.util.StringOps
import scala.collection.mutable.ListBuffer
import interpreter.{ returning }
/** A mutable Settings object.
*/
@ -84,7 +82,7 @@ class MutableSettings(val errorFn: String => Unit) extends AbsSettings with Scal
/** Split the given line into parameters.
*/
def splitParams(line: String) = CommandLineParser.tokenize(line, errorFn)
def splitParams(line: String) = cmd.Parser.tokenize(line, errorFn)
/** Returns any unprocessed arguments.
*/
@ -426,7 +424,7 @@ class MutableSettings(val errorFn: String => Unit) extends AbsSettings with Scal
prependPath: StringSetting,
appendPath: StringSetting)
extends StringSetting(name, arg, descr, default) {
import ClassPath.join
import util.ClassPath.join
def prepend(s: String) = prependPath.value = join(s, prependPath.value)
def append(s: String) = appendPath.value = join(appendPath.value, s)

View File

@ -4,22 +4,18 @@
*/
// $Id$
package scala.tools.nsc
package scala.tools
package nsc
package settings
import io.AbstractFile
import util.{ ClassPath, SourceFile, CommandLineParser }
import annotation.elidable
import scala.tools.util.{ PathResolver, StringOps }
import scala.collection.mutable.{ HashSet, ListBuffer }
import scala.collection.immutable.TreeSet
import interpreter.{ returning }
import scala.tools.util.PathResolver.Defaults
import scala.collection.mutable.HashSet
trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
self: MutableSettings =>
import PathResolver.{ Defaults, Environment }
import Defaults.{ scalaUserClassPath }
import Defaults.scalaUserClassPath
/** Set of settings */
protected lazy val allSettings = HashSet[Setting]()

View File

@ -1,142 +0,0 @@
/* NEST (New Scala Test)
* Copyright 2007-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package util
import scala.util.parsing.combinator._
import scala.util.parsing.input.{ Reader }
import scala.util.parsing.input.CharArrayReader.EofCh
import scala.collection.mutable.ListBuffer
/** A simple command line parser to replace the several different
* simple ones spread around trunk.
*/
trait ParserUtil extends Parsers {
class ParserPlus[+T](underlying: Parser[T]) {
def !~>[U](p: => Parser[U]): Parser[U] = (underlying ~! p) ^^ { case a~b => b }
def <~![U](p: => Parser[U]): Parser[T] = (underlying ~! p) ^^ { case a~b => a }
}
protected implicit def parser2parserPlus[T](p: Parser[T]): ParserPlus[T] = new ParserPlus(p)
}
case class CommandLine(
args: List[String],
unaryOptions: List[String],
binaryOptions: List[String]
) {
def this(args: List[String]) = this(args, Nil, Nil)
def this(args: Array[String]) = this(args.toList, Nil, Nil)
def this(line: String) = this(CommandLineParser tokenize line, Nil, Nil)
def withUnary(xs: List[String]) = copy(unaryOptions = xs)
def withBinary(xs: List[String]) = copy(binaryOptions = xs)
def allOptions = unaryOptions ++ binaryOptions
def originalArgs = args
def assumeBinary = true
def enforceArity = true
def onlyKnownOptions = false
val Terminator = "--"
val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true
def mapForUnary(opt: String) = Map(opt -> ValueForUnaryOption)
def errorFn(msg: String) = println(msg)
/** argMap is option -> argument (or "" if it is a unary argument)
* residualArgs are what is left after removing the options and their args.
*/
lazy val (argMap, residualArgs) = {
val residualBuffer = new ListBuffer[String]
def stripQuotes(s: String) = {
def isQuotedBy(c: Char) = s.length > 0 && s.head == c && s.last == c
if (List('"', '\'') exists isQuotedBy) s.tail.init else s
}
def isValidOption(s: String) = !onlyKnownOptions || (unaryOptions contains s) || (binaryOptions contains s)
def isOption(s: String) = (s startsWith "-") && (isValidOption(s) || { unknownOption(s) ; false })
def isUnary(s: String) = isOption(s) && (unaryOptions contains s)
def isBinary(s: String) = isOption(s) && !isUnary(s) && (assumeBinary || (binaryOptions contains s))
def unknownOption(opt: String) =
errorFn("Option '%s' not recognized.".format(opt))
def missingArg(opt: String, what: String) =
errorFn("Option '%s' requires argument, found %s instead.".format(opt, what))
def loop(args: List[String]): Map[String, String] = {
def residual(xs: List[String]) = { residualBuffer ++= xs ; Map[String, String]() }
if (args.isEmpty) return Map()
val hd :: rest = args
if (rest.isEmpty) {
if (isBinary(hd) && enforceArity)
missingArg(hd, "EOF")
if (isOption(hd)) mapForUnary(hd) else residual(args)
}
else
if (hd == Terminator) residual(rest)
else {
val hd1 :: hd2 :: rest = args
if (hd2 == Terminator) mapForUnary(hd1) ++ residual(rest)
else if (isUnary(hd1)) mapForUnary(hd1) ++ loop(hd2 :: rest)
else if (isBinary(hd1)) {
// Disabling this check so
// --scalacopts "-verbose" works. We can't tell if it's quoted,
// the shell does us in.
//
// if (isOption(hd2) && enforceArity)
// missingArg(hd1, hd2)
Map(hd1 -> hd2) ++ loop(rest)
}
else { residual(List(hd1)) ++ loop(hd2 :: rest) }
}
}
(loop(args), residualBuffer map stripQuotes toList)
}
def isSet(arg: String) = args contains arg
def get(arg: String) = argMap get arg
def getOrElse(arg: String, orElse: => String) = if (isSet(arg)) apply(arg) else orElse
def apply(arg: String) = argMap(arg)
override def toString() = "CommandLine(\n%s)\n" format (args map (" " + _ + "\n") mkString)
}
object CommandLineParser extends RegexParsers with ParserUtil {
override def skipWhitespace = false
def elemExcept(xs: Elem*): Parser[Elem] = elem("elemExcept", x => x != EofCh && !(xs contains x))
def elemOf(xs: Elem*): Parser[Elem] = elem("elemOf", xs contains _)
def escaped(ch: Char): Parser[String] = "\\" + ch
def mkQuoted(ch: Char): Parser[String] = (
elem(ch) !~> rep(escaped(ch) | elemExcept(ch)) <~ ch ^^ (_.mkString)
| failure("Unmatched %s in input." format ch)
)
/** Apparently windows can't deal with the quotes sticking around. */
lazy val squoted: Parser[String] = mkQuoted('\'') // ^^ (x => "'%s'" format x)
lazy val dquoted: Parser[String] = mkQuoted('"') // ^^ (x => "\"" + x + "\"")
lazy val token: Parser[String] = """\S+""".r
lazy val argument: Parser[String] = squoted | dquoted | token
lazy val commandLine: Parser[List[String]] = phrase(repsep(argument, whiteSpace))
class ParseException(msg: String) extends RuntimeException(msg)
def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x))
def tokenize(line: String, errorFn: String => Unit): List[String] = {
parse(commandLine, line.trim) match {
case Success(args, _) => args
case NoSuccess(msg, rest) => errorFn(msg) ; Nil
}
}
def apply(line: String) = new CommandLine(tokenize(line))
}

View File

@ -1,150 +0,0 @@
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package util
import Properties._
import io._
import CommandLineSpec._
import CommandLineParser.tokenize
/** This trait works together with CommandLine to allow declaratively
* specifying a command line program, with many attendant benefits.
* See scala.tools.partest.PartestSpec for a full example.
*/
trait CommandLineSpec {
def parsed: CommandLine
def isReferenceSpec: Boolean = false
def isPassthroughProperty(name: String): Boolean = false
def isSysPropOption(key: String): Option[String] = None
protected var _expandingOptions: Map[String, List[String]] = Map()
private var _helpMessage: String = ""
private var _unaryOptions: List[String] = Nil
private var _binaryOptions: List[String] = Nil
private def allOptions = if (isReferenceSpec) Nil else parsed.allOptions
private def longestArg = if (allOptions.isEmpty) 1 else allOptions map (_.length) max
private def unquoted(s: String) = {
def isQuoted = (s.head == '\'' || s.head == '"') && s.head == s.last
if (s == null || s.length < 2 || !isQuoted) s
else s drop 1 dropRight 1
}
protected def help(str: String) = if (isReferenceSpec) () else _helpMessage += (str.stripMargin + "\n")
protected def heading(s: String) = if (isReferenceSpec) () else help("\n " + s)
/** The various operators:
* val isCond1 = "cond1" ? // --cond1 is unary, cond1 is boolean
* "cond2" ?> body // --cond2 is unary, body is executed if it is given
* "cond3" ?+> List(x1, x2...) // --cond3 is unary, arguments on rhs will be substituted in as if given
* val val1 = "val1" |> "alt" // --val1 is binary, val1 is String, alt used if none given
* val val2 = "val2" >> // --val2 is binary, val2 is Option[String], None if none given
*/
protected class OptionStringAdditions(name: String) {
val s = toOpt(name)
def ?+>(args: List[String]): Unit = { _unaryOptions +:= s ; if (isReferenceSpec) _expandingOptions += (name -> args) }
def ? : Boolean = { _unaryOptions +:= s ; if (isReferenceSpec) false else parsed isSet s }
def ?>(body: => Unit): Unit = { _unaryOptions +:= s ; if (isReferenceSpec) () else if (parsed isSet s) body }
def |>(alt: String): String = { _binaryOptions +:= s ; if (isReferenceSpec) "" else parsed.getOrElse(s, alt) }
def >> : Option[String] = { _binaryOptions +:= s ; if (isReferenceSpec) None else parsed get s }
def /(description: String) = {
val formatStr = " %-" + longestArg + "s %s"
help(formatStr.format(s, description))
name
}
}
protected implicit def stringAdditions(s: String) = new OptionStringAdditions(s)
lazy val unaryOptions = _unaryOptions.distinct
lazy val binaryOptions = _binaryOptions.distinct
lazy val expandingOptions = _expandingOptions.keys.toList
lazy val helpMsg = _helpMessage
def isUnaryOption(s: String) = unaryOptions contains toOpt(s)
def isBinaryOption(s: String) = binaryOptions contains toOpt(s)
def isExpandingOption(s: String) = expandingOptions contains toOpt(s)
private def sysPropToOptions(k: String, v: String): List[String] = {
if (isPassthroughProperty(k)) toArgs(v)
else isSysPropOption(k).toList flatMap { optName =>
val opt = toOpt(optName)
if (isUnaryOption(optName)) List(opt)
else if (isBinaryOption(optName)) List(opt, v)
else {
if (warnSuspiciousProperties) {
println("Warning, this looks like a command line option but I don't understand it.")
println("Ignoring: " + k + "=" + v)
}
Nil
}
}
}
def warnSuspiciousProperties: Boolean = true
def sysPropsAsOptions() = allSystemProperties.toList flatMap (sysPropToOptions _).tupled
def isSet(s: String) = parsed isSet toOpt(s)
def reconstruct: List[String] = {
val unary = unaryOptions filter (parsed isSet _)
val binary = binaryOptions collect { case x if parsed isSet x => List(x, parsed(x)) }
val resid = parsed.residualArgs
unary ++ binary.flatten ++ resid
}
def bashCompletion(programName: String) = {
val opts = unaryOptions ++ binaryOptions
bashCompletionTemplate.replaceAll("@@PROGRAM@@", programName).replaceAll("@@OPTIONS@@", opts mkString " ")
}
}
trait CommandLineReferenceSpec extends CommandLineSpec {
final override def isReferenceSpec: Boolean = true
final def apply(args: String*) = creator(args.toList flatMap expandArg)
protected lazy val expansionMap = _expandingOptions
protected def creator(args: List[String]) = new ThisCommandLine(args)
protected def expandArg(arg: String) = expansionMap.getOrElse(fromOpt(arg), List(arg))
class ThisCommandLine(args: List[String]) extends CommandLine(args, unaryOptions, binaryOptions) {
}
}
object CommandLineSpec {
def toOpt(s: String) = if (s startsWith "--") s else "--" + s
def fromOpt(s: String) = s stripPrefix "--"
def toArgs(line: String) = tokenize(line)
def fromArgs(args: List[String]) = args mkString " "
def allSystemProperties: Map[String, String] = {
import collection.JavaConversions._
System.getProperties.toMap
}
/** A very simple template for generating bash completion functions.
*/
val bashCompletionTemplate = """
|_@@PROGRAM@@()
|{
| local cur opts base
| COMPREPLY=()
| cur="${COMP_WORDS[COMP_CWORD]}"
| opts="@@OPTIONS@@"
|
| COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
| _filedir
| return 0
|}
|complete -F _@@PROGRAM@@ @@PROGRAM@@
""".stripMargin
}

View File

@ -4,7 +4,8 @@
*/
// $Id$
package scala.tools.nsc
package scala.tools
package nsc
package util
import java.io.{File, FileInputStream, PrintStream, IOException}
@ -290,7 +291,7 @@ object ShowPickled extends Names {
/** Option --bare suppresses numbers so the output can be diffed.
*/
def main(args: Array[String]) {
val parsed = CommandLine(args.toList, List("--bare"), Nil)
val parsed = cmd.CommandLine(args.toList, List("--bare"), Nil)
def isBare = parsed isSet "--bare"
parsed.residualArgs foreach { arg =>

View File

@ -54,6 +54,7 @@ private[scala] trait PropertiesTrait
def clearProp(name: String) = System.clearProperty(name)
def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt
def envOrNone(name: String) = Option(System getenv name)
// for values based on propFilename
def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt)

View File

@ -27,8 +27,10 @@ trait PartestCompilation {
// }
def javac(args: List[String]): Boolean = {
val allArgString = fromArgs(javacpArg :: javacOpts :: args)
// javac -d outdir -classpath <basepath> <files>
val cmd = "%s -d %s %s %s".format(javacCmd, outDir, javacpArg, fromArgs(args))
val cmd = "%s -d %s %s".format(javacCmd, outDir, allArgString)
def traceMsg =
if (isVerbose) cmd
else "%s -d %s %s".format(tracePath(Path(javacCmd)), tracePath(outDir), fromArgs(args))

View File

@ -23,8 +23,6 @@ trait Config {
* run we only allocate one worker so the output isn't interspersed.
*/
def workerTimeout = 3600 // 1 hour, probably overly generous
def testTimeout = testTimeout_ flatMap safeToInt getOrElse 900 // test timeout
def testWarning = testWarning_ flatMap safeToInt getOrElse (testTimeout / 10) // test warning
def numWorkers = if (isDryRun) 1 else propOrElse("partest.actors", "8").toInt
def expectedErrors = propOrElse("partest.errors", "0").toInt
def poolSize = (wrapAccessControl(propOrNone("actors.corePoolSize")) getOrElse "16").toInt
@ -121,6 +119,7 @@ trait Config {
"Java binaries in: " + javaBin,
"Java runtime is: " + javaInfoString,
"Java runtime options: " + (Process.javaVmArguments mkString " "),
"Javac options are: " + universe.javacOpts,
"Java options are: " + universe.javaOpts,
"Source directory is: " + src,
"Selected categories: " + (selectedCategories mkString " "),

View File

@ -6,7 +6,7 @@ package scala.tools
package partest
import nsc.io._
import nsc.util.CommandLine
import nsc.util._
import category.AllCategories
/** Global object for a Partest run. It is completely configured by the list
@ -15,10 +15,15 @@ import category.AllCategories
* for the complete list.
*/
class Partest(args: List[String]) extends {
val parsed = PartestSpecReference(args: _*)
} with Universe with PartestSpec with AllCategories {
val parsed = PartestSpec(args: _*)
} with Universe with PartestSpec with cmd.Instance with AllCategories {
debug("Partest object created with args: " + (args mkString " "))
if (parsed.propertyArgs.nonEmpty)
debug("Partest property args: " + fromArgs(parsed.propertyArgs))
debug("Partest created with args: " + fromArgs(args))
def helpMsg = PartestSpec.helpMsg
// The abstract values from Universe.
lazy val testBuildDir = searchForDir(buildDir)
@ -30,7 +35,6 @@ class Partest(args: List[String]) extends {
// Coarse validation of partest directory: holds a file called partest.
(partestDir / "partest").isFile || error("'%s' is not a valid partest directory." format partestDir)
def runSets = toArgs(parsed.getOrElse("--runsets", ""))
def specifiedTests = parsed.residualArgs map (x => Path(x).normalize)
def specifiedKinds = testKinds filter (x => isSet(x) || (runSets contains x))
def specifiedCats = specifiedKinds flatMap (x => allCategories find (_.kind == x))

View File

@ -6,9 +6,8 @@
package scala.tools
package partest
import Properties._
import nsc.io._
import nsc.util.{ CommandLine, CommandLineSpec, CommandLineReferenceSpec }
import cmd._
/** This takes advantage of bits of scala goodness to fully define a command
* line program with a minimum of duplicated code. When the specification object
@ -16,93 +15,90 @@ import nsc.util.{ CommandLine, CommandLineSpec, CommandLineReferenceSpec }
* a private accumulator. What emerges is a full list of the valid unary
* and binary arguments, as well as autogenerated help.
*/
trait PartestSpec extends CommandLineSpec {
override def isPassthroughProperty(key: String) = key == "partest.options"
override def isSysPropOption(key: String) = {
val segments = (key split '.').toList
if (segments.size == 2 && segments.head == "partest") Some(segments.last)
else None
}
private var _testKinds: List[String] = Nil
private def kind(s: String) = returning(s)(_testKinds +:= _)
def testKinds = _testKinds
def versionMsg = Properties.versionMsg
trait PartestSpec extends Spec with Meta.StdOpts with Interpolation {
def referenceSpec = PartestSpec
def programInfo = Spec.Names("partest", "scala.tools.partest.Runner")
private val kind = new Spec.Accumulator[String]()
protected def testKinds = kind.get
private implicit val tokenizeString = FromString.ArgumentsFromString // String => List[String]
help("""
|# Pro Tip! Instant bash completion: `partest --bash` (note backticks)
|Usage: partest [<options>] [<test> <test> ...]
| <test>: a path to a test designator, typically a .scala file or a directory.
| Examples: files/pos/test1.scala, files/res/bug785""")
heading ("Test categories:")
val isAll = ("all" / "run all tests (default, unless no options given)" ?)
(kind("pos") / "Compile files that are expected to build" ?)
(kind("neg") / "Compile files that are expected to fail" ?)
(kind("run") / "Test JVM backend" ?)
(kind("jvm") / "Test JVM backend" ?)
(kind("res") / "Run resident compiler scenarii" ?)
(kind("buildmanager") / "Run Build Manager scenarii" ?)
(kind("scalacheck") / "Run Scalacheck tests" ?)
(kind("script") / "Run script files" ?)
(kind("shootout") / "Run shootout tests" ?)
(kind("scalap") / "Run scalap tests" ?)
| Examples: files/pos/test1.scala, files/res/bug785
|
| Test categories:""".stripMargin)
val isAll = ("all" / "run all tests (default, unless no options given)" --?)
(kind("pos") / "Compile files that are expected to build" --?)
(kind("neg") / "Compile files that are expected to fail" --?)
(kind("run") / "Test JVM backend" --?)
(kind("jvm") / "Test JVM backend" --?)
(kind("res") / "Run resident compiler scenarii" --?)
(kind("buildmanager") / "Run Build Manager scenarii" --?)
(kind("scalacheck") / "Run Scalacheck tests" --?)
(kind("script") / "Run script files" --?)
(kind("shootout") / "Run shootout tests" --?)
(kind("scalap") / "Run scalap tests" --?)
heading ("""Test "smart" categories:""")
val grepExpr = "grep" / "run all tests with a source file containing <expr>" >>
val isFailed = "failed" / "run all tests which failed on the last run" ?
val grepExpr = "grep" / "run all tests with a source file containing <expr>" --|
val isFailed = "failed" / "run all tests which failed on the last run" --?
heading ("Specifying paths and additional flags, ~ means repository root:")
val rootDir = "rootdir" / "path from ~ to partest (default: test)" |> "test"
val buildDir = "builddir" / "path from ~ to test build (default: build/pack)" |> "build/pack"
val srcDir = "srcdir" / "path from --rootdir to sources (default: files)" |> "files"
val javaOpts = "javaopts" / "flags to java on all runs (overrides JAVA_OPTS)" |> envOrElse("JAVA_OPTS", "")
val scalacOpts = "scalacopts" / "flags to scalac on all tests (overrides SCALAC_OPTS)" |> envOrElse("SCALAC_OPTS", "")
val rootDir = "rootdir" / "path from ~ to partest" defaultTo "test"
val buildDir = "builddir" / "path from ~ to test build" defaultTo "build/pack"
val srcDir = "srcdir" / "path from --rootdir to sources" defaultTo "files"
val javaOpts = "javaopts" / "flags to java on all runs" defaultToEnv "JAVA_OPTS"
val javacOpts = "javacopts" / "flags to javac on all runs" defaultToEnv "JAVAC_OPTS"
val scalacOpts = "scalacopts" / "flags to scalac on all tests" defaultToEnv "SCALAC_OPTS"
("pack" / "alias for --builddir build/pack") ?+> List("--builddir", "build/pack")
("quick" / "alias for --builddir build/quick") ?+> List("--builddir", "build/quick")
"pack" / "" expandTo ("--builddir", "build/pack")
"quick" / "" expandTo ("--builddir", "build/quick")
heading ("Options influencing output:")
val isTrace = "trace" / "show the individual steps taken by each test" ?
val isShowDiff = "show-diff" / "show diff between log and check file" ?
val isShowLog = "show-log" / "show log on failures" ?
val isDryRun = "dry-run" / "do not run tests, only show their traces." ?
val isTerse = "terse" / "be less verbose (almost silent except for failures)" ?
val isVerbose = "verbose" / "be more verbose (additive with --trace)" ?
val isDebug = "debug" / "maximum debugging output" ?
val isAnsi = "ansi" / "print output in color" ?
val isTrace = "trace" / "show the individual steps taken by each test" --?
val isShowDiff = "show-diff" / "show diff between log and check file" --?
val isShowLog = "show-log" / "show log on failures" --?
val isDryRun = "dry-run" / "do not run tests, only show their traces." --?
val isTerse = "terse" / "be less verbose (almost silent except for failures)" --?
val isVerbose = "verbose" / "be more verbose (additive with --trace)" --?
val isDebug = "debug" / "maximum debugging output" --?
val isAnsi = "ansi" / "print output in color" --?
heading ("Other options:")
val timeout_ = "timeout" / "Overall timeout in seconds" |> "14400"
val testWarning_ = "test-warning" / "Test warning in seconds" >> ; // defaults to testTimeout / 10
val testTimeout_ = "test-timeout" / "Test timeout in seconds" >> ; // defaults to 900
val isCleanup = "cleanup" / "delete all stale files and dirs before run" ?
val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" ?
val isStats = "stats" / "collect and print statistics about the tests" ?
val isValidate = "validate" / "examine test filesystem for inconsistencies" ?
val isVersion = "version" / "print version" ?
val timeout = "timeout" / "Overall timeout in seconds" defaultTo 14400
val testWarning = "test-warning" / "Test warning in seconds" defaultTo 90
val testTimeout = "test-timeout" / "Test timeout in seconds" defaultTo 900
val isCleanup = "cleanup" / "delete all stale files and dirs before run" --?
val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" --?
val isStats = "stats" / "collect and print statistics about the tests" --?
val isValidate = "validate" / "examine test filesystem for inconsistencies" --?
"version" / "print version" --> runAndExit(println(Properties.versionMsg))
// no help for anything below this line - secret options
// mostly intended for property configuration.
val runsets = "runsets" |> ""
val isNoAlarms = ("noalarms" ?)
val isInsideAnt = ("is-in-ant" ?)
val runSets = ("runsets" --^) getOrElse Nil
val isNoAlarms = "noalarms" --?
val isInsideAnt = "is-in-ant" --?
}
object PartestSpecReference extends PartestSpec with CommandLineReferenceSpec {
import CommandLineSpec._
object PartestSpec extends PartestSpec with Property {
lazy val propMapper = new PropertyMapper(PartestSpec) {
override def isPassThrough(key: String) = key == "partest.options"
}
def parsed: CommandLine = null
override def creator(args: List[String]) =
new ThisCommandLine(args) {
override def onlyKnownOptions = true
override def errorFn(msg: String) = printAndExit("Error: " + msg)
}
def main(args: Array[String]): Unit = println(bashCompletion("partest"))
type ThisCommandLine = PartestCommandLine
class PartestCommandLine(args: List[String]) extends SpecCommandLine(args) {
override def onlyKnownOptions = true
override def errorFn(msg: String) = printAndExit("Error: " + msg)
def propertyArgs = PartestSpec.propertyArgs
}
/** Append bash completion for partest to the given file.
*/
def appendCompletionTo(f: File) = f appendAll bashCompletion("partest")
override def creator(args: List[String]): PartestCommandLine = new PartestCommandLine(args)
}

View File

@ -9,13 +9,10 @@ package partest
import nsc.io._
object Runner {
def main(mainArgs: Array[String]) {
val propArgs = PartestSpecReference.sysPropsAsOptions()
val args = (propArgs ++ mainArgs).toList
def main(args: Array[String]) {
val runner = Partest(args: _*)
import runner._
if (isVersion) return println(versionMsg)
if (args.isEmpty) return println(helpMsg)
if (isValidate) return validateAll()

View File

@ -12,19 +12,21 @@ package ant
import org.apache.tools.ant.Task
import org.apache.tools.ant.taskdefs.Java
import org.apache.tools.ant.types.{ EnumeratedAttribute, Commandline, Environment, PropertySet }
import org.apache.tools.ant.types.Environment
import scala.tools.nsc.io._
import scala.tools.nsc.util.{ ClassPath, CommandLineSpec }
import CommandLineSpec._
import scala.tools.nsc.util.ClassPath
import cmd.Spec._
class JavaTask extends Java {
override def getTaskName() = "partest"
private val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner"
private val partestRunnerClass = "scala.tools.partest.Runner"
def defaultJvmArgs = "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M"
protected def rootDir = prop("partest.rootdir") getOrElse (baseDir / "test").path
protected def partestJVMArgs = prop("partest.jvm.args") getOrElse "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M"
protected def runnerArgs = List("-usejavacp", "scala.tools.partest.Runner", "--javaopts", partestJVMArgs)
protected def partestJVMArgs = prop("partest.jvm.args") getOrElse defaultJvmArgs
protected def runnerArgs = List("-usejavacp", partestRunnerClass, "--javaopts", partestJVMArgs)
private def baseDir = Directory(getProject.getBaseDir)
private def prop(s: String) = Option(getProject getProperty s)

View File

@ -1,90 +0,0 @@
/* __ *\
** ________ ___ / / ___ Scala Parallel Testing **
** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
/**** Note -- this isn't used anymore, but I left it in for the moment. ****/
package scala.tools
package partest
package ant
import java.io.{ File => JFile }
import org.apache.tools.ant.Task
import org.apache.tools.ant.types.{ Reference, FileSet}
import scala.reflect.BeanProperty
import scala.tools.ant.sabbus.CompilationPathProperty
import scala.tools.nsc.io
import scala.tools.nsc.util.CommandLineSpec._
class PartestTask extends Task with CompilationPathProperty {
/** Used only in ant task */
@BeanProperty protected var errorOnFailed: Boolean = _
@BeanProperty protected var jUnitReportDir: JFile = _
/** Propagated to partest run via system properties */
@BeanProperty protected var debug: Boolean = _
@BeanProperty protected var javaOpts: String = _
@BeanProperty protected var partestOpts: String = _
@BeanProperty protected var runSets: String = _
@BeanProperty protected var scalacOpts: String = _
@BeanProperty protected var showDiff: Boolean = _
@BeanProperty protected var showLog: Boolean = _
@BeanProperty protected var srcDir: String = _
@BeanProperty protected var timeout: Int = _
/** Translating ant information into command line arguments. */
private def notEmpty(s: String) = s != null && s.length > 0
private def quoted(s: String) = if (s exists (_.isWhitespace)) "\"" + s.trim + "\"" else s
private def optionCollection = List[(Boolean, () => List[String])](
debug -> (() => List("--debug")),
showLog -> (() => List("--show-log")),
showDiff -> (() => List("--show-diff")),
(timeout > 0) -> (() => List("--timeout", timeout.toString)),
notEmpty(javaOpts) -> (() => List("--javaopts", javaOpts)),
notEmpty(scalacOpts) -> (() => List("--scalacopts", scalacOpts)),
notEmpty(srcDir) -> (() => List("--srcdir", srcDir)),
notEmpty(partestOpts) -> (() => toArgs(partestOpts))
)
private def antPropOrNone(name: String) = Option(getProject getProperty name)
private def antPropsToCommandLine() = {
setProp("partest.isInAnt", "true")
val partestDir = antPropOrNone("partest.dir") getOrElse error("Mandatory attribute 'partest.dir' is not set.")
val root = List("--rootdir", io.Path(partestDir).path)
val opts = optionCollection collect { case (true, f) => f() } flatten
val sets = Option(runSets).toList flatMap toArgs map toOpt
root ++ opts ++ sets
}
private def antRunTests() = {
val args = antPropsToCommandLine()
val runner = Partest(args: _*)
import runner._
normal("Ant options translate to command line: partest " + fromArgs(args))
printConfigBanner()
val result = launchTestSuite()
val msg = result.toString
if (result.hasFailures && errorOnFailed) error(msg)
else log(msg)
}
override def execute() {
try antRunTests()
catch {
case x =>
System.err.println("Uncaught exception %s in partest ant ask: aborting." format x)
x.printStackTrace()
throw x
}
}
}

View File

@ -5,7 +5,6 @@
package scala.tools
import nsc.io.{ File, Path, Process, Directory }
import nsc.util.CommandLineSpec
import java.nio.charset.CharacterCodingException
package object partest {
@ -18,13 +17,12 @@ package object partest {
private[partest] def safeLines(f: File) = safeSlurp(f) split """\r\n|\r|\n""" toList
private[partest] def safeArgs(f: File) = toArgs(safeSlurp(f))
private[partest] def safeToInt(s: String) = try Some(s.toInt) catch { case _: NumberFormatException => None }
private[partest] def isJava(f: Path) = f.isFile && (f hasExtension "java")
private[partest] def isScala(f: Path) = f.isFile && (f hasExtension "scala")
private[partest] def isJavaOrScala(f: Path) = isJava(f) || isScala(f)
private[partest] def toArgs(line: String) = CommandLineSpec toArgs line
private[partest] def fromArgs(args: List[String]) = CommandLineSpec fromArgs args
private[partest] def toArgs(line: String) = cmd toArgs line
private[partest] def fromArgs(args: List[String]) = cmd fromArgs args
/** Strings, argument lists, etc. */

View File

@ -75,11 +75,11 @@ if $cygwin; then
fi
# Reminder: substitution ${JAVA_OPTS:=-Xmx256M -Xms16M} DO NOT work on Solaris
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:MaxPermSize=128M"
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xms64M -Xmx1024M -Xss768K -XX:MaxPermSize=96M"
[ -n "$SCALAC_OPTS" ] || SCALAC_OPTS=""
export SCALAC_OPTS
export JAVA_OPTS
# export SCALAC_OPTS
# export JAVA_OPTS
export JAVACMD
${JAVACMD:=java} $JAVA_OPTS \