Export approximate command lines executed for 'doc', 'compile', and 'console'

This commit is contained in:
Mark Harrah 2013-02-28 17:59:38 -05:00
parent daf300f4a9
commit f2d29d8678
11 changed files with 132 additions and 40 deletions

View File

@ -82,6 +82,9 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial
def noErrors(dreporter: DelegatingReporter) = !dreporter.hasErrors && command.ok
def commandArguments(sources: Array[File]): Array[String] =
(command.settings.recreateArgs ++ sources.map(_.getAbsolutePath)).toArray[String]
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress): Unit = synchronized
{
debug(log, "Running cached compiler " + hashCode.toHexString + ", interfacing (CompilerInterface) with Scala compiler " + scala.tools.nsc.Properties.versionString)

View File

@ -11,18 +11,18 @@ import scala.tools.nsc.util.ClassPath
class ConsoleInterface
{
def commandArguments(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Array[String] =
MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String]
def run(args: Array[String], bootClasspathString: String, classpathString: String, initialCommands: String, cleanupCommands: String, loader: ClassLoader, bindNames: Array[String], bindValues: Array[Any], log: Logger)
{
val options = args.toList
lazy val interpreterSettings = MakeSettings.sync(options, log)
val compilerSettings = MakeSettings.sync(options, log)
lazy val interpreterSettings = MakeSettings.sync(args.toList, log)
val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log)
if(!bootClasspathString.isEmpty)
compilerSettings.bootclasspath.value = bootClasspathString
compilerSettings.classpath.value = classpathString
log.info(Message("Starting scala interpreter..."))
log.debug(Message(" Boot classpath: " + compilerSettings.bootclasspath.value))
log.debug(Message(" Classpath: " + compilerSettings.classpath.value))
log.info(Message(""))
val loop = new InterpreterLoop {
@ -68,6 +68,15 @@ object MakeSettings
throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg)
}
def sync(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Settings =
{
val compilerSettings = sync(args.toList, log)
if(!bootClasspathString.isEmpty)
compilerSettings.bootclasspath.value = bootClasspathString
compilerSettings.classpath.value = classpathString
compilerSettings
}
def sync(options: List[String], log: Logger) =
{
val settings = apply(options, log)

View File

@ -13,8 +13,10 @@ package compiler
* 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.*/
final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions) extends CachedCompilerProvider
final class AnalyzingCompiler private(val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions, onArgsF: Seq[String] => Unit) extends CachedCompilerProvider
{
def this(scalaInstance: xsbti.compile.ScalaInstance, provider: CompilerInterfaceProvider, cp: xsbti.compile.ClasspathOptions) =
this(scalaInstance, provider, cp, _ => ())
def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider) = this(scalaInstance, provider, ClasspathOptions.auto)
@deprecated("A Logger is no longer needed.", "0.13.0")
@ -23,6 +25,8 @@ final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, va
@deprecated("A Logger is no longer needed.", "0.13.0")
def this(scalaInstance: xsbti.compile.ScalaInstance, provider: CompilerInterfaceProvider, cp: xsbti.compile.ClasspathOptions, log: Logger) = this(scalaInstance, provider, cp)
def onArgs(f: Seq[String] => Unit): AnalyzingCompiler = new AnalyzingCompiler(scalaInstance, provider, cp, f)
def apply(sources: Seq[File], changes: DependencyChanges, classpath: Seq[File], singleOutput: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger)
{
val arguments = (new CompilerArguments(scalaInstance, cp))(Nil, classpath, None, options)
@ -39,6 +43,7 @@ final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, va
def compile(sources: Seq[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, reporter: Reporter, progress: CompileProgress, compiler: CachedCompiler)
{
onArgsF(compiler.commandArguments(sources.toArray))
call("xsbt.CompilerInterface", "run", log)(
classOf[Array[File]], classOf[DependencyChanges], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter], classOf[CompileProgress], classOf[CachedCompiler]) (
sources.toArray, changes, callback, log, reporter, progress, compiler )
@ -59,19 +64,35 @@ final class AnalyzingCompiler(val scalaInstance: xsbti.compile.ScalaInstance, va
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger, reporter: Reporter): Unit =
{
val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, Some(outputDirectory), options)
onArgsF(arguments)
call("xsbt.ScaladocInterface", "run", log) (classOf[Array[String]], classOf[xLogger], classOf[Reporter]) (
arguments.toArray[String] : Array[String], log, reporter)
}
def console(classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String, log: Logger)(loader: Option[ClassLoader] = None, bindings: Seq[(String, Any)] = Nil): Unit =
{
val arguments = new CompilerArguments(scalaInstance, cp)
val classpathString = CompilerArguments.absString(arguments.finishClasspath(classpath))
val bootClasspath = if(cp.autoBoot) arguments.createBootClasspathFor(classpath) else ""
onArgsF(consoleCommandArguments(classpath, options, log))
val (classpathString, bootClasspath) = consoleClasspaths(classpath)
val (names, values) = bindings.unzip
call("xsbt.ConsoleInterface", "run", log)(
classOf[Array[String]], classOf[String], classOf[String], classOf[String], classOf[String], classOf[ClassLoader], classOf[Array[String]], classOf[Array[Any]], classOf[xLogger])(
options.toArray[String]: Array[String], bootClasspath, classpathString, initialCommands, cleanupCommands, loader.orNull, names.toArray[String], values.toArray[Any], log)
}
private[this] def consoleClasspaths(classpath: Seq[File]): (String, String) =
{
val arguments = new CompilerArguments(scalaInstance, cp)
val classpathString = CompilerArguments.absString(arguments.finishClasspath(classpath))
val bootClasspath = if(cp.autoBoot) arguments.createBootClasspathFor(classpath) else ""
(classpathString, bootClasspath)
}
def consoleCommandArguments(classpath: Seq[File], options: Seq[String], log: Logger): Seq[String] =
{
val (classpathString, bootClasspath) = consoleClasspaths(classpath)
val argsObj = call("xsbt.ConsoleInterface", "commandArguments", log)(
classOf[Array[String]], classOf[String], classOf[String], classOf[xLogger])(
options.toArray[String]: Array[String], bootClasspath, classpathString, log)
argsObj.asInstanceOf[Array[String]].toSeq
}
def force(log: Logger): Unit = provider(scalaInstance, log)
private def call(interfaceClassName: String, methodName: String, log: Logger)(argTypes: Class[_]*)(args: AnyRef*): AnyRef =
{

View File

@ -20,10 +20,14 @@ trait JavaCompiler extends xsbti.compile.JavaCompiler
}
apply(sources, classpath, outputDirectory, options)(log)
}
def onArgs(f: Seq[String] => Unit): JavaCompiler
}
trait Javadoc
{
def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger)
def onArgs(f: Seq[String] => Unit): Javadoc
}
trait JavaTool extends Javadoc with JavaCompiler
{
@ -34,6 +38,8 @@ trait JavaTool extends Javadoc with JavaCompiler
compile(JavaCompiler.javadoc, sources, classpath, outputDirectory, options)(log)
def compile(contract: JavacContract, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger): Unit
def onArgs(f: Seq[String] => Unit): JavaTool
}
object JavaCompiler
{
@ -52,18 +58,25 @@ object JavaCompiler
}
}
def construct(f: Fork, cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaTool =
new JavaTool {
def compile(contract: JavacContract, 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 = ClasspathOptions.javac(cp.compiler)
val arguments = (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, Some(outputDirectory), options)
log.debug("Calling " + contract.name.capitalize + " with arguments:\n\t" + arguments.mkString("\n\t"))
val code: Int = f(contract, arguments, log)
log.debug(contract.name + " returned exit code: " + code)
if( code != 0 ) throw new CompileFailed(arguments.toArray, contract.name + " returned nonzero exit code", Array())
}
def construct(f: Fork, cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaTool = new JavaTool0(f, cp, scalaInstance, _ => ())
private[this] class JavaTool0(f: Fork, cp: ClasspathOptions, scalaInstance: ScalaInstance, onArgsF: Seq[String] => Unit) extends JavaTool
{
def onArgs(g: Seq[String] => Unit): JavaTool = new JavaTool0(f, cp, scalaInstance, g)
def commandArguments(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger): Seq[String] =
{
val augmentedClasspath = if(cp.autoBoot) classpath ++ Seq(scalaInstance.libraryJar) else classpath
val javaCp = ClasspathOptions.javac(cp.compiler)
(new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, Some(outputDirectory), options)
}
def compile(contract: JavacContract, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger) {
val arguments = commandArguments(sources, classpath, outputDirectory, options, log)
onArgsF(arguments)
val code: Int = f(contract, arguments, log)
log.debug(contract.name + " returned exit code: " + code)
if( code != 0 ) throw new CompileFailed(arguments.toArray, contract.name + " returned nonzero exit code", Array())
}
}
def directOrFork(cp: ClasspathOptions, scalaInstance: ScalaInstance)(implicit doFork: Fork): JavaTool =
construct(directOrForkJavac, cp, scalaInstance)

View File

@ -7,5 +7,7 @@ import java.io.File;
public interface CachedCompiler
{
/** Returns an array of arguments representing the nearest command line equivalent of a call to run but without the command name itself.*/
public String[] commandArguments(File[] sources);
public void run(File[] sources, DependencyChanges cpChanges, AnalysisCallback callback, Logger log, Reporter delegate, CompileProgress progress);
}

View File

@ -3,7 +3,7 @@
*/
package sbt
import java.io.File
import java.io.{File, PrintWriter}
import compiler.{AnalyzingCompiler, JavaCompiler}
import Predef.{conforms => _, _}
@ -20,8 +20,9 @@ object Doc
import RawCompileLike._
def scaladoc(label: String, cache: File, compiler: AnalyzingCompiler): Gen =
cached(cache, prepare(label + " Scala API documentation", compiler.doc))
def javadoc(label: String, cache: File, doc: sbt.compiler.Javadoc): Gen =
cached(cache, prepare(label + " Scala API documentation", filterSources(javaSourcesOnly, doc.doc)))
cached(cache, prepare(label + " Java API documentation", filterSources(javaSourcesOnly, doc.doc)))
val javaSourcesOnly: File => Boolean = _.getName.endsWith(".java")

View File

@ -34,6 +34,7 @@ ShowCommand + """ <setting>
val LastCommand = "last"
val LastGrepCommand = "last-grep"
val ExportCommand = "export"
val lastGrepBrief = (LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.")
val lastGrepDetailed =
@ -56,6 +57,17 @@ LastCommand + """
See also '""" + LastGrepCommand + "'."
val exportBrief = (ExportCommand, "Displays the equivalent command line(s) for previously executed tasks.")
val exportDetailed =
s"""$ExportCommand <task-key>+
Prints the approximate command line(s) for the previously executed tasks.
NOTE: These command lines are necessarily approximate. Usually tasks do not actually
execute the command line and the actual command line program may not be installed or
on the PATH. Incremental tasks will typically show the command line for the previous
incremental run and not for a full run.
"""
val InspectCommand = "inspect"
val inspectBrief = (InspectCommand, "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
val inspectDetailed =

View File

@ -4,7 +4,6 @@
package sbt
import java.io.File
import compiler.AnalyzingCompiler
object ConsoleProject
{

View File

@ -21,7 +21,7 @@ package sbt
import scala.xml.NodeSeq
import org.apache.ivy.core.module.{descriptor, id}
import descriptor.ModuleDescriptor, id.ModuleRevisionId
import java.io.File
import java.io.{File, PrintWriter}
import java.net.{URI,URL,MalformedURLException}
import java.util.concurrent.Callable
import sbinary.DefaultProtocol.StringFormat
@ -619,9 +619,11 @@ object Defaults extends BuildCommon
val label = nameForSrc(config.name)
val (options, runDoc) =
if(hasScala)
(in.config.options ++ Opts.doc.externalAPI(xapis), Doc.scaladoc(label, s.cacheDirectory / "scala", in.compilers.scalac))
(in.config.options ++ Opts.doc.externalAPI(xapis),
Doc.scaladoc(label, s.cacheDirectory / "scala", in.compilers.scalac.onArgs(exported(s, "scaladoc"))))
else if(hasJava)
(in.config.javacOptions, Doc.javadoc(label, s.cacheDirectory / "java", in.compilers.javac))
(in.config.javacOptions,
Doc.javadoc(label, s.cacheDirectory / "java", in.compilers.javac.onArgs(exported(s, "javadoc"))))
else
(Nil, RawCompileLike.nop)
runDoc(srcs, cp, out, options, in.config.maxErrors, s.log)
@ -641,15 +643,29 @@ object Defaults extends BuildCommon
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] =
(compilers in task, classpath in task, scalacOptions in task, initialCommands in task, cleanupCommands in task, taskTemporaryDirectory in task, scalaInstance in task, streams) map {
(cs, cp, options, initCommands, cleanup, temp, si, s) =>
val fullcp = (data(cp) ++ si.jars).distinct
val cpFiles = data(cp)
val fullcp = (cpFiles ++ si.jars).distinct
val loader = sbt.classpath.ClasspathUtilities.makeLoader(fullcp, si, IO.createUniqueDirectory(temp))
(new Console(cs.scalac))(data(cp), options, loader, initCommands, cleanup)()(s.log).foreach(msg => error(msg))
val compiler = cs.scalac.onArgs(exported(s, "scala"))
(new Console(compiler))(cpFiles, options, loader, initCommands, cleanup)()(s.log).foreach(msg => error(msg))
println()
}
private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit = args =>
w.println( (command +: args).mkString(" ") )
private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = args =>
exported(s.text("export"), command)
def compileTaskSettings: Seq[Setting[_]] = inTask(compile)(compileInputsSettings)
def compileTask = (compileInputs in compile, streams) map { (i,s) => Compiler(i,s.log) }
def compileTask: Initialize[Task[inc.Analysis]] = Def.task { compileTaskImpl(streams.value, (compileInputs in compile).value) }
private[this] def compileTaskImpl(s: TaskStreams, ci: Compiler.Inputs): inc.Analysis =
{
lazy val x = s.text("export")
def onArgs(cs: Compiler.Compilers) = cs.copy(scalac = cs.scalac.onArgs(exported(x, "scalac")), javac = cs.javac.onArgs(exported(x, "javac")))
val i = ci.copy(compilers = onArgs(ci.compilers))
Compiler(i,s.log)
}
def compileIncSetupTask =
(dependencyClasspath, skip in compile, definesClass, compilerCache, streams, incOptions) map { (cp, skip, definesC, cache, s, incOptions) =>
Compiler.IncSetup(analysisMap(cp), definesC, skip, s.cacheDirectory / "inc_compile", cache, incOptions)

View File

@ -77,7 +77,7 @@ object BuiltinCommands
def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, act, nop)
def DefaultCommands: Seq[Command] = Seq(ignore, help, about, tasks, settingsCommand, loadProject,
projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
setOnFailure, clearOnFailure, ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, boot, nop, call, exit, act)
setOnFailure, clearOnFailure, ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, act)
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
def boot = Command.make(BootCommand)(bootParser)
@ -295,20 +295,26 @@ object BuiltinCommands
val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
val spacedAggregatedParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.aggregatedKeyParser(s))
val aggregatedKeyValueParser: State => Parser[Option[AnyKeys]] =
(s: State) => spacedAggregatedParser(s).map(x => Act.keyValues(s)(x) ).?
val exportParser: State => Parser[AnyKeys] = (s: State) => spacedAggregatedParser(s).map(x => Act.keyValues(s)(x) )
val aggregatedKeyValueParser: State => Parser[Option[AnyKeys]] = s => exportParser(s).?
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ aggregatedKeyValueParser(s))
def last = Command(LastCommand, lastBrief, lastDetailed)(aggregatedKeyValueParser) {
case (s,Some(sks)) =>
val (str, ref, display) = extractLast(s)
Output.last(sks, str.streams(s), printLast(s))(display)
keepLastLog(s)
case (s,Some(sks)) => lastImpl(s, sks, None)
case (s, None) =>
for(logFile <- lastLogFile(s)) yield
Output.last( logFile, printLast(s) )
keepLastLog(s)
}
def export = Command(ExportCommand, exportBrief, exportDetailed)(exportParser) { (s, sks) =>
lastImpl(s, sks, Some("export"))
}
private[this] def lastImpl(s: State, sks: AnyKeys, sid: Option[String]): State =
{
val (str, ref, display) = extractLast(s)
Output.last(sks, str.streams(s), printLast(s), sid)(display)
keepLastLog(s)
}
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
def lastLogFile(s: State) =

View File

@ -16,8 +16,12 @@ object Output
{
final val DefaultTail = "> "
@deprecated("Explicitly provide None for the stream ID.", "0.13.0")
def last(keys: Values[_], streams: Streams, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
printLines( flatLines(lastLines(keys, streams))(idFun) )
last(keys, streams, printLines, None)(display)
def last(keys: Values[_], streams: Streams, printLines: Seq[String] => Unit, sid: Option[String])(implicit display: Show[ScopedKey[_]]): Unit =
printLines( flatLines(lastLines(keys, streams, sid))(idFun) )
def last(file: File, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
printLines(tailLines(file, tailDelim))
@ -42,13 +46,19 @@ object Output
}
}
def lastLines(keys: Values[_], streams: Streams): Values[Seq[String]] =
def lastLines(keys: Values[_], streams: Streams, sid: Option[String] = None): Values[Seq[String]] =
{
val outputs = keys map { (kv: KeyValue[_]) => KeyValue(kv.key, lastLines(kv.key, streams)) }
val outputs = keys map { (kv: KeyValue[_]) => KeyValue(kv.key, lastLines(kv.key, streams, sid)) }
outputs.filterNot(_.value.isEmpty)
}
def lastLines(key: ScopedKey[_], mgr: Streams): Seq[String] = mgr.use(key) { s => IO.readLines(s.readText( Project.fillTaskAxis(key) )) }
@deprecated("Explicitly provide None for the stream ID.", "0.13.0")
def lastLines(key: ScopedKey[_], mgr: Streams): Seq[String] = lastLines(key, mgr, None)
def lastLines(key: ScopedKey[_], mgr: Streams, sid: Option[String]): Seq[String] = mgr.use(key) { s => IO.readLines(s.readText( Project.fillTaskAxis(key), sid )) }
def tailLines(file: File, tailDelim: String): Seq[String] = headLines(IO.readLines(file).reverse, tailDelim).reverse
@tailrec def headLines(lines: Seq[String], tailDelim: String): Seq[String] =
if(lines.isEmpty)
lines