sbt/main/build/Parse.scala

150 lines
4.5 KiB
Scala

/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
package build
import java.io.File
final class ParseException(msg: String) extends RuntimeException(msg)
/** Parses a load command. The implementation is a quick hack.
It is not robust and errors are not helpful.*/
object Parse
{
def helpBrief(name: String, label: String): (String, String) = (name + " <options>", "Loads " + label + " according to the specified options.")
def helpDetail(name: String, label: String, multiple: Boolean) =
"Loads " + label + """ by one of the following methods:
1) binary: loads a class from an existing classpath
2) source: compiles provided sources and loads a specific class
3) project: builds from source using default settings
The command has the following syntax:
load ::= """ + name + """ ('-help' | binary | source | project)
binary ::= classpath module? name
source ::= classpath? sources out? module? (detect | name)
project ::= base? name?
base ::= '-project' path
sources ::= '-src' paths
out ::= '-d' dir
detect ::= '-auto' ('sub' | 'annot')
name ::= '-name' nameString
module ::= '-module' ('true'|'false')
classpath ::= '-cp' paths
path ::= pathChar+
paths ::= path (pathSep path)*
""" +
( if(multiple) "\nTo specify multiple names, separate them by commas." else "")
import File.{pathSeparatorChar => sep}
def error(msg: String) = throw new ParseException(msg)
def apply(commandString: String)(implicit base: File): LoadCommand =
{
val args = arguments(commandString)
val srcs = sourcepath(args)
val nme = name(args)
lazy val cp = classpath(args)
lazy val mod = module(args)
lazy val proj = project(args).getOrElse(base)
if(!srcs.isEmpty)
SourceLoad(cp, srcs, output(args), mod, auto(args), nme)
else if(!cp.isEmpty)
BinaryLoad(cp, mod.getOrElse(false), nme)
else
ProjectLoad(proj, auto(args), nme)
}
def arguments(in: String) = in.split("""\s+""").toSeq
def compile(commandString: String)(implicit base: File): CompileCommand =
{
val args = arguments(commandString)
CompileCommand(classpath(args), sourcepath(args), output(args), Nil)
}
def discover(commandString: String): DiscoverCommand =
{
val args = arguments(commandString)
val subs = names("sub", args)
val annots = names("annot", args)
DiscoverCommand(module(args), new inc.Discovery(subs, annots))
}
def auto(args: Seq[String]): Auto.Value =
getArg(args, "auto") match {
case None => Auto.Explicit
case Some("sub") => Auto.Subclass
case Some("annot") => Auto.Annotation
case Some(x) => error("Illegal auto argument '" + x + "'")
}
def module(args: Seq[String]): Option[Boolean] =
getArg(args, "module") map {
case "false" => false
case "true" => true
case x => error("Expected boolean, got '" + x + "'")
}
def names(label: String, args: Seq[String]): Set[String] =
getArg(args, label) match { case Some(ns) => ns.split(",").toSet; case None => Set.empty }
def name(args: Seq[String]): String =
getArg(args, "name") getOrElse("")
def output(args: Seq[String])(implicit base: File): Option[File] =
getArg(args, "d") map file(base)
def project(args: Seq[String])(implicit base: File): Option[File] =
getArg(args, "project") map file(base)
def pathArg(args: Seq[String], name: String)(implicit base: File): Seq[File] =
getArg(args, name).toSeq flatMap paths
def classpath(args: Seq[String])(implicit base: File): Seq[File] = pathArg(args, "cp")
def sourcepath(args: Seq[String])(implicit base: File): Seq[File] = pathArg(args, "src")
def getArg(args: Seq[String], name: String): Option[String] =
{
val opt = "-" + name
val found = args.dropWhile(_ != opt)
if(found.isEmpty)
None
else
found.drop(1).headOption match
{
case x @ Some(arg) if !arg.startsWith("-") => x
case _ => error("No argument provided for -" + name)
}
}
def paths(implicit base: File): String => Seq[File] =
_ split sep flatMap files(base)
def files(base: File)(path: String): Seq[File] = readFinder(Path.fromFile(base), path).getFiles.toSeq
def file(base: File) = (path: String) => Path.fromString(base, path).asFile
def readFinder(base: File, s: String): PathFinder =
{
val f = new File(s)
asFinder( if(f.isAbsolute) f else new File(base, s) )
}
def asFinder(f: File): PathFinder =
{
val parent = f.getParentFile
if(parent eq null)
Path.fromFile(f)
else
{
val finder = asFinder(parent)
val sub = f.getName
if(sub == "**") (finder ***) else if(sub contains "*") finder * GlobFilter(sub) else finder / sub
}
}
}