diff --git a/main/Build.scala b/main/Build.scala index 36fdc9eb1..8ee8b879d 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -10,6 +10,7 @@ package sbt import Compiler.Compilers import Project.{ScopedKey, Setting} import Keys.Streams + import Scope.GlobalScope import scala.annotation.tailrec // name is more like BuildDefinition, but that is too long @@ -127,20 +128,19 @@ object BuildStreams import Load.{BuildStructure, LoadedBuildUnit} import Project.display import std.{TaskExtra,Transform} + import Path._ + import BuildPaths.outputDirectory - val GlobalPath = "$global" - val BuildUnitPath = "$build" + final val GlobalPath = "$global" + final val BuildUnitPath = "$build" + final val StreamsDirectory = "streams" - def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope], logRelativePath: Seq[String] = defaultLogPath): Streams = - std.Streams( path(units, root, logRelativePath), display, LogManager.construct(data) ) + def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): Streams = + std.Streams( path(units, root, data), display, LogManager.construct(data) ) - def defaultLogPath = "target" :: "streams" :: Nil + def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File = + resolvePath( projectPath(units, root, scoped, data), nonProjectPath(scoped) ) - def path(units: Map[URI, LoadedBuildUnit], root: URI, sep: Seq[String])(scoped: ScopedKey[_]): File = - { - val (base, sub) = projectPath(units, root, scoped) - resolvePath(base, sep ++ sub ++ nonProjectPath(scoped) ) - } def resolvePath(base: File, components: Seq[String]): File = (base /: components)( (b,p) => new File(b,p) ) @@ -159,34 +159,39 @@ object BuildStreams pathComponent(scope.extra, scoped, "extra")(_ => error("Unimplemented")) :: Nil } - def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_]): (File, Seq[String]) = + def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_], data: Settings[Scope]): File = scoped.scope.project match { - case Global => (units(root).localBase, GlobalPath :: Nil) - case Select(BuildRef(uri)) => (units(uri).localBase, BuildUnitPath :: Nil) - case Select(ProjectRef(uri, id)) => (units(uri).defined(id).base, Nil) + case Global => refTarget(GlobalScope, units(root).localBase, data) / GlobalPath + case Select(br @ BuildRef(uri)) => refTarget(br, units(uri).localBase, data) / BuildUnitPath + case Select(pr @ ProjectRef(uri, id)) => refTarget(pr, units(uri).defined(id).base, data) case Select(pr) => error("Unresolved project reference (" + pr + ") in " + display(scoped)) case This => error("Unresolved project reference (This) in " + display(scoped)) } + + def refTarget(ref: ResolvedReference, fallbackBase: File, data: Settings[Scope]): File = + refTarget(GlobalScope.copy(project = Select(ref)), fallbackBase, data) + def refTarget(scope: Scope, fallbackBase: File, data: Settings[Scope]): File = + (Keys.target in scope get data getOrElse outputDirectory(fallbackBase).asFile ) / StreamsDirectory } object BuildPaths { import Path._ import GlobFilter._ - def defaultStaging = Path.userHome / ".sbt" / "staging" - def defaultGlobalPlugins = Path.userHome / ".sbt" / "plugins" + def defaultStaging = Path.userHome / ConfigDirectoryName / "staging" + def defaultGlobalPlugins = Path.userHome / ConfigDirectoryName / PluginsDirectoryName def definitionSources(base: File): Seq[File] = (base * "*.scala").getFiles def configurationSources(base: File): Seq[File] = (base * "*.sbt").getFiles - def pluginDirectory(definitionBase: Path) = definitionBase / "plugins" + def pluginDirectory(definitionBase: Path) = definitionBase / PluginsDirectoryName def evalOutputDirectory(base: Path) = outputDirectory(base) / "config-classes" - def outputDirectory(base: Path) = base / "target" + def outputDirectory(base: Path) = base / DefaultTargetName def buildOutputDirectory(base: Path, compilers: Compilers) = crossPath(outputDirectory(base), compilers.scalac.scalaInstance) def projectStandard(base: Path) = base / "project" - def projectHidden(base: Path) = base / ".sbt" + def projectHidden(base: Path) = base / ConfigDirectoryName def selectProjectDir(base: Path) = { val a = projectHidden(base) @@ -194,5 +199,9 @@ object BuildPaths if(a.exists) a else b } + final val PluginsDirectoryName = "plugins" + final val DefaultTargetName = "target" + final val ConfigDirectoryName = ".sbt" + def crossPath(base: File, instance: ScalaInstance): File = base / ("scala_" + instance.version) } \ No newline at end of file diff --git a/main/Keys.scala b/main/Keys.scala index 7cd541ad2..5e44da56b 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -23,6 +23,7 @@ object Keys val showSuccess = SettingKey[Boolean]("show-success") val showTiming = SettingKey[Boolean]("show-timing") val timingFormat = SettingKey[java.text.DateFormat]("timing-format") + val logManager = SettingKey[LogManager]("log-manager") // Project keys val projectCommand = AttributeKey[Boolean]("project-command") diff --git a/main/LogManager.scala b/main/LogManager.scala index 5a0960847..dbdfd87c7 100644 --- a/main/LogManager.scala +++ b/main/LogManager.scala @@ -7,11 +7,30 @@ package sbt import LogManager._ import std.Transform import Project.ScopedKey - import Keys.{logLevel, persistLogLevel, persistTraceLevel, traceLevel} + import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, traceLevel} object LogManager { def construct(data: Settings[Scope]) = (task: ScopedKey[_], to: PrintWriter) => + { + val manager = logManager in task.scope get data getOrElse default + manager(data, task, to) + } + lazy val default: LogManager = withLoggers() + + def defaultScreen: AbstractLogger = ConsoleLogger() + def defaultBacked(useColor: Boolean): PrintWriter => AbstractLogger = + to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false + + def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk) + + def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(false)): LogManager = + new LogManager { + def apply(data: Settings[Scope], task: ScopedKey[_], to: PrintWriter): Logger = + defaultLogger(data, task, screen, backed(to)) + } + + def defaultLogger(data: Settings[Scope], task: ScopedKey[_], console: AbstractLogger, backed: AbstractLogger): Logger = { val scope = task.scope def getOr[T](key: AttributeKey[T], default: T): T = data.get(scope, key) getOrElse default @@ -20,9 +39,6 @@ object LogManager val screenTrace = getOr(traceLevel.key, -1) val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue) - val console = ConsoleLogger() - val backed = ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = false) // TODO: wrap this with a filter that strips ANSI codes - val multi = new MultiLogger(console :: backed :: Nil) // sets multi to the most verbose for clients that inspect the current level multi setLevel Level.union(backingLevel, screenLevel) @@ -33,4 +49,8 @@ object LogManager backed setTrace backingTrace multi: Logger } +} +trait LogManager +{ + def apply(data: Settings[Scope], task: ScopedKey[_], writer: PrintWriter): Logger } \ No newline at end of file