compile, discover, append commands

This commit is contained in:
Mark Harrah 2010-08-04 19:51:12 -04:00
parent ab78c8791a
commit 23bda55124
5 changed files with 120 additions and 24 deletions

View File

@ -88,8 +88,15 @@ ReloadCommand + """
"""
def Multi = ";"
def MultiBrief = ("( " + Multi + " command )+", MultiDetailed)
def MultiDetailed = "Runs the provided semicolon-separated commands."
def MultiBrief = ("( " + Multi + " command )+", "Runs the provided semicolon-separated commands.")
def MultiDetailed =
Multi + " command1 " + Multi + """ command2 ...
Runs the specified commands.
"""
def Append = "append"
def AppendLastBrief = (Append + " command", AppendLastDetailed)
def AppendLastDetailed = "Appends `command` to list of commands to run."
val AliasCommand = "alias"
def AliasBrief = (AliasCommand, "Adds, removes, or prints command aliases.")
@ -112,6 +119,48 @@ AliasCommand + """ name=
Removes the alias for `name`.
"""
def Discover = "discover"
def DiscoverBrief = (DiscoverSyntax, "Finds annotated classes and subclasses.")
def DiscoverSyntax = Discover + " [-module true|false] [-sub <names>] [-annot <names>]"
def DiscoverDetailed =
DiscoverSyntax + """
Looks for public, concrete classes that match the requested query using the current sbt.inc.Analysis instance.
-module
Specifies whether modules (true) or classes (false) are found.
The default is classes/traits (false).
-sub
Specifies comma-separated class names.
Classes that have one or more of these classes as an ancestor are included in the resulting list.
-annot
Specifies comma-separated annotation names.
Classes with one or more of these annotations on the class or one of its non-private methods are included in the resulting list.
"""
def Compile = "compile"
def CompileBrief = (CompileSyntax, "Incrementally compiles the provided sources.")
def CompileSyntax = Compile + " -src <paths> [-cp <paths>] [-d <path>]"
def CompileDetailed =
CompileSyntax + """
Incrementally compiles Scala and Java sources.
Java source support is limited at this time.
<paths> are explicit paths separated by the platform path separator.
The specified output path will contain the following directory structure:
scala_<version>/
classes/
cache/
Compiled classes will be written to the 'classes' directory.
Cached information about the compilation will be written to 'cache'.
"""
def Load = "load"
def LoadLabel = "a project"
def LoadCommand = "load-commands"

View File

@ -25,9 +25,9 @@ object Build
case BinaryLoad(classpath, module, name) =>
binary(classpath, module, name, loader(configuration), allowMultiple)
case SourceLoad(classpath, sourcepath, output, module, auto, name) =>
source(classpath, sourcepath, output, module, auto, name, configuration, allowMultiple)._1
source(classpath, sourcepath, output, module, auto, name, configuration, allowMultiple)
case ProjectLoad(base, auto, name) =>
project(base, auto, name, configuration, allowMultiple)._1
project(base, auto, name, configuration, allowMultiple)
}
def project(base: File, auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration, allowMultiple: Boolean): Seq[Any] =
@ -48,33 +48,33 @@ object Build
}
}
def source(classpath: Seq[File], sources: Seq[File], output: Option[File], module: Boolean, auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration, allowMultiple: Boolean = false): (Seq[Any], Analysis) =
def compile(command: CompileCommand, configuration: xsbti.AppConfiguration): Analysis =
{
import command._
compile(classpath, sources, output, options, configuration)
}
def compile(classpath: Seq[File], sources: Seq[File], output: Option[File], options: Seq[String], configuration: xsbti.AppConfiguration): Analysis =
compile(new Compile(classpath, sources, output, options, configuration))
def compile(conf : Compile): Analysis =
{
import conf._
// TODO: accept Logger as an argument
val log = new ConsoleLogger with Logger with sbt.IvyLogger
val scalaProvider = configuration.provider.scalaProvider
val launcher = scalaProvider.launcher
val instance = ScalaInstance(scalaProvider.version, launcher)
val out = output.getOrElse(configuration.baseDirectory / "target" asFile)
val target = out / ("scala_" + instance.actualVersion)
val outputDirectory = target / "classes"
val cacheDirectory = target / "cache"
val projectClasspath = outputDirectory.asFile +: classpath
val compileClasspath = projectClasspath ++ configuration.provider.mainClasspath.toSeq
val componentManager = new ComponentManager(launcher.globalLock, configuration.provider.components, log)
val compiler = new AnalyzingCompiler(instance, componentManager, log)
val javac = JavaCompiler.directOrFork(compiler.cp, compiler.scalaInstance)( (args: Seq[String], log: Logger) => Process("javac", args) ! log )
val agg = new AggressiveCompile(cacheDirectory)
val analysis = agg(compiler, javac, sources, compileClasspath, outputDirectory, Nil, Nil)(log)
agg(compiler, javac, sources, compileClasspath, outputDirectory, Nil, options)(log)
}
def source(classpath: Seq[File], sources: Seq[File], output: Option[File], module: Boolean, auto: Auto.Value, name: String, configuration: xsbti.AppConfiguration, allowMultiple: Boolean = false): Seq[Any] =
{
val conf = new Compile(classpath, sources, output, Nil, configuration)
val analysis = compile(conf)
val discovered = discover(analysis, module, auto, name)
val loaded = binaries(projectClasspath, module, check(discovered, allowMultiple), loader(configuration))
(loaded, analysis)
binaries(conf.projectClasspath, module, check(discovered, allowMultiple), loader(configuration))
}
def discover(analysis: inc.Analysis, module: Boolean, auto: Auto.Value, name: String): Seq[String] =
{
@ -85,6 +85,9 @@ object Build
case Annotation => discover(analysis, module, new inc.Discovery(Set.empty, Set(name)))
}
}
def discover(analysis: inc.Analysis, command: DiscoverCommand): Seq[String] =
discover(analysis, command.module, command.discovery)
def discover(analysis: inc.Analysis, module: Boolean, discovery: inc.Discovery): Seq[String] =
{
for(src <- analysis.apis.internal.values.toSeq;

24
main/build/Compiled.scala Normal file
View File

@ -0,0 +1,24 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
package build
import inc.Analysis
import java.io.File
import Path._
final class Compile(val classpath: Seq[File], val sources: Seq[File], output: Option[File], val options: Seq[String], val configuration: xsbti.AppConfiguration)
{
val scalaProvider = configuration.provider.scalaProvider
val launcher = scalaProvider.launcher
val instance = ScalaInstance(scalaProvider.version, launcher)
val out = output.getOrElse(configuration.baseDirectory / "target" asFile)
val target = out / ("scala_" + instance.actualVersion)
val outputDirectory = target / "classes"
val cacheDirectory = target / "cache"
val projectClasspath = outputDirectory.asFile +: classpath
val compileClasspath = projectClasspath ++ configuration.provider.mainClasspath.toSeq
}
final class Compiled(val config: Compile, val analysis: Analysis)

View File

@ -16,3 +16,5 @@ object Auto extends Enumeration
val Subclass, Annotation, Explicit = Value
}
final case class CompileCommand(classpath: Seq[File], sources: Seq[File], output: Option[File], options: Seq[String])
final case class DiscoverCommand(module: Boolean, discovery: inc.Discovery)

View File

@ -9,7 +9,7 @@ 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 feedback is not helpful.*/
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.")
@ -44,7 +44,7 @@ The command has the following syntax:
def error(msg: String) = throw new ParseException(msg)
def apply(commandString: String)(implicit base: File): LoadCommand =
{
val args = commandString.split("""\s+""").toSeq
val args = arguments(commandString)
val srcs = sourcepath(args)
val nme = name(args)
@ -60,6 +60,21 @@ The command has the following syntax:
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
@ -75,6 +90,9 @@ The command has the following syntax:
case Some(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("")