From eecaeafbdf2e992e60fa0982d02b57ca90a65e2a Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 31 Jul 2012 11:52:10 -0400 Subject: [PATCH] reorganization of main/ * split several source files * move base settings sources (Scope, Structure, ...) into main/settings/ * breaks cycles. In particular, setting system moved from Project to Def --- main/Act.scala | 6 +- main/Aggregation.scala | 20 +- main/Build.scala | 298 +--------------------------- main/BuildLoader.scala | 1 - main/BuildPaths.scala | 73 +++++++ main/BuildStructure.scala | 119 +++++++++++ main/BuildUtil.scala | 42 +++- main/Cross.scala | 2 +- main/Defaults.scala | 20 +- main/EvaluateConfigurations.scala | 151 ++++++++++++++ main/EvaluateTask.scala | 21 +- main/Extracted.scala | 48 +++++ main/GlobalPlugin.scala | 4 +- main/IvyConsole.scala | 3 +- main/KeyIndex.scala | 3 +- main/Keys.scala | 65 +----- main/Load.scala | 164 ++++++--------- main/LogManager.scala | 2 +- main/Main.scala | 12 +- main/Opts.scala | 3 +- main/Output.scala | 2 +- main/PluginManagement.scala | 2 +- main/Project.scala | 231 ++++----------------- main/RetrieveUnit.scala | 37 ++++ main/ScopedKeyData.scala | 19 ++ main/SessionSettings.scala | 5 +- main/SessionVar.scala | 71 +++++++ main/SettingCompletions.scala | 6 +- main/SettingGraph.scala | 3 +- main/TaskData.scala | 52 ----- main/{ => settings}/Append.scala | 4 +- main/settings/ConfigKey.scala | 7 + main/settings/Def.scala | 37 ++++ main/settings/DelegateIndex.scala | 19 ++ main/settings/KeyRanks.scala | 45 +++++ main/{ => settings}/Reference.scala | 24 ++- main/{ => settings}/Scope.scala | 68 +------ main/settings/ScopeAxis.scala | 29 +++ main/settings/ScopeMask.scala | 16 ++ main/{ => settings}/Structure.scala | 85 ++++---- project/Sbt.scala | 7 +- util/collection/Attributes.scala | 1 + 42 files changed, 960 insertions(+), 867 deletions(-) create mode 100644 main/BuildPaths.scala create mode 100644 main/BuildStructure.scala create mode 100644 main/EvaluateConfigurations.scala create mode 100644 main/Extracted.scala create mode 100644 main/RetrieveUnit.scala create mode 100644 main/ScopedKeyData.scala create mode 100644 main/SessionVar.scala delete mode 100644 main/TaskData.scala rename main/{ => settings}/Append.scala (98%) create mode 100644 main/settings/ConfigKey.scala create mode 100644 main/settings/Def.scala create mode 100644 main/settings/DelegateIndex.scala create mode 100644 main/settings/KeyRanks.scala rename main/{ => settings}/Reference.scala (84%) rename main/{ => settings}/Scope.scala (80%) create mode 100644 main/settings/ScopeAxis.scala create mode 100644 main/settings/ScopeMask.scala rename main/{ => settings}/Structure.scala (93%) diff --git a/main/Act.scala b/main/Act.scala index e764d0b82..f54628937 100644 --- a/main/Act.scala +++ b/main/Act.scala @@ -3,9 +3,9 @@ */ package sbt - import Project.{ScopedKey, showContextKey} + import Def.{showRelativeKey, ScopedKey} + import Project.showContextKey import Keys.{sessionSettings, thisProject} - import Load.BuildStructure import complete.{DefaultParsers, Parser} import Aggregation.{KeyValue,Values} import DefaultParsers._ @@ -31,7 +31,7 @@ object Act def scopedKeySelected(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ParsedKey] = scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices => - select(choices, data)( Project.showRelativeKey(current, index.buildURIs.size > 1) ) + select(choices, data)( showRelativeKey(current, index.buildURIs.size > 1) ) } def scopedKeyFull(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = diff --git a/main/Aggregation.scala b/main/Aggregation.scala index ee6125898..28812fc63 100644 --- a/main/Aggregation.scala +++ b/main/Aggregation.scala @@ -3,8 +3,7 @@ */ package sbt - import Project.ScopedKey - import Load.{BuildStructure,LoadedBuildUnit} + import Def.ScopedKey import Keys.{aggregate, showSuccess, showTiming, timingFormat} import sbt.complete.Parser import java.net.URI @@ -30,7 +29,7 @@ final object Aggregation Command.applyEffect(seqParser(ps)) { ts => runTasks(s, structure, ts, Dummies(KNil, HNil), show) } - def runTasksWithResult[HL <: HList, T](s: State, structure: Load.BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]]): (State, Result[Seq[KeyValue[T]]]) = + def runTasksWithResult[HL <: HList, T](s: State, structure: BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]]): (State, Result[Seq[KeyValue[T]]]) = { import EvaluateTask._ import std.TaskExtra._ @@ -55,7 +54,7 @@ final object Aggregation (newS, result) } - def runTasks[HL <: HList, T](s: State, structure: Load.BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]]): State = { + def runTasks[HL <: HList, T](s: State, structure: BuildStructure, ts: Values[Task[T]], extra: Dummies[HL], show: Boolean)(implicit display: Show[ScopedKey[_]]): State = { runTasksWithResult(s, structure, ts, extra, show)._1 } @@ -157,16 +156,7 @@ final object Aggregation def aggregationEnabled(key: ScopedKey[_], data: Settings[Scope]): Boolean = Keys.aggregate in Scope.fillTaskAxis(key.scope, key.key) get data getOrElse true + @deprecated("Use BuildUtil.aggregationRelation", "0.13.0") def relation(units: Map[URI, LoadedBuildUnit]): Relation[ProjectRef, ProjectRef] = - { - val depPairs = - for { - (uri, unit) <- units.toIterable - project <- unit.defined.values - ref = ProjectRef(uri, project.id) - agg <- project.aggregate - } yield - (ref, agg) - Relation.empty ++ depPairs - } + BuildUtil.aggregationRelation(units) } \ No newline at end of file diff --git a/main/Build.scala b/main/Build.scala index 45fb37440..8104dee15 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -4,16 +4,8 @@ package sbt import java.io.File - import java.net.URI - import BuildLoader.ResolveInfo - import compiler.{Eval, EvalImports} - import complete.DefaultParsers.validID - import Compiler.Compilers - import Keys.{globalBaseDirectory, globalPluginsDirectory, globalSettingsDirectory, stagingDirectory, Streams} import Keys.{name, organization, thisProject} - import Project.{ScopedKey, Setting} - import Scope.GlobalScope - import scala.annotation.tailrec + import Def.{ScopedKey, Setting} // name is more like BuildDefinition, but that is too long trait Build @@ -26,16 +18,16 @@ trait Build trait Plugin { @deprecated("Override projectSettings or buildSettings instead.", "0.12.0") - def settings: Seq[Project.Setting[_]] = Nil + def settings: Seq[Setting[_]] = Nil /** Settings to be appended to all projects in a build. */ - def projectSettings: Seq[Project.Setting[_]] = Nil + def projectSettings: Seq[Setting[_]] = Nil /** Settings to be appended at the build scope. */ - def buildSettings: Seq[Project.Setting[_]] = Nil + def buildSettings: Seq[Setting[_]] = Nil /** Settings to be appended at the global scope. */ - def globalSettings: Seq[Project.Setting[_]] = Nil + def globalSettings: Seq[Setting[_]] = Nil } object Build @@ -47,283 +39,7 @@ object Build organization <<= (thisProject, organization, name) { (p, o, n) => if(p.id == n) "default" else o } ) - def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data) + @deprecated("Use Attributed.data", "0.13.0") + def data[T](in: Seq[Attributed[T]]): Seq[T] = Attributed.data(in) def analyzed(in: Seq[Attributed[_]]): Seq[inc.Analysis] = in.flatMap{ _.metadata.get(Keys.analysis) } } -object RetrieveUnit -{ - def apply(info: ResolveInfo): Option[() => File] = - { - info.uri match { - case Scheme("svn") | Scheme("svn+ssh") => Resolvers.subversion(info) - case Scheme("hg") => Resolvers.mercurial(info) - case Scheme("git") => Resolvers.git(info) - case Path(path) if path.endsWith(".git") => Resolvers.git(info) - case Scheme("http") | Scheme("https") | Scheme("ftp") => Resolvers.remote(info) - case Scheme("file") => Resolvers.local(info) - case _ => None - } - } - - object Scheme - { - def unapply(uri: URI) = Option(uri.getScheme) - } - - object Path - { - import RichURI.fromURI - - def unapply(uri: URI) = Option(uri.withoutMarkerScheme.getPath) - } -} - -object EvaluateConfigurations -{ - private[this] final class ParsedFile(val imports: Seq[(String,Int)], val definitions: Seq[(String,LineRange)], val settings: Seq[(String,LineRange)]) - private[this] final class Definitions(val loader: ClassLoader => ClassLoader, val moduleNames: Seq[String]) - - private[this] val DefinitionKeywords = Seq("lazy val ", "def ", "val ") - - def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): ClassLoader => Seq[Setting[_]] = - flatten(srcs.sortBy(_.getName) map { src => evaluateConfiguration(eval, src, imports) }) - def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): ClassLoader => Seq[Setting[_]] = - evaluateConfiguration(eval, src.getPath, IO.readLines(src), imports, 0) - - private[this] def parseConfiguration(lines: Seq[String], builtinImports: Seq[String], offset: Int): ParsedFile = - { - val (importStatements, settingsAndDefinitions) = splitExpressions(lines) - val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements) - val (definitions, settings) = splitSettingsDefinitions(addOffsetToRange(offset, settingsAndDefinitions)) - new ParsedFile(allImports, definitions, settings) - } - - def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): ClassLoader => Seq[Setting[_]] = - { - val parsed = parseConfiguration(lines, imports, offset) - val importDefs = if(parsed.definitions.isEmpty) Nil else { - val definitions = evaluateDefinitions(eval, name, parsed.imports, parsed.definitions) - Load.importAllRoot(definitions.moduleNames).map(s => (s, -1)) - } - val allImports = importDefs ++ parsed.imports - val settings = parsed.settings map { case (settingExpression,range) => - evaluateSetting(eval, name, allImports, settingExpression, range) - } - eval.unlinkDeferred() - flatten(settings) - } - def flatten(mksettings: Seq[ClassLoader => Seq[Setting[_]]]): ClassLoader => Seq[Setting[_]] = - loader => mksettings.flatMap(_ apply loader) - def addOffset(offset: Int, lines: Seq[(String,Int)]): Seq[(String,Int)] = - lines.map { case (s, i) => (s, i + offset) } - def addOffsetToRange(offset: Int, ranges: Seq[(String,LineRange)]): Seq[(String,LineRange)] = - ranges.map { case (s, r) => (s, r shift offset) } - - def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, range: LineRange): ClassLoader => Seq[Setting[_]] = - { - val result = try { - eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.SettingsDefinition"), line = range.start) - } catch { - case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage) - } - loader => { - val pos = RangePosition(name, range shift 1) - result.getValue(loader).asInstanceOf[Project.SettingsDefinition].settings map (_ withPos pos) - } - } - private[this] def isSpace = (c: Char) => Character isWhitespace c - private[this] def fstS(f: String => Boolean): ((String,Int)) => Boolean = { case (s,i) => f(s) } - private[this] def firstNonSpaceIs(lit: String) = (_: String).view.dropWhile(isSpace).startsWith(lit) - private[this] def or[A](a: A => Boolean, b: A => Boolean): A => Boolean = in => a(in) || b(in) - def splitExpressions(lines: Seq[String]): (Seq[(String,Int)], Seq[(String,LineRange)]) = - { - val blank = (_: String).forall(isSpace) - val isImport = firstNonSpaceIs("import ") - val comment = firstNonSpaceIs("//") - val blankOrComment = or(blank, comment) - val importOrBlank = fstS(or(blankOrComment, isImport)) - - val (imports, settings) = lines.zipWithIndex span importOrBlank - (imports filterNot fstS( blankOrComment ), groupedLines(settings, blank, blankOrComment)) - } - def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean, skipInitial: String => Boolean): Seq[(String,LineRange)] = - { - val fdelim = fstS(delimiter) - @tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,LineRange)]): Seq[(String,LineRange)] = - if(lines.isEmpty) accum.reverse - else - { - val start = lines dropWhile fstS( skipInitial ) - val (next, tail) = start.span { case (s,_) => !delimiter(s) } - val grouped = if(next.isEmpty) accum else (next.map(_._1).mkString("\n"), LineRange(next.head._2, next.last._2 + 1)) +: accum - group0(tail, grouped) - } - group0(lines, Nil) - } - private[this] def splitSettingsDefinitions(lines: Seq[(String,LineRange)]): (Seq[(String,LineRange)], Seq[(String,LineRange)]) = - lines partition { case (line, range) => isDefinition(line) } - private[this] def isDefinition(line: String): Boolean = - { - val trimmed = line.trim - DefinitionKeywords.exists(trimmed startsWith _) - } - private[this] def evaluateDefinitions(eval: Eval, name: String, imports: Seq[(String,Int)], definitions: Seq[(String,LineRange)]): Definitions = - { - val convertedRanges = definitions.map { case (s, r) => (s, r.start to r.end) } - val res = eval.evalDefinitions(convertedRanges, new EvalImports(imports, name), name) - new Definitions(loader => res.getValue(loader).getClass.getClassLoader, res.enclosingModule :: Nil) - } -} -object Index -{ - def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] = - { - // AttributeEntry + the checked type test 'value: Task[_]' ensures that the cast is correct. - // (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match still required the cast) - val pairs = for( scope <- data.scopes; AttributeEntry(key, value: Task[_]) <- data.data(scope).entries ) yield - (value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) // unclear why this cast is needed even with a type test in the above filter - pairs.toMap[Task[_], ScopedKey[Task[_]]] - } - def allKeys(settings: Seq[Setting[_]]): Set[ScopedKey[_]] = - settings.flatMap(s => if(s.key.key.isLocal) Nil else s.key +: s.dependencies).filter(!_.key.isLocal).toSet - def attributeKeys(settings: Settings[Scope]): Set[AttributeKey[_]] = - settings.data.values.flatMap(_.keys).toSet[AttributeKey[_]] - def stringToKeyMap(settings: Set[AttributeKey[_]]): Map[String, AttributeKey[_]] = - { - val multiMap = settings.groupBy(_.label) - val duplicates = multiMap collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) } collect { case (k, xs) if xs.size > 1 => (k, xs) } - if(duplicates.isEmpty) - multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap; - else - error(duplicates map { case (k, tps) => "'" + k + "' (" + tps.mkString(", ") + ")" } mkString("AttributeKey ID collisions detected for: ", ", ", "")) - } - private[this] type TriggerMap = collection.mutable.HashMap[Task[_], Seq[Task[_]]] - def triggers(ss: Settings[Scope]): Triggers[Task] = - { - val runBefore = new TriggerMap - val triggeredBy = new TriggerMap - for( (_, amap) <- ss.data; AttributeEntry(_, value: Task[_]) <- amap.entries) - { - val as = value.info.attributes - update(runBefore, value, as get Keys.runBefore) - update(triggeredBy, value, as get Keys.triggeredBy) - } - val onComplete = Keys.onComplete in GlobalScope get ss getOrElse { () => () } - new Triggers[Task](runBefore, triggeredBy, map => { onComplete(); map } ) - } - private[this] def update(map: TriggerMap, base: Task[_], tasksOpt: Option[Seq[Task[_]]]): Unit = - for( tasks <- tasksOpt; task <- tasks ) - map(task) = base +: map.getOrElse(task, Nil) -} -object BuildStreams -{ - import Load.{BuildStructure, LoadedBuildUnit} - import Project.displayFull - import std.{TaskExtra,Transform} - import Path._ - import BuildPaths.outputDirectory - - final val GlobalPath = "$global" - final val BuildUnitPath = "$build" - final val StreamsDirectory = "streams" - - def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): State => Streams = s => - std.Streams( path(units, root, data), displayFull, LogManager.construct(data, s) ) - - def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File = - resolvePath( projectPath(units, root, scoped, data), nonProjectPath(scoped) ) - - def resolvePath(base: File, components: Seq[String]): File = - (base /: components)( (b,p) => new File(b,p) ) - - def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(show: T => String): String = - axis match - { - case Global => GlobalPath - case This => error("Unresolved This reference for " + label + " in " + Project.displayFull(scoped)) - case Select(t) => show(t) - } - def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] = - { - val scope = scoped.scope - pathComponent(scope.config, scoped, "config")(_.name) :: - pathComponent(scope.task, scoped, "task")(_.label) :: - pathComponent(scope.extra, scoped, "extra")(showAMap) :: - Nil - } - def showAMap(a: AttributeMap): String = - a.entries.toSeq.sortBy(_.key.label).map { case AttributeEntry(key, value) => key.label + "=" + value.toString } mkString(" ") - def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_], data: Settings[Scope]): File = - scoped.scope.project match - { - 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 " + displayFull(scoped)) - case This => error("Unresolved project reference (This) in " + displayFull(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._ - - def getGlobalBase(state: State): File = - getFileSetting(globalBaseDirectory, GlobalBaseProperty, defaultGlobalBase)(state) - - def getStagingDirectory(state: State, globalBase: File): File = - getFileSetting(stagingDirectory, StagingProperty, defaultStaging(globalBase))(state) - - def getGlobalPluginsDirectory(state: State, globalBase: File): File = - getFileSetting(globalPluginsDirectory, GlobalPluginsProperty, defaultGlobalPlugins(globalBase))(state) - - def getGlobalSettingsDirectory(state: State, globalBase: File): File = - getFileSetting(globalSettingsDirectory, GlobalSettingsProperty, globalBase)(state) - - def getFileSetting(stateKey: AttributeKey[File], property: String, default: File)(state: State): File = - state get stateKey orElse getFileProperty(property) getOrElse default - - def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap { path => - if(path.isEmpty) None else Some(new File(path)) - } - - def defaultGlobalBase = Path.userHome / ConfigDirectoryName - private[this] def defaultStaging(globalBase: File) = globalBase / "staging" - private[this] def defaultGlobalPlugins(globalBase: File) = globalBase / PluginsDirectoryName - - def definitionSources(base: File): Seq[File] = (base * "*.scala").get - def configurationSources(base: File): Seq[File] = (base * (GlobFilter("*.sbt") - ".sbt")).get - def pluginDirectory(definitionBase: File) = definitionBase / PluginsDirectoryName - - def evalOutputDirectory(base: File) = outputDirectory(base) / "config-classes" - def outputDirectory(base: File) = base / DefaultTargetName - def buildOutputDirectory(base: File, compilers: Compilers) = crossPath(outputDirectory(base), compilers.scalac.scalaInstance) - - def projectStandard(base: File) = base / "project" - def projectHidden(base: File) = base / ConfigDirectoryName - def selectProjectDir(base: File, log: Logger) = - { - val a = projectHidden(base) - val b = projectStandard(base) - if(a.exists) - { - log.warn("Alternative project directory " + ConfigDirectoryName + " (" + a + ") has been deprecated since sbt 0.12.0.\n Please use the standard location: " + b) - a - } - else b - } - - final val PluginsDirectoryName = "plugins" - final val DefaultTargetName = "target" - final val ConfigDirectoryName = ".sbt" - final val GlobalBaseProperty = "sbt.global.base" - final val StagingProperty = "sbt.global.staging" - final val GlobalPluginsProperty = "sbt.global.plugins" - final val GlobalSettingsProperty = "sbt.global.settings" - - def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File = base / ("scala_" + instance.version) -} diff --git a/main/BuildLoader.scala b/main/BuildLoader.scala index 11e6d968e..3fa1bd3d2 100644 --- a/main/BuildLoader.scala +++ b/main/BuildLoader.scala @@ -5,7 +5,6 @@ package sbt import java.io.File import java.net.URI - import Load.{BuildUnit, LoadBuildConfiguration, PartBuild} import BuildLoader._ import Alternatives._ import Types.{const,idFun} diff --git a/main/BuildPaths.scala b/main/BuildPaths.scala new file mode 100644 index 000000000..c160d5ea0 --- /dev/null +++ b/main/BuildPaths.scala @@ -0,0 +1,73 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + + import java.io.File + import java.net.URI + import KeyRanks.DSetting + +object BuildPaths +{ + val globalBaseDirectory = AttributeKey[File]("global-base-directory", "The base directory for global sbt configuration and staging.", DSetting) + val globalPluginsDirectory = AttributeKey[File]("global-plugins-directory", "The base directory for global sbt plugins.", DSetting) + val globalSettingsDirectory = AttributeKey[File]("global-settings-directory", "The base directory for global sbt settings.", DSetting) + val stagingDirectory = AttributeKey[File]("staging-directory", "The directory for staging remote projects.", DSetting) + + import Path._ + + def getGlobalBase(state: State): File = + getFileSetting(globalBaseDirectory, GlobalBaseProperty, defaultGlobalBase)(state) + + def getStagingDirectory(state: State, globalBase: File): File = + getFileSetting(stagingDirectory, StagingProperty, defaultStaging(globalBase))(state) + + def getGlobalPluginsDirectory(state: State, globalBase: File): File = + getFileSetting(globalPluginsDirectory, GlobalPluginsProperty, defaultGlobalPlugins(globalBase))(state) + + def getGlobalSettingsDirectory(state: State, globalBase: File): File = + getFileSetting(globalSettingsDirectory, GlobalSettingsProperty, globalBase)(state) + + def getFileSetting(stateKey: AttributeKey[File], property: String, default: File)(state: State): File = + state get stateKey orElse getFileProperty(property) getOrElse default + + def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap { path => + if(path.isEmpty) None else Some(new File(path)) + } + + def defaultGlobalBase = Path.userHome / ConfigDirectoryName + private[this] def defaultStaging(globalBase: File) = globalBase / "staging" + private[this] def defaultGlobalPlugins(globalBase: File) = globalBase / PluginsDirectoryName + + def definitionSources(base: File): Seq[File] = (base * "*.scala").get + def configurationSources(base: File): Seq[File] = (base * (GlobFilter("*.sbt") - ".sbt")).get + def pluginDirectory(definitionBase: File) = definitionBase / PluginsDirectoryName + + def evalOutputDirectory(base: File) = outputDirectory(base) / "config-classes" + def outputDirectory(base: File) = base / DefaultTargetName + def buildOutputDirectory(base: File, scalaInstance: xsbti.compile.ScalaInstance) = crossPath(outputDirectory(base), scalaInstance) + + def projectStandard(base: File) = base / "project" + def projectHidden(base: File) = base / ConfigDirectoryName + def selectProjectDir(base: File, log: Logger) = + { + val a = projectHidden(base) + val b = projectStandard(base) + if(a.exists) + { + log.warn("Alternative project directory " + ConfigDirectoryName + " (" + a + ") has been deprecated since sbt 0.12.0.\n Please use the standard location: " + b) + a + } + else b + } + + final val PluginsDirectoryName = "plugins" + final val DefaultTargetName = "target" + final val ConfigDirectoryName = ".sbt" + final val GlobalBaseProperty = "sbt.global.base" + final val StagingProperty = "sbt.global.staging" + final val GlobalPluginsProperty = "sbt.global.plugins" + final val GlobalSettingsProperty = "sbt.global.settings" + + def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File = base / ("scala_" + instance.version) +} diff --git a/main/BuildStructure.scala b/main/BuildStructure.scala new file mode 100644 index 000000000..eacbdc59b --- /dev/null +++ b/main/BuildStructure.scala @@ -0,0 +1,119 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + + import java.io.File + import java.net.URI + import compiler.Eval + import inc.Locate + import Def.{displayFull, ScopedKey, ScopeLocal, Setting} + import Attributed.data + import BuildPaths.outputDirectory + import Scope.GlobalScope + import BuildStreams.Streams + import Path._ + +final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: State => Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal) +{ + val rootProject: URI => String = Load getRootProject units + def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq + def allProjects(build: URI): Seq[ResolvedProject] = units.get(build).toList.flatMap(_.defined.values) + def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap { case (build, unit) => refs(build, unit.defined.values.toSeq) } + def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build)) + val extra: BuildUtil[ResolvedProject] = BuildUtil(root, units, index.keyIndex, data) + private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) } +} +// information that is not original, but can be reconstructed from the rest of BuildStructure +final class StructureIndex( + val keyMap: Map[String, AttributeKey[_]], + val taskToKey: Map[Task[_], ScopedKey[Task[_]]], + val triggers: Triggers[Task], + val keyIndex: KeyIndex, + val aggregateKeyIndex: KeyIndex +) +final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase +{ + assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit) + val root = rootProjects.head + def localBase = unit.localBase + def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath + def loader = unit.definitions.loader + def imports = BuildUtil.getImports(unit) + override def toString = unit.toString +} + +final class LoadedDefinitions(val base: File, val target: Seq[File], val loader: ClassLoader, val builds: Seq[Build], val buildNames: Seq[String]) +final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val plugins: Seq[Plugin], val pluginNames: Seq[String]) +{ + def fullClasspath: Seq[Attributed[File]] = pluginData.classpath + def classpath = data(fullClasspath) +} +final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins) +{ + override def toString = if(uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase +")") +} + +final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit]) +{ + BuildUtil.checkCycles(units) + def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj + def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = BuildUtil(root, units, keyIndex, data) +} +final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit]) +sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] } +final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase +{ + def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings) + def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f) +} + +object BuildStreams +{ + type Streams = std.Streams[ScopedKey[_]] + + final val GlobalPath = "$global" + final val BuildUnitPath = "$build" + final val StreamsDirectory = "streams" + + def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): State => Streams = s => + std.Streams( path(units, root, data), displayFull, LogManager.construct(data, s) ) + + def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File = + resolvePath( projectPath(units, root, scoped, data), nonProjectPath(scoped) ) + + def resolvePath(base: File, components: Seq[String]): File = + (base /: components)( (b,p) => new File(b,p) ) + + def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(show: T => String): String = + axis match + { + case Global => GlobalPath + case This => error("Unresolved This reference for " + label + " in " + displayFull(scoped)) + case Select(t) => show(t) + } + def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] = + { + val scope = scoped.scope + pathComponent(scope.config, scoped, "config")(_.name) :: + pathComponent(scope.task, scoped, "task")(_.label) :: + pathComponent(scope.extra, scoped, "extra")(showAMap) :: + Nil + } + def showAMap(a: AttributeMap): String = + a.entries.toSeq.sortBy(_.key.label).map { case AttributeEntry(key, value) => key.label + "=" + value.toString } mkString(" ") + def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_], data: Settings[Scope]): File = + scoped.scope.project match + { + 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 " + displayFull(scoped)) + case This => error("Unresolved project reference (This) in " + displayFull(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 +} \ No newline at end of file diff --git a/main/BuildUtil.scala b/main/BuildUtil.scala index fec4d346a..09463e1c3 100644 --- a/main/BuildUtil.scala +++ b/main/BuildUtil.scala @@ -1,6 +1,6 @@ package sbt -import java.net.URI + import java.net.URI final class BuildUtil[Proj]( val keyIndex: KeyIndex, @@ -37,4 +37,44 @@ final class BuildUtil[Proj]( val configurationsForAxis: Option[ResolvedReference] => Seq[String] = refOpt => configurations(projectForAxis(refOpt)).map(_.name) +} +object BuildUtil +{ + def apply(root: URI, units: Map[URI, LoadedBuildUnit], keyIndex: KeyIndex, data: Settings[Scope]): BuildUtil[ResolvedProject] = + { + val getp = (build: URI, project: String) => Load.getProject(units, build, project) + val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name)) + val aggregates = aggregationRelation(units) + new BuildUtil(keyIndex, data, root, Load getRootProject units, getp, configs, aggregates) + } + + def checkCycles(units: Map[URI, LoadedBuildUnit]) + { + def getRef(pref: ProjectRef) = units(pref.build).defined(pref.project) + def deps(proj: ResolvedProject)(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = Dag.topologicalSort(proj)(p => base(p) map getRef) + // check for cycles + for( (_, lbu) <- units; proj <- lbu.defined.values) { + deps(proj)(_.dependencies.map(_.project)) + deps(proj)(_.delegates) + deps(proj)(_.aggregate) + } + } + def baseImports = "import sbt._, Process._, Keys._" :: Nil + def getImports(unit: BuildUnit) = baseImports ++ importAllRoot(unit.plugins.pluginNames ++ unit.definitions.buildNames) + def importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil + def importAllRoot(values: Seq[String]) = importAll(values map rootedName) + def rootedName(s: String) = if(s contains '.') "_root_." + s else s + + def aggregationRelation(units: Map[URI, LoadedBuildUnit]): Relation[ProjectRef, ProjectRef] = + { + val depPairs = + for { + (uri, unit) <- units.toIterable + project <- unit.defined.values + ref = ProjectRef(uri, project.id) + agg <- project.aggregate + } yield + (ref, agg) + Relation.empty ++ depPairs + } } \ No newline at end of file diff --git a/main/Cross.scala b/main/Cross.scala index d170c13cf..d58ab6598 100644 --- a/main/Cross.scala +++ b/main/Cross.scala @@ -6,7 +6,7 @@ package sbt import Keys._ import complete.{DefaultParsers, Parser} import DefaultParsers._ - import Project.{ScopedKey, Setting} + import Def.{ScopedKey, Setting} import Scope.GlobalScope object Cross diff --git a/main/Defaults.scala b/main/Defaults.scala index aef89458f..2da216e85 100755 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -7,8 +7,8 @@ package sbt import Scope.{fillTaskAxis, GlobalScope, ThisScope} import xsbt.api.Discovery import xsbti.compile.CompileOrder - import Project.{inConfig, Initialize, inScope, inTask, ScopedKey, Setting, SettingsDefinition} - import Load.LoadedBuild + import Project.{inConfig, inScope, inTask, richInitialize, richInitializeTask, richTaskSessionVar} + import Def.{Initialize, ScopedKey, Setting, SettingsDefinition} import Artifact.{DocClassifier, SourceClassifier} import Configurations.{Compile, CompilerPlugin, IntegrationTest, names, Provided, Runtime, Test} import CrossVersion.{binarySbtVersion, binaryScalaVersion} @@ -263,7 +263,7 @@ object Defaults extends BuildCommon override def watchPaths(s: State) = EvaluateTask.evaluateTask(Project structure s, key, s, base) match { case Some(Value(ps)) => ps case Some(Inc(i)) => throw i - case None => error("key not found: " + Project.displayFull(key)) + case None => error("key not found: " + Def.displayFull(key)) } } } @@ -657,7 +657,7 @@ object Defaults extends BuildCommon forDependencies[T,T](ref => (key in ref) ?? default(ref), includeRoot, classpath, aggregate) def forDependencies[T,V](init: ProjectRef => Initialize[V], includeRoot: Boolean = true, classpath: Boolean = true, aggregate: Boolean = false): Initialize[Seq[V]] = - Project.bind( (loadedBuild, thisProjectRef).identity ) { case (lb, base) => + Def.bind( (loadedBuild, thisProjectRef).identity ) { case (lb, base) => transitiveDependencies(base, lb, includeRoot, classpath, aggregate) map init join ; } @@ -773,7 +773,7 @@ object Classpaths ) val baseSettings: Seq[Setting[_]] = sbtClassifiersTasks ++ Seq( conflictWarning in GlobalScope :== ConflictWarning.default("global"), - conflictWarning <<= (thisProjectRef, conflictWarning) { (ref, cw) => cw.copy(label = Project.display(ref)) }, + conflictWarning <<= (thisProjectRef, conflictWarning) { (ref, cw) => cw.copy(label = Reference.display(ref)) }, unmanagedBase <<= baseDirectory / "lib", normalizedName <<= name(StringUtilities.normalize), isSnapshot <<= isSnapshot or version(_ endsWith "-SNAPSHOT"), @@ -859,7 +859,7 @@ object Classpaths (module, ref, config, cacheDirectory, si, reports, roots, resolved, skip, s) => val depsUpdated = reports.exists(!_.stats.cached) val isRoot = roots contains resolved - cachedUpdate(cacheDirectory / "update", Project.display(ref), module, config, Some(si), skip = skip, force = isRoot, depsUpdated = depsUpdated, log = s.log) + cachedUpdate(cacheDirectory / "update", Reference.display(ref), module, config, Some(si), skip = skip, force = isRoot, depsUpdated = depsUpdated, log = s.log) } tag(Tags.Update, Tags.Network), update <<= (conflictWarning, update, streams) map { (config, report, s) => ConflictWarning(config, report, s.log); report }, transitiveClassifiers in GlobalScope :== Seq(SourceClassifier, DocClassifier), @@ -1277,12 +1277,12 @@ trait BuildExtra extends BuildCommon seq( artLocal <<= artifact, taskLocal <<= taskDef, art, pkgd ) } - def seq(settings: Setting[_]*): SettingsDefinition = new Project.SettingList(settings) + def seq(settings: Setting[_]*): SettingsDefinition = new Def.SettingList(settings) def externalIvySettings(file: Initialize[File] = baseDirectory / "ivysettings.xml", addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = externalIvySettingsURI(file(_.toURI), addMultiResolver) def externalIvySettingsURL(url: URL, addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = - externalIvySettingsURI(Project.value(url.toURI), addMultiResolver) + externalIvySettingsURI(Def.value(url.toURI), addMultiResolver) def externalIvySettingsURI(uri: Initialize[URI], addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = { val other = (baseDirectory, appConfiguration, projectResolver, streams).identityMap @@ -1375,12 +1375,12 @@ trait BuildCommon // intended for use in constructing InputTasks def loadForParser[P,T](task: TaskKey[T])(f: (State, Option[T]) => Parser[P])(implicit format: sbinary.Format[T]): Initialize[State => Parser[P]] = - loadForParserI(task)(Project value f)(format) + loadForParserI(task)(Def value f)(format) def loadForParserI[P,T](task: TaskKey[T])(init: Initialize[(State, Option[T]) => Parser[P]])(implicit format: sbinary.Format[T]): Initialize[State => Parser[P]] = (resolvedScoped, init)( (ctx, f) => (s: State) => f( s, loadFromContext(task, ctx, s)(format)) ) def getForParser[P,T](task: TaskKey[T])(init: (State, Option[T]) => Parser[P]): Initialize[State => Parser[P]] = - getForParserI(task)(Project value init) + getForParserI(task)(Def value init) def getForParserI[P,T](task: TaskKey[T])(init: Initialize[(State, Option[T]) => Parser[P]]): Initialize[State => Parser[P]] = (resolvedScoped, init)( (ctx, f) => (s: State) => f(s, getFromContext(task, ctx, s)) ) diff --git a/main/EvaluateConfigurations.scala b/main/EvaluateConfigurations.scala new file mode 100644 index 000000000..8160f0a7c --- /dev/null +++ b/main/EvaluateConfigurations.scala @@ -0,0 +1,151 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + + import java.io.File + import java.net.URI + import compiler.{Eval, EvalImports} + import complete.DefaultParsers.validID + import Def.{ScopedKey, Setting, SettingsDefinition} + import Scope.GlobalScope + import scala.annotation.tailrec + +object EvaluateConfigurations +{ + private[this] final class ParsedFile(val imports: Seq[(String,Int)], val definitions: Seq[(String,LineRange)], val settings: Seq[(String,LineRange)]) + private[this] final class Definitions(val loader: ClassLoader => ClassLoader, val moduleNames: Seq[String]) + + private[this] val DefinitionKeywords = Seq("lazy val ", "def ", "val ") + + def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): ClassLoader => Seq[Setting[_]] = + flatten(srcs.sortBy(_.getName) map { src => evaluateConfiguration(eval, src, imports) }) + def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): ClassLoader => Seq[Setting[_]] = + evaluateConfiguration(eval, src.getPath, IO.readLines(src), imports, 0) + + private[this] def parseConfiguration(lines: Seq[String], builtinImports: Seq[String], offset: Int): ParsedFile = + { + val (importStatements, settingsAndDefinitions) = splitExpressions(lines) + val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements) + val (definitions, settings) = splitSettingsDefinitions(addOffsetToRange(offset, settingsAndDefinitions)) + new ParsedFile(allImports, definitions, settings) + } + + def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): ClassLoader => Seq[Setting[_]] = + { + val parsed = parseConfiguration(lines, imports, offset) + val importDefs = if(parsed.definitions.isEmpty) Nil else { + val definitions = evaluateDefinitions(eval, name, parsed.imports, parsed.definitions) + Load.importAllRoot(definitions.moduleNames).map(s => (s, -1)) + } + val allImports = importDefs ++ parsed.imports + val settings = parsed.settings map { case (settingExpression,range) => + evaluateSetting(eval, name, allImports, settingExpression, range) + } + eval.unlinkDeferred() + flatten(settings) + } + def flatten(mksettings: Seq[ClassLoader => Seq[Setting[_]]]): ClassLoader => Seq[Setting[_]] = + loader => mksettings.flatMap(_ apply loader) + def addOffset(offset: Int, lines: Seq[(String,Int)]): Seq[(String,Int)] = + lines.map { case (s, i) => (s, i + offset) } + def addOffsetToRange(offset: Int, ranges: Seq[(String,LineRange)]): Seq[(String,LineRange)] = + ranges.map { case (s, r) => (s, r shift offset) } + + val SettingsDefinitionName = classOf[SettingsDefinition].getName + def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, range: LineRange): ClassLoader => Seq[Setting[_]] = + { + val result = try { + eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some(SettingsDefinitionName), line = range.start) + } catch { + case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage) + } + loader => { + val pos = RangePosition(name, range shift 1) + result.getValue(loader).asInstanceOf[SettingsDefinition].settings map (_ withPos pos) + } + } + private[this] def isSpace = (c: Char) => Character isWhitespace c + private[this] def fstS(f: String => Boolean): ((String,Int)) => Boolean = { case (s,i) => f(s) } + private[this] def firstNonSpaceIs(lit: String) = (_: String).view.dropWhile(isSpace).startsWith(lit) + private[this] def or[A](a: A => Boolean, b: A => Boolean): A => Boolean = in => a(in) || b(in) + def splitExpressions(lines: Seq[String]): (Seq[(String,Int)], Seq[(String,LineRange)]) = + { + val blank = (_: String).forall(isSpace) + val isImport = firstNonSpaceIs("import ") + val comment = firstNonSpaceIs("//") + val blankOrComment = or(blank, comment) + val importOrBlank = fstS(or(blankOrComment, isImport)) + + val (imports, settings) = lines.zipWithIndex span importOrBlank + (imports filterNot fstS( blankOrComment ), groupedLines(settings, blank, blankOrComment)) + } + def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean, skipInitial: String => Boolean): Seq[(String,LineRange)] = + { + val fdelim = fstS(delimiter) + @tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,LineRange)]): Seq[(String,LineRange)] = + if(lines.isEmpty) accum.reverse + else + { + val start = lines dropWhile fstS( skipInitial ) + val (next, tail) = start.span { case (s,_) => !delimiter(s) } + val grouped = if(next.isEmpty) accum else (next.map(_._1).mkString("\n"), LineRange(next.head._2, next.last._2 + 1)) +: accum + group0(tail, grouped) + } + group0(lines, Nil) + } + private[this] def splitSettingsDefinitions(lines: Seq[(String,LineRange)]): (Seq[(String,LineRange)], Seq[(String,LineRange)]) = + lines partition { case (line, range) => isDefinition(line) } + private[this] def isDefinition(line: String): Boolean = + { + val trimmed = line.trim + DefinitionKeywords.exists(trimmed startsWith _) + } + private[this] def evaluateDefinitions(eval: Eval, name: String, imports: Seq[(String,Int)], definitions: Seq[(String,LineRange)]): Definitions = + { + val convertedRanges = definitions.map { case (s, r) => (s, r.start to r.end) } + val res = eval.evalDefinitions(convertedRanges, new EvalImports(imports, name), name) + new Definitions(loader => res.getValue(loader).getClass.getClassLoader, res.enclosingModule :: Nil) + } +} +object Index +{ + def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] = + { + // AttributeEntry + the checked type test 'value: Task[_]' ensures that the cast is correct. + // (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match still required the cast) + val pairs = for( scope <- data.scopes; AttributeEntry(key, value: Task[_]) <- data.data(scope).entries ) yield + (value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) // unclear why this cast is needed even with a type test in the above filter + pairs.toMap[Task[_], ScopedKey[Task[_]]] + } + def allKeys(settings: Seq[Setting[_]]): Set[ScopedKey[_]] = + settings.flatMap(s => if(s.key.key.isLocal) Nil else s.key +: s.dependencies).filter(!_.key.isLocal).toSet + def attributeKeys(settings: Settings[Scope]): Set[AttributeKey[_]] = + settings.data.values.flatMap(_.keys).toSet[AttributeKey[_]] + def stringToKeyMap(settings: Set[AttributeKey[_]]): Map[String, AttributeKey[_]] = + { + val multiMap = settings.groupBy(_.label) + val duplicates = multiMap collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) } collect { case (k, xs) if xs.size > 1 => (k, xs) } + if(duplicates.isEmpty) + multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap; + else + error(duplicates map { case (k, tps) => "'" + k + "' (" + tps.mkString(", ") + ")" } mkString("AttributeKey ID collisions detected for: ", ", ", "")) + } + private[this] type TriggerMap = collection.mutable.HashMap[Task[_], Seq[Task[_]]] + def triggers(ss: Settings[Scope]): Triggers[Task] = + { + val runBefore = new TriggerMap + val triggeredBy = new TriggerMap + for( (_, amap) <- ss.data; AttributeEntry(_, value: Task[_]) <- amap.entries) + { + val as = value.info.attributes + update(runBefore, value, as get Keys.runBefore) + update(triggeredBy, value, as get Keys.triggeredBy) + } + val onComplete = Keys.onComplete in GlobalScope get ss getOrElse { () => () } + new Triggers[Task](runBefore, triggeredBy, map => { onComplete(); map } ) + } + private[this] def update(map: TriggerMap, base: Task[_], tasksOpt: Option[Seq[Task[_]]]): Unit = + for( tasks <- tasksOpt; task <- tasks ) + map(task) = base +: map.getOrElse(task, Nil) +} \ No newline at end of file diff --git a/main/EvaluateTask.scala b/main/EvaluateTask.scala index 45d4a6485..c11983de5 100644 --- a/main/EvaluateTask.scala +++ b/main/EvaluateTask.scala @@ -4,9 +4,10 @@ package sbt import java.io.File - import Project.{ScopedKey, Setting} + import Def.{displayFull, ScopedKey, Setting} import Keys.{streams, Streams, TaskStreams} import Keys.{dummyRoots, dummyState, dummyStreamsManager, executionRoots, pluginData, streamsManager, taskDefinitionKey, transformState} + import Project.richInitializeTask import Scope.{GlobalScope, ThisScope} import Types.const import scala.Console.RED @@ -25,8 +26,6 @@ object PluginData object EvaluateTask { - import Load.BuildStructure - import Project.display import std.{TaskExtra,Transform} import TaskExtra._ import Keys.state @@ -34,7 +33,7 @@ object EvaluateTask val SystemProcessors = Runtime.getRuntime.availableProcessors def defaultConfig(state: State): EvaluateConfig = EvaluateConfig(false, restrictions(state)) - def defaultConfig(extracted: Extracted, structure: Load.BuildStructure) = + def defaultConfig(extracted: Extracted, structure: BuildStructure) = EvaluateConfig(false, restrictions(extracted, structure)) def extractedConfig(extracted: Extracted, structure: BuildStructure): EvaluateConfig = @@ -45,7 +44,7 @@ object EvaluateTask } def defaultRestrictions(maxWorkers: Int) = Tags.limitAll(maxWorkers) :: Nil - def defaultRestrictions(extracted: Extracted, structure: Load.BuildStructure): Seq[Tags.Rule] = + def defaultRestrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] = Tags.limitAll(maxWorkers(extracted, structure)) :: Nil def restrictions(state: State): Seq[Tags.Rule] = @@ -53,16 +52,16 @@ object EvaluateTask val extracted = Project.extract(state) restrictions(extracted, extracted.structure) } - def restrictions(extracted: Extracted, structure: Load.BuildStructure): Seq[Tags.Rule] = + def restrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] = getSetting(Keys.concurrentRestrictions, defaultRestrictions(extracted, structure), extracted, structure) - def maxWorkers(extracted: Extracted, structure: Load.BuildStructure): Int = + def maxWorkers(extracted: Extracted, structure: BuildStructure): Int = if(getSetting(Keys.parallelExecution, true, extracted, structure)) SystemProcessors else 1 - def cancelable(extracted: Extracted, structure: Load.BuildStructure): Boolean = + def cancelable(extracted: Extracted, structure: BuildStructure): Boolean = getSetting(Keys.cancelable, false, extracted, structure) - def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: Load.BuildStructure): T = + def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T = key in extracted.currentRef get structure.data getOrElse default def injectSettings: Seq[Setting[_]] = Seq( @@ -97,7 +96,7 @@ object EvaluateTask def logIncomplete(result: Incomplete, state: State, streams: Streams) { val all = Incomplete linearize result - val keyed = for(Incomplete(Some(key: Project.ScopedKey[_]), _, msg, _, ex) <- all) yield (key, msg, ex) + val keyed = for(Incomplete(Some(key: ScopedKey[_]), _, msg, _, ex) <- all) yield (key, msg, ex) val un = all.filter { i => i.node.isEmpty || i.message.isEmpty } import ExceptionCategory._ @@ -199,7 +198,7 @@ object EvaluateTask case _ => c.toString } def name(node: Task[_]): String = - node.info.name orElse transformNode(node).map(Project.displayFull) getOrElse ("") + node.info.name orElse transformNode(node).map(displayFull) getOrElse ("") def liftAnonymous: Incomplete => Incomplete = { case i @ Incomplete(node, tpe, None, causes, None) => causes.find( inc => !inc.node.isDefined && (inc.message.isDefined || inc.directCause.isDefined)) match { diff --git a/main/Extracted.scala b/main/Extracted.scala new file mode 100644 index 000000000..a9462df18 --- /dev/null +++ b/main/Extracted.scala @@ -0,0 +1,48 @@ +package sbt + + import Project._ + import Scope.GlobalScope + import Def.{ScopedKey, Setting} + +final case class Extracted(structure: BuildStructure, session: SessionSettings, currentRef: ProjectRef)(implicit val showKey: Show[ScopedKey[_]]) +{ + def rootProject = structure.rootProject + lazy val currentUnit = structure units currentRef.build + lazy val currentProject = currentUnit defined currentRef.project + lazy val currentLoader: ClassLoader = currentUnit.loader + def get[T](key: TaskKey[T]): Task[T] = get(key.task) + def get[T](key: SettingKey[T]) = getOrError(inCurrent(key), key.key) + def getOpt[T](key: SettingKey[T]): Option[T] = structure.data.get(inCurrent(key), key.key) + private[this] def inCurrent[T](key: SettingKey[T]): Scope = if(key.scope.project == This) key.scope.copy(project = Select(currentRef)) else key.scope + @deprecated("This method does not apply state changes requested during task execution. Use 'runTask' instead, which does.", "0.11.1") + def evalTask[T](key: TaskKey[T], state: State): T = runTask(key, state)._2 + def runTask[T](key: TaskKey[T], state: State): (State, T) = + { + import EvaluateTask._ + val rkey = resolve(key.scopedKey) + val config = extractedConfig(this, structure) + val value: Option[(State, Result[T])] = apply(structure, key.task.scopedKey, state, currentRef, config) + val (newS, result) = getOrError(rkey.scope, rkey.key, value) + (newS, processResult(result, newS.log)) + } + def runAggregated[T](key: TaskKey[T], state: State): State = + { + val rkey = resolve(key.scopedKey) + val keys = Aggregation.aggregate(rkey, ScopeMask(), structure.extra) + val tasks = Act.keyValues(structure)(keys) + Aggregation.runTasks(state, structure, tasks, Aggregation.Dummies(KNil, HNil), show = false )(showKey) + } + private[this] def resolve[T](key: ScopedKey[T]): ScopedKey[T] = + Project.mapScope(Scope.resolveScope(GlobalScope, currentRef.build, rootProject) )( key.scopedKey ) + private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])(implicit display: Show[ScopedKey[_]]): T = + value getOrElse error(display(ScopedKey(scope, key)) + " is undefined.") + private def getOrError[T](scope: Scope, key: AttributeKey[T])(implicit display: Show[ScopedKey[_]]): T = + structure.data.get(scope, key) getOrElse error(display(ScopedKey(scope, key)) + " is undefined.") + + def append(settings: Seq[Setting[_]], state: State): State = + { + val appendSettings = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) + val newStructure = Load.reapply(session.original ++ appendSettings, structure) + Project.setProject(session, newStructure, state) + } +} diff --git a/main/GlobalPlugin.scala b/main/GlobalPlugin.scala index 6c61ecb98..6ff26e326 100644 --- a/main/GlobalPlugin.scala +++ b/main/GlobalPlugin.scala @@ -1,7 +1,7 @@ package sbt import Load._ - import Project._ + import Def.{ScopedKey,Setting} import Scoped._ import Keys._ import Configurations.{Compile,Runtime} @@ -64,7 +64,7 @@ object GlobalPlugin (newS, processResult(result, newS.log)) } } - val globalPluginSettings = inScope(Scope.GlobalScope in LocalRootProject)(Seq( + val globalPluginSettings = Project.inScope(Scope.GlobalScope in LocalRootProject)(Seq( organization := SbtArtifacts.Organization, onLoadMessage <<= Keys.baseDirectory("Loading global plugins from " + _), name := "global-plugin", diff --git a/main/IvyConsole.scala b/main/IvyConsole.scala index 39318f851..479826635 100644 --- a/main/IvyConsole.scala +++ b/main/IvyConsole.scala @@ -6,6 +6,7 @@ package sbt import java.io.File import Attributed.blankSeq import Configurations.Compile + import Def.Setting import Keys._ object IvyConsole @@ -22,7 +23,7 @@ object IvyConsole val extracted = Project.extract(session, structure) import extracted._ - val depSettings: Seq[Project.Setting[_]] = Seq( + val depSettings: Seq[Setting[_]] = Seq( libraryDependencies ++= managed.reverse, resolvers ++= repos.reverse, unmanagedJars in Compile ++= Attributed blankSeq unmanaged.reverse, diff --git a/main/KeyIndex.scala b/main/KeyIndex.scala index c7ae46a63..5183476d9 100644 --- a/main/KeyIndex.scala +++ b/main/KeyIndex.scala @@ -4,8 +4,7 @@ package sbt import java.net.URI - import Project.ScopedKey - import Load.BuildStructure + import Def.ScopedKey import complete.DefaultParsers.validID import Types.{idFun, some} diff --git a/main/Keys.scala b/main/Keys.scala index 64fb210a5..fb26359d0 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -5,7 +5,7 @@ package sbt import java.io.File import java.net.URL - import Project.ScopedKey + import Def.ScopedKey import complete._ import inc.Analysis import inc.Locate.DefinesClass @@ -38,9 +38,9 @@ object Keys // Project keys val projectCommand = AttributeKey[Boolean]("project-command", "Marks Commands that were registered for the current Project.", Invisible) val sessionSettings = AttributeKey[SessionSettings]("session-settings", "Tracks current build, project, and setting modifications.", DSetting) - val stateBuildStructure = AttributeKey[Load.BuildStructure]("build-structure", "Data structure containing all information about the build definition.", BSetting) - val buildStructure = TaskKey[Load.BuildStructure]("build-structure", "Provides access to the build structure, settings, and streams manager.", DTask) - val loadedBuild = SettingKey[Load.LoadedBuild]("loaded-build", "Provides access to the loaded project structure. This is the information available before settings are evaluated.", DSetting) + val stateBuildStructure = AttributeKey[BuildStructure]("build-structure", "Data structure containing all information about the build definition.", BSetting) + val buildStructure = TaskKey[BuildStructure]("build-structure", "Provides access to the build structure, settings, and streams manager.", DTask) + val loadedBuild = SettingKey[LoadedBuild]("loaded-build", "Provides access to the loaded project structure. This is the information available before settings are evaluated.", DSetting) val buildDependencies = SettingKey[BuildDependencies]("build-dependencies", "Definitive source of inter-project dependencies for compilation and dependency management.\n\tThis is populated by default by the dependencies declared on Project instances, but may be modified.\n\tThe main restriction is that new builds may not be introduced.", DSetting) val appConfiguration = SettingKey[xsbti.AppConfiguration]("app-configuration", "Provides access to the launched sbt configuration, including the ScalaProvider, Launcher, and GlobalLock.", DSetting) val thisProject = SettingKey[ResolvedProject]("this-project", "Provides the current project for the referencing scope.", CSetting) @@ -70,10 +70,6 @@ object Keys // Path Keys val baseDirectory = SettingKey[File]("base-directory", "The base directory. Depending on the scope, this is the base directory for the build, project, configuration, or task.", AMinusSetting) - val globalBaseDirectory = AttributeKey[File]("global-base-directory", "The base directory for global sbt configuration and staging.", DSetting) - val globalPluginsDirectory = AttributeKey[File]("global-plugins-directory", "The base directory for global sbt plugins.", DSetting) - val globalSettingsDirectory = AttributeKey[File]("global-settings-directory", "The base directory for global sbt settings.", DSetting) - val stagingDirectory = AttributeKey[File]("staging-directory", "The directory for staging remote projects.", DSetting) val target = SettingKey[File]("target", "Main directory for files generated by the build.", AMinusSetting) val crossTarget = SettingKey[File]("cross-target", "Main directory for files generated by the build that are cross-built.", BSetting) @@ -210,7 +206,7 @@ object Keys val isModule = AttributeKey[Boolean]("is-module", "True if the target is a module.", DSetting) // Classpath/Dependency Management Keys - type Classpath = Seq[Attributed[File]] + type Classpath = Def.Classpath val name = SettingKey[String]("name", "Project name.", APlusSetting) val normalizedName = SettingKey[String]("normalized-name", "Project name transformed from mixed case and spaces to lowercase and dash-separated.", BSetting) @@ -333,12 +329,11 @@ object Keys val (executionRoots, dummyRoots)= dummy[Seq[ScopedKey[_]]]("execution-roots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.") val (state, dummyState) = dummy[State]("state", "Current build state.") val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager", "Streams manager, which provides streams for different contexts.") - val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", DSetting) + val resolvedScoped = Def.resolvedScoped val pluginData = TaskKey[PluginData]("plugin-data", "Information from the plugin build needed in the main build definition.", DTask) - private[sbt] val parseResult: TaskKey[Any] = TaskKey("$parse-result", "Internal: used to implement input tasks.", Invisible) - val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by") - val runBefore = AttributeKey[Seq[Task[_]]]("run-before") + val triggeredBy = Def.triggeredBy + val runBefore = Def.runBefore type Streams = std.Streams[ScopedKey[_]] type TaskStreams = std.TaskStreams[ScopedKey[_]] @@ -351,47 +346,3 @@ object Keys } def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false } - -object KeyRanks -{ - // task and setting ranks, used to prioritize displaying information - // main tasks - final val APlusTask = 4 - final val ATask = 5 - final val AMinusTask = 6 - - // main settings - final val APlusSetting = 9 - final val ASetting = 10 - final val AMinusSetting = 11 - - // less major tasks or tasks that print useful information - final val BPlusTask = 29 - final val BTask = 30 - final val BMinusTask = 31 - - // secondary settings - final val BPlusSetting = 39 - final val BSetting = 40 - final val BMinusSetting = 41 - - // advanced settings - final val CSetting = 100 - // advanced tasks - final val CTask = 200 - // explicit settings - final val DSetting = 10000 - // explicit tasks - final val DTask = 20000 - - final val MainTaskCutoff = AMinusTask - final val MainSettingCutoff = AMinusSetting - final val MainCutoff = math.max(AMinusTask, AMinusSetting) - - final val DefaultTaskRank = (ATask + BTask)/2 - final val DefaultInputRank = ATask // input tasks are likely a main task - final val DefaultSettingRank = (ASetting + BSetting) / 2 - - // implementation details - val Invisible = Int.MaxValue -} diff --git a/main/Load.scala b/main/Load.scala index 4c127d4cf..bfe38a7db 100755 --- a/main/Load.scala +++ b/main/Load.scala @@ -13,20 +13,20 @@ package sbt import collection.mutable import Compiler.{Compilers,Inputs} import inc.{FileValueCache, Locate} - import Project.{inScope, ScopedKey, ScopeLocal, Setting} - import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, Streams, thisProject, thisProjectRef, update} - import Keys.{exportedProducts, isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey} + import Project.{inScope,makeSettings} + import Def.{parseResult, ScopedKey, ScopeLocal, Setting} + import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, thisProject, thisProjectRef, update} + import Keys.{exportedProducts, isDummy, loadedBuild, resolvedScoped, taskDefinitionKey} import tools.nsc.reporters.ConsoleReporter import Build.{analyzed, data} import Scope.{GlobalScope, ThisScope} import Types.const - -object Load -{ import BuildPaths._ import BuildStreams._ import Locate.DefinesClass - + +object Load +{ // note that there is State passed in but not pulled out def defaultLoad(state: State, baseDirectory: File, log: Logger, isPlugin: Boolean = false, topLevelExtras: List[URI] = Nil): (() => Eval, BuildStructure) = { @@ -55,7 +55,7 @@ object Load new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass, delegates, EvaluateTask.injectStreams, pluginMgmt, inject, None, Nil, log) } - def injectGlobal(state: State): Seq[Project.Setting[_]] = + def injectGlobal(state: State): Seq[Setting[_]] = (appConfiguration in GlobalScope :== state.configuration) +: EvaluateTask.injectSettings def defaultWithGlobal(state: State, base: File, rawConfig: LoadBuildConfiguration, globalBase: File, log: Logger): LoadBuildConfiguration = @@ -126,7 +126,7 @@ object Load lazy val rootEval = lazyEval(loaded.units(loaded.root).unit) val settings = finalTransforms(buildConfigurations(loaded, getRootProject(projects), rootEval, config.injectSettings)) val delegates = config.delegates(loaded) - val data = Project.makeSettings(settings, delegates, config.scopeLocal)( Project.showLoadingKey( loaded ) ) + val data = makeSettings(settings, delegates, config.scopeLocal)( Project.showLoadingKey( loaded ) ) val index = structureIndex(data, settings, loaded.extra(data)) val streams = mkStreams(projects, loaded.root, data) (rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal)) @@ -156,7 +156,7 @@ object Load case resolvedScoped.key => Some(defining.asInstanceOf[T]) case parseResult.key => import std.TaskExtra._ - val getResult = InputTask.inputMap map { m => m get defining getOrElse error("No parsed value for " + Project.displayFull(defining) + "\n" + m) } + val getResult = InputTask.inputMap map { m => m get defining getOrElse error("No parsed value for " + Def.displayFull(defining) + "\n" + m) } Some(getResult.asInstanceOf[T]) case _ => None } @@ -180,7 +180,7 @@ object Load def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure)(implicit display: Show[ScopedKey[_]]): BuildStructure = { val transformed = finalTransforms(newSettings) - val newData = Project.makeSettings(transformed, structure.delegates, structure.scopeLocal) + val newData = makeSettings(transformed, structure.delegates, structure.scopeLocal) val newIndex = structureIndex(newData, transformed, index => buildUtil(structure.root, structure.units, index, newData)) val newStreams = mkStreams(structure.units, structure.root, newData) new BuildStructure(units = structure.units, root = structure.root, settings = transformed, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal) @@ -439,7 +439,7 @@ object Load "Put .sbt plugin definitions directly in project/,\n .scala plugin definitions in project/project/,\n and remove the project/plugins/ directory.") val plugs = plugins(pluginDir, s, config) val defs = definitionSources(defDir) - val target = buildOutputDirectory(defDir, config.compilers) + val target = buildOutputDirectory(defDir, config.compilers.scalac.scalaInstance) IO.createDirectory(target) val loadedDefs = if(defs.isEmpty) @@ -594,10 +594,6 @@ object Load def loadPlugin(pluginName: String, loader: ClassLoader): Plugin = ModuleUtilities.getObject(pluginName, loader).asInstanceOf[Plugin] - def importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil - def importAllRoot(values: Seq[String]) = importAll(values map rootedName) - def rootedName(s: String) = if(s contains '.') "_root_." + s else s - def findPlugins(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Plugin") def findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build") def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] = @@ -634,93 +630,65 @@ object Load } def defaultEvalOptions: Seq[String] = Nil - def baseImports = "import sbt._, Process._, Keys._" :: Nil - final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]]) - final class LoadedDefinitions(val base: File, val target: Seq[File], val loader: ClassLoader, val builds: Seq[Build], val buildNames: Seq[String]) - final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val plugins: Seq[Plugin], val pluginNames: Seq[String]) - { - def fullClasspath: Seq[Attributed[File]] = pluginData.classpath - def classpath: Seq[File] = data(fullClasspath) - } - final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins) - { - override def toString = if(uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase +")") - } - - final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit]) - { - checkCycles(units) - def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj - def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = buildUtil(root, units, keyIndex, data) - } - def checkCycles(units: Map[URI, LoadedBuildUnit]) - { - def getRef(pref: ProjectRef) = units(pref.build).defined(pref.project) - def deps(proj: ResolvedProject)(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = Dag.topologicalSort(proj)(p => base(p) map getRef) - // check for cycles - for( (_, lbu) <- units; proj <- lbu.defined.values) { - deps(proj)(_.dependencies.map(_.project)) - deps(proj)(_.delegates) - deps(proj)(_.aggregate) - } - } - final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit]) - sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] } - final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase - { - def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings) - def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f) - } - final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase - { - assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit) - val root = rootProjects.head - def localBase = unit.localBase - def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath - def loader = unit.definitions.loader - def imports = getImports(unit) - override def toString = unit.toString - } - def getImports(unit: BuildUnit) = baseImports ++ importAllRoot(unit.plugins.pluginNames ++ unit.definitions.buildNames) + @deprecated("Use BuildUtil.baseImports", "0.13.0") + def baseImports = BuildUtil.baseImports + @deprecated("Use BuildUtil.checkCycles", "0.13.0") + def checkCycles(units: Map[URI, LoadedBuildUnit]): Unit = BuildUtil.checkCycles(units) + @deprecated("Use BuildUtil.importAll", "0.13.0") + def importAll(values: Seq[String]): Seq[String] = BuildUtil.importAll(values) + @deprecated("Use BuildUtil.importAllRoot", "0.13.0") + def importAllRoot(values: Seq[String]): Seq[String] = BuildUtil.importAllRoot(values) + @deprecated("Use BuildUtil.rootedNames", "0.13.0") + def rootedName(s: String): String = BuildUtil.rootedName(s) + @deprecated("Use BuildUtil.getImports", "0.13.0") + def getImports(unit: BuildUnit): Seq[String] = BuildUtil.getImports(unit) def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced } - final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: State => Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal) - { - val rootProject: URI => String = Load getRootProject units - def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq - def allProjects(build: URI): Seq[ResolvedProject] = units.get(build).toList.flatMap(_.defined.values) - def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap { case (build, unit) => refs(build, unit.defined.values.toSeq) } - def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build)) - val extra: BuildUtil[ResolvedProject] = buildUtil(root, units, index.keyIndex, data) - private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) } - } - def buildUtil(root: URI, units: Map[URI, LoadedBuildUnit], keyIndex: KeyIndex, data: Settings[Scope]): BuildUtil[ResolvedProject] = - { - val getp = (build: URI, project: String) => Load.getProject(units, build, project) - val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name)) - val aggregates = Aggregation.relation(units) - new BuildUtil(keyIndex, data, root, Load getRootProject units, getp, configs, aggregates) - } - final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader, - compilers: Compilers, evalPluginDef: (BuildStructure, State) => PluginData, definesClass: DefinesClass, - delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, - pluginManagement: PluginManagement, injectSettings: InjectSettings, globalPlugin: Option[GlobalPlugin], extraBuilds: Seq[URI], - log: Logger) - { - lazy val (globalPluginClasspath, globalPluginLoader) = pluginDefinitionLoader(this, Load.globalPluginClasspath(globalPlugin)) - lazy val globalPluginNames = if(globalPluginClasspath.isEmpty) Nil else getPluginNames(globalPluginClasspath, globalPluginLoader) - } + @deprecated("LoadedBuildUnit is now top-level", "0.13.0") + type LoadedBuildUnit = sbt.LoadedBuildUnit + + @deprecated("BuildStructure is now top-level", "0.13.0") + type BuildStructure = sbt.BuildStructure + + @deprecated("StructureIndex is now top-level", "0.13.0") + type StructureIndex = sbt.StructureIndex + + @deprecated("LoadBuildConfiguration is now top-level", "0.13.0") + type LoadBuildConfiguration = sbt.LoadBuildConfiguration + @deprecated("LoadBuildConfiguration is now top-level", "0.13.0") + val LoadBuildConfiguration = sbt.LoadBuildConfiguration + + final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]]) final case class InjectSettings(global: Seq[Setting[_]], project: Seq[Setting[_]], projectLoaded: ClassLoader => Seq[Setting[_]]) - // information that is not original, but can be reconstructed from the rest of BuildStructure - final class StructureIndex( - val keyMap: Map[String, AttributeKey[_]], - val taskToKey: Map[Task[_], ScopedKey[Task[_]]], - val triggers: Triggers[Task], - val keyIndex: KeyIndex, - val aggregateKeyIndex: KeyIndex - ) + @deprecated("LoadedDefinitions is now top-level", "0.13.0") + type LoadedDefinitions = sbt.LoadedDefinitions + @deprecated("LoadedPlugins is now top-level", "0.13.0") + type LoadedPlugins = sbt.LoadedPlugins + @deprecated("BuildUnit is now top-level", "0.13.0") + type BuildUnit = sbt.BuildUnit + @deprecated("LoadedBuild is now top-level", "0.13.0") + type LoadedBuild = sbt.LoadedBuild + @deprecated("PartBuild is now top-level", "0.13.0") + type PartBuild = sbt.PartBuild + @deprecated("BuildUnitBase is now top-level", "0.13.0") + type BuildUnitBase = sbt.BuildUnitBase + @deprecated("PartBuildUnit is now top-level", "0.13.0") + type PartBuildUnit = sbt.PartBuildUnit + @deprecated("Use BuildUtil.apply", "0.13.0") + def buildUtil(root: URI, units: Map[URI, LoadedBuildUnit], keyIndex: KeyIndex, data: Settings[Scope]): BuildUtil[ResolvedProject] = BuildUtil(root, units, keyIndex, data) } + +final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader, + compilers: Compilers, evalPluginDef: (BuildStructure, State) => PluginData, definesClass: DefinesClass, + delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, + pluginManagement: PluginManagement, injectSettings: Load.InjectSettings, globalPlugin: Option[GlobalPlugin], extraBuilds: Seq[URI], + log: Logger) +{ + lazy val (globalPluginClasspath, globalPluginLoader) = Load.pluginDefinitionLoader(this, Load.globalPluginClasspath(globalPlugin)) + lazy val globalPluginNames = if(globalPluginClasspath.isEmpty) Nil else Load.getPluginNames(globalPluginClasspath, globalPluginLoader) +} + final class IncompatiblePluginsException(msg: String, cause: Throwable) extends Exception(msg, cause) \ No newline at end of file diff --git a/main/LogManager.scala b/main/LogManager.scala index 220d15881..3b4a0a653 100644 --- a/main/LogManager.scala +++ b/main/LogManager.scala @@ -7,7 +7,7 @@ package sbt import java.io.File import LogManager._ import std.Transform - import Project.ScopedKey + import Def.ScopedKey import Scope.GlobalScope import MainLogging._ import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, state, traceLevel} diff --git a/main/Main.scala b/main/Main.scala index cb80bcc29..d4c0aa3fd 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -94,7 +94,7 @@ object BuiltinCommands if(Project.isProjectLoaded(s)) { val e = Project.extract(s) - val current = "The current project is " + Project.display(e.currentRef) + "\n" + val current = "The current project is " + Reference.display(e.currentRef) + "\n" val sc = aboutScala(s, e) val built = if(sc.isEmpty) "" else "The current project is built against " + sc + "\n" current + built + aboutPlugins(e) @@ -199,7 +199,7 @@ object BuiltinCommands s } def sessionCommand = Command.make(SessionCommand, sessionBrief, SessionSettings.Help)(SessionSettings.command) - def reapply(newSession: SessionSettings, structure: Load.BuildStructure, s: State): State = + def reapply(newSession: SessionSettings, structure: BuildStructure, s: State): State = { s.log.info("Reapplying settings...") val newStructure = Load.reapply(newSession.mergeSettings, structure)( Project.showContextKey(newSession, structure) ) @@ -215,13 +215,13 @@ object BuiltinCommands reapply(setResult.session, structure, s) } // @deprecated("Use SettingCompletions.setThis", "0.13.0") - def setThis(s: State, extracted: Extracted, settings: Seq[Project.Setting[_]], arg: String) = + def setThis(s: State, extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String) = SettingCompletions.setThis(s, extracted, settings, arg) def inspect = Command(InspectCommand, inspectBrief, inspectDetailed)(inspectParser) { case (s, (option, sk)) => s.log.info(inspectOutput(s, option, sk)) s } - def inspectOutput(s: State, option: InspectOption, sk: Project.ScopedKey[_]): String = + def inspectOutput(s: State, option: InspectOption, sk: Def.ScopedKey[_]): String = { val extracted = Project.extract(s) import extracted._ @@ -262,7 +262,7 @@ object BuiltinCommands import InspectOption._ def inspectParser = (s: State) => spacedInspectOptionParser(s) flatMap { - case opt @ (Uses | Definitions) => allKeyParser(s).map(key => (opt, Project.ScopedKey(Global, key))) + case opt @ (Uses | Definitions) => allKeyParser(s).map(key => (opt, Def.ScopedKey(Global, key))) case opt @ (DependencyTree | Details(_)) => spacedKeyParser(s).map(key => (opt, key)) } val spacedInspectOptionParser: (State => Parser[InspectOption]) = (s: State) => { @@ -322,7 +322,7 @@ object BuiltinCommands extracted.structure.units(curi).imports.map(s => (s, -1)) } - def listBuild(uri: URI, build: Load.LoadedBuildUnit, current: Boolean, currentID: String, log: Logger) = + def listBuild(uri: URI, build: LoadedBuildUnit, current: Boolean, currentID: String, log: Logger) = { log.info("In " + uri) def prefix(id: String) = if(currentID != id) " " else if(current) " * " else "(*)" diff --git a/main/Opts.scala b/main/Opts.scala index 7f4d82682..ecd10c9c0 100644 --- a/main/Opts.scala +++ b/main/Opts.scala @@ -33,7 +33,8 @@ object DefaultOptions { import Opts._ import Path._ import BuildPaths.{getGlobalBase, getGlobalSettingsDirectory} - import Project.{Setting, extract} + import Project.{extract, richInitializeTask} + import Def.Setting def javac: Seq[String] = compile.encoding("UTF-8") def scalac: Seq[String] = compile.encoding("UTF-8") diff --git a/main/Output.scala b/main/Output.scala index 2964f87da..22b6c44aa 100644 --- a/main/Output.scala +++ b/main/Output.scala @@ -6,7 +6,7 @@ package sbt import java.util.regex.Pattern import java.io.File import Keys.{Streams, TaskStreams} - import Project.ScopedKey + import Def.ScopedKey import Aggregation.{KeyValue, Values} import Types.idFun import Highlight.{bold, showMatches} diff --git a/main/PluginManagement.scala b/main/PluginManagement.scala index 81a906cda..ef65c2a65 100644 --- a/main/PluginManagement.scala +++ b/main/PluginManagement.scala @@ -1,7 +1,7 @@ package sbt import Keys.Classpath - import Project.Setting + import Def.Setting import PluginManagement._ import java.net.{URL,URLClassLoader} diff --git a/main/Project.scala b/main/Project.scala index f87eff749..68cb4df55 100755 --- a/main/Project.scala +++ b/main/Project.scala @@ -6,10 +6,10 @@ package sbt import java.io.File import java.net.URI import Project._ - import Keys.{appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, sessionVars, shellPrompt, thisProject, thisProjectRef, watch} + import Keys.{appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, thisProject, thisProjectRef, watch} import Scope.{GlobalScope,ThisScope} - import Load.BuildStructure - import Types.{idFun, Id} + import Def.{Flattened, Initialize, ScopedKey, Setting} + import Types.idFun import complete.DefaultParsers sealed trait ProjectDefinition[PR <: ProjectReference] @@ -17,7 +17,7 @@ sealed trait ProjectDefinition[PR <: ProjectReference] def id: String def base: File def configurations: Seq[Configuration] - def settings: Seq[Project.Setting[_]] + def settings: Seq[Setting[_]] def aggregate: Seq[PR] @deprecated("Delegation between projects should be replaced by directly sharing settings.", "0.13.0") def delegates: Seq[PR] @@ -36,7 +36,7 @@ sealed trait ProjectDefinition[PR <: ProjectReference] sealed trait Project extends ProjectDefinition[ProjectReference] { def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies, - delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Project.Setting[_]] = settings, configurations: Seq[Configuration] = configurations, + delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations, auto: AddSettings = auto): Project = Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto) @@ -61,85 +61,28 @@ sealed trait Project extends ProjectDefinition[ProjectReference] def delegateTo(from: ProjectReference*): Project = copy(delegates = delegates ++ from) def aggregate(refs: ProjectReference*): Project = copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs) def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs) - def settings(ss: Project.Setting[_]*): Project = copy(settings = (settings: Seq[Project.Setting[_]]) ++ ss) + def settings(ss: Setting[_]*): Project = copy(settings = (settings: Seq[Setting[_]]) ++ ss) def autoSettings(select: AddSettings*): Project = copy(auto = AddSettings.seq(select : _*)) } sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] -final case class Extracted(structure: BuildStructure, session: SessionSettings, currentRef: ProjectRef)(implicit val showKey: Show[ScopedKey[_]]) -{ - def rootProject = structure.rootProject - lazy val currentUnit = structure units currentRef.build - lazy val currentProject = currentUnit defined currentRef.project - lazy val currentLoader: ClassLoader = currentUnit.loader - def get[T](key: TaskKey[T]): Task[T] = get(key.task) - def get[T](key: SettingKey[T]) = getOrError(inCurrent(key), key.key) - def getOpt[T](key: SettingKey[T]): Option[T] = structure.data.get(inCurrent(key), key.key) - private[this] def inCurrent[T](key: SettingKey[T]): Scope = if(key.scope.project == This) key.scope.copy(project = Select(currentRef)) else key.scope - @deprecated("This method does not apply state changes requested during task execution. Use 'runTask' instead, which does.", "0.11.1") - def evalTask[T](key: TaskKey[T], state: State): T = runTask(key, state)._2 - def runTask[T](key: TaskKey[T], state: State): (State, T) = - { - import EvaluateTask._ - val rkey = resolve(key.scopedKey) - val config = extractedConfig(this, structure) - val value: Option[(State, Result[T])] = apply(structure, key.task.scopedKey, state, currentRef, config) - val (newS, result) = getOrError(rkey.scope, rkey.key, value) - (newS, processResult(result, newS.log)) - } - def runAggregated[T](key: TaskKey[T], state: State): State = - { - val rkey = resolve(key.scopedKey) - val keys = Aggregation.aggregate(rkey, ScopeMask(), structure.extra) - val tasks = Act.keyValues(structure)(keys) - Aggregation.runTasks(state, structure, tasks, Aggregation.Dummies(KNil, HNil), show = false )(showKey) - } - private[this] def resolve[T](key: ScopedKey[T]): ScopedKey[T] = - Project.mapScope(Scope.resolveScope(GlobalScope, currentRef.build, rootProject) )( key.scopedKey ) - private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])(implicit display: Show[ScopedKey[_]]): T = - value getOrElse error(display(ScopedKey(scope, key)) + " is undefined.") - private def getOrError[T](scope: Scope, key: AttributeKey[T])(implicit display: Show[ScopedKey[_]]): T = - structure.data.get(scope, key) getOrElse error(display(ScopedKey(scope, key)) + " is undefined.") - - def append(settings: Seq[Setting[_]], state: State): State = - { - val appendSettings = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) - val newStructure = Load.reapply(session.original ++ appendSettings, structure) - Project.setProject(session, newStructure, state) - } -} - sealed trait ClasspathDep[PR <: ProjectReference] { def project: PR; def configuration: Option[String] } final case class ResolvedClasspathDependency(project: ProjectRef, configuration: Option[String]) extends ClasspathDep[ProjectRef] final case class ClasspathDependency(project: ProjectReference, configuration: Option[String]) extends ClasspathDep[ProjectReference] -object Project extends Init[Scope] with ProjectExtra +object Project extends ProjectExtra { - lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None) - def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] = - new Show[ScopedKey[_]] { def apply(key: ScopedKey[_]) = displayFull(key, keyNameColor) } def showContextKey(state: State): Show[ScopedKey[_]] = showContextKey(state, None) def showContextKey(state: State, keyNameColor: Option[String]): Show[ScopedKey[_]] = - if(isProjectLoaded(state)) showContextKey( session(state), structure(state), keyNameColor ) else showFullKey + if(isProjectLoaded(state)) showContextKey( session(state), structure(state), keyNameColor ) else Def.showFullKey def showContextKey(session: SessionSettings, structure: BuildStructure, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = - showRelativeKey(session.current, structure.allProjects.size > 1, keyNameColor) + Def.showRelativeKey(session.current, structure.allProjects.size > 1, keyNameColor) - def showLoadingKey(loaded: Load.LoadedBuild, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = - showRelativeKey( ProjectRef(loaded.root, loaded.units(loaded.root).rootProjects.head), loaded.allProjectRefs.size > 1, keyNameColor) - - def showRelativeKey(current: ProjectRef, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = new Show[ScopedKey[_]] { - def apply(key: ScopedKey[_]) = - Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayRelative(current, multi, ref)) - } - def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match { - case BuildRef(current.build) => "{.}/" - case `current` => if(multi) current.project + "/" else "" - case ProjectRef(current.build, x) => x + "/" - case _ => display(project) + "/" - } + def showLoadingKey(loaded: LoadedBuild, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = + Def.showRelativeKey( ProjectRef(loaded.root, loaded.units(loaded.root).rootProjects.head), loaded.allProjectRefs.size > 1, keyNameColor) private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]], delegates0: => Seq[PR], settings0: => Seq[Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings) extends ProjectDefinition[PR] @@ -183,8 +126,8 @@ object Project extends Init[Scope] with ProjectExtra def getProjectForReference(ref: Reference, structure: BuildStructure): Option[ResolvedProject] = ref match { case pr: ProjectRef => getProject(pr, structure); case _ => None } def getProject(ref: ProjectRef, structure: BuildStructure): Option[ResolvedProject] = getProject(ref, structure.units) - def getProject(ref: ProjectRef, structure: Load.LoadedBuild): Option[ResolvedProject] = getProject(ref, structure.units) - def getProject(ref: ProjectRef, units: Map[URI, Load.LoadedBuildUnit]): Option[ResolvedProject] = + def getProject(ref: ProjectRef, structure: LoadedBuild): Option[ResolvedProject] = getProject(ref, structure.units) + def getProject(ref: ProjectRef, units: Map[URI, LoadedBuildUnit]): Option[ResolvedProject] = (units get ref.build).flatMap(_.defined get ref.project) def runUnloadHooks(s: State): State = @@ -227,40 +170,11 @@ object Project extends Init[Scope] with ProjectExtra def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap = vopt match { case Some(v) => attributes.put(key, v); case None => attributes.remove(key) } def makeSettings(settings: Seq[Setting[_]], delegates: Scope => Seq[Scope], scopeLocal: ScopedKey[_] => Seq[Setting[_]])(implicit display: Show[ScopedKey[_]]) = - make(settings)(delegates, scopeLocal, display) + Def.make(settings)(delegates, scopeLocal, display) def equal(a: ScopedKey[_], b: ScopedKey[_], mask: ScopeMask): Boolean = a.key == b.key && Scope.equal(a.scope, b.scope, mask) - def colored(s: String, color: Option[String]): String = color match { - case Some(c) => c + s + scala.Console.RESET - case None => s - } - def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None) - def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor)) - def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask) - def display(ref: Reference): String = - ref match - { - case pr: ProjectReference => display(pr) - case br: BuildReference => display(br) - } - def display(ref: BuildReference) = - ref match - { - case ThisBuild => "{}" - case BuildRef(uri) => "{" + uri + "}" - } - def display(ref: ProjectReference) = - ref match - { - case ThisProject => "{}" - case LocalRootProject => "{}" - case LocalProject(id) => "{}" + id - case RootProject(uri) => "{" + uri + " }" - case ProjectRef(uri, id) => "{" + uri + "}" + id - } - def fillTaskAxis(scoped: ScopedKey[_]): ScopedKey[_] = ScopedKey(Scope.fillTaskAxis(scoped.scope, scoped.key), scoped.key) @@ -294,7 +208,7 @@ object Project extends Init[Scope] with ProjectExtra case Some(sc) => "Provided by:\n\t" + Scope.display(sc, key.label) + "\n" case None => "" } - val comp = compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display) + val comp = Def.compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display) val definedAt = comp get scoped map { c => def fmt(s: Setting[_]) = s.pos match { case pos: FilePosition => Some(pos.path + ":" + pos.startLine) @@ -309,7 +223,7 @@ object Project extends Init[Scope] with ProjectExtra } getOrElse "" - val cMap = flattenLocals(comp) + val cMap = Def.flattenLocals(comp) val related = cMap.keys.filter(k => k.key == key && k.scope != scope) val depends = cMap.get(scoped) match { case Some(c) => c.dependencies.toSet; case None => Set.empty } @@ -343,7 +257,7 @@ object Project extends Init[Scope] with ProjectExtra def relation(structure: BuildStructure, actual: Boolean)(implicit display: Show[ScopedKey[_]]) = { type Rel = Relation[ScopedKey[_], ScopedKey[_]] - val cMap = flattenLocals(compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display)) + val cMap = Def.flattenLocals(Def.compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display)) ((Relation.empty: Rel) /: cMap) { case (r, (key, value)) => r + (key, value.dependencies) } @@ -412,26 +326,25 @@ object Project extends Init[Scope] with ProjectExtra val extracted = Project.extract(state) EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config) } - // this is here instead of Scoped so that it is considered without need for import (because of Project.Initialize) - implicit def richInitializeTask[T](init: Initialize[Task[T]]): Scoped.RichInitializeTask[T] = new Scoped.RichInitializeTask(init) - implicit def richInitializeInputTask[T](init: Initialize[InputTask[T]]): Scoped.RichInitializeInputTask[T] = new Scoped.RichInitializeInputTask(init) - implicit def richInitialize[T](i: Initialize[T]): Scoped.RichInitialize[T] = new Scoped.RichInitialize[T](i) -} -final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) -{ - import Types.const - val key = scoped.key - val scope = scoped.scope - def typeName: String = fold(fmtMf("Task[%s]"), fmtMf("InputTask[%s]"), key.manifest.toString) - def settingValue: Option[Any] = fold(const(None), const(None), Some(value)) - def description: String = fold(fmtMf("Task: %s"), fmtMf("Input task: %s"), - "Setting: %s = %s" format (key.manifest.toString, value.toString)) - def fold[A](targ: OptManifest[_] => A, itarg: OptManifest[_] => A, s: => A): A = - if (key.manifest.erasure == classOf[Task[_]]) targ(key.manifest.typeArguments.head) - else if (key.manifest.erasure == classOf[InputTask[_]]) itarg(key.manifest.typeArguments.head) - else s - def fmtMf(s: String): OptManifest[_] => String = s format _ + /** Many methods were moved to Def in 0.13. This implicit makes those methods still available on Project for the transition. */ + @inline + @deprecated("Use Def directly", "0.13.0") + implicit def projectToDef(p: Project.type): Def.type = Def + + implicit def projectToRef(p: Project): ProjectReference = LocalProject(p.id) + + final class RichTaskSessionVar[S](i: Initialize[Task[S]]) + { + import SessionVar.{persistAndSet, resolveContext, set, transform => tx} + + def updateState(f: (State, S) => State): Initialize[Task[S]] = i(t => tx(t, f)) + def storeAs(key: TaskKey[S])(implicit f: sbinary.Format[S]): Initialize[Task[S]] = (Keys.resolvedScoped, i) { (scoped, task) => + tx(task, (state, value) => persistAndSet( resolveContext(key, scoped.scope, state), state, value)(f)) + } + def keepAs(key: TaskKey[S]): Initialize[Task[S]] = + (i, Keys.resolvedScoped)( (t,scoped) => tx(t, (state,value) => set(resolveContext(key, scoped.scope, state), state, value) ) ) + } } trait ProjectExtra @@ -439,6 +352,14 @@ trait ProjectExtra implicit def configDependencyConstructor[T <% ProjectReference](p: T): Constructor = new Constructor(p) implicit def classpathDependency[T <% ProjectReference](p: T): ClasspathDependency = new ClasspathDependency(p, None) + // These used to be in Project so that they didn't need to get imported (due to Initialize being nested in Project). + // Moving Initialize and other settings types to Def and decoupling Project, Def, and Structure means these go here for now + implicit def richInitializeTask[T](init: Initialize[Task[T]]): Scoped.RichInitializeTask[T] = new Scoped.RichInitializeTask(init) + implicit def richInitializeInputTask[T](init: Initialize[InputTask[T]]): Scoped.RichInitializeInputTask[T] = new Scoped.RichInitializeInputTask(init) + implicit def richInitialize[T](i: Initialize[T]): Scoped.RichInitialize[T] = new Scoped.RichInitialize[T](i) + + implicit def richTaskSessionVar[T](init: Initialize[Task[T]]): Project.RichTaskSessionVar[T] = new Project.RichTaskSessionVar(init) + def inConfig(conf: Configuration)(ss: Seq[Setting[_]]): Seq[Setting[_]] = inScope(ThisScope.copy(config = Select(conf)) )( (configuration :== conf) +: ss) def inTask(t: Scoped)(ss: Seq[Setting[_]]): Seq[Setting[_]] = @@ -446,69 +367,3 @@ trait ProjectExtra def inScope(scope: Scope)(ss: Seq[Setting[_]]): Seq[Setting[_]] = Project.transform(Scope.replaceThis(scope), ss) } - - import sbinary.{Format, Operations} -object SessionVar -{ - val DefaultDataID = "data" - - // these are required because of inference+manifest limitations - final case class Key[T](key: ScopedKey[Task[T]]) - final case class Map(map: IMap[Key, Id]) { - def get[T](k: ScopedKey[Task[T]]): Option[T] = map get Key(k) - def put[T](k: ScopedKey[Task[T]], v: T): Map = Map(map put (Key(k), v)) - } - def emptyMap = Map(IMap.empty) - - def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): State = - { - persist(key, state, value)(f) - set(key, state, value) - } - - def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): Unit = - Project.structure(state).streams(state).use(key)( s => - Operations.write(s.binary(DefaultDataID), value)(f) - ) - - def clear(s: State): State = s.put(sessionVars, SessionVar.emptyMap) - - def get[T](key: ScopedKey[Task[T]], state: State): Option[T] = orEmpty(state get sessionVars) get key - - def set[T](key: ScopedKey[Task[T]], state: State, value: T): State = state.update(sessionVars)(om => orEmpty(om) put (key, value)) - - def orEmpty(opt: Option[Map]) = opt getOrElse emptyMap - - def transform[S](task: Task[S], f: (State, S) => State): Task[S] = - { - val g = (s: S, map: AttributeMap) => map.put(Keys.transformState, (state: State) => f(state, s)) - task.copy(info = task.info.postTransform(g)) - } - - def resolveContext[T](key: ScopedKey[Task[T]], context: Scope, state: State): ScopedKey[Task[T]] = - { - val subScope = Scope.replaceThis(context)(key.scope) - val scope = Project.structure(state).data.definingScope(subScope, key.key) getOrElse subScope - ScopedKey(scope, key.key) - } - - def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] = - Project.structure(state).streams(state).use(key) { s => - try { Some(Operations.read(s.readBinary(key, DefaultDataID))) } - catch { case e: Exception => None } - } - - def load[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] = - get(key, state) orElse read(key, state)(f) - - def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)(implicit f: Format[T]): (State, Option[T]) = - get(key, state) match { - case s: Some[T] => (state, s) - case None => read(key, state)(f) match { - case s @ Some(t) => - val newState = if(setIfUnset && get(key, state).isDefined) state else set(key, state, t) - (newState, s) - case None => (state, None) - } - } -} diff --git a/main/RetrieveUnit.scala b/main/RetrieveUnit.scala new file mode 100644 index 000000000..833a0abea --- /dev/null +++ b/main/RetrieveUnit.scala @@ -0,0 +1,37 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + + import java.io.File + import java.net.URI + import BuildLoader.ResolveInfo + import Def.{ScopedKey, Setting} + +object RetrieveUnit +{ + def apply(info: ResolveInfo): Option[() => File] = + { + info.uri match { + case Scheme("svn") | Scheme("svn+ssh") => Resolvers.subversion(info) + case Scheme("hg") => Resolvers.mercurial(info) + case Scheme("git") => Resolvers.git(info) + case Path(path) if path.endsWith(".git") => Resolvers.git(info) + case Scheme("http") | Scheme("https") | Scheme("ftp") => Resolvers.remote(info) + case Scheme("file") => Resolvers.local(info) + case _ => None + } + } + + object Scheme + { + def unapply(uri: URI) = Option(uri.getScheme) + } + + object Path + { + import RichURI.fromURI + + def unapply(uri: URI) = Option(uri.withoutMarkerScheme.getPath) + } +} diff --git a/main/ScopedKeyData.scala b/main/ScopedKeyData.scala new file mode 100644 index 000000000..3ce5b5c73 --- /dev/null +++ b/main/ScopedKeyData.scala @@ -0,0 +1,19 @@ +package sbt + + import Def.ScopedKey + +final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) +{ + import Types.const + val key = scoped.key + val scope = scoped.scope + def typeName: String = fold(fmtMf("Task[%s]"), fmtMf("InputTask[%s]"), key.manifest.toString) + def settingValue: Option[Any] = fold(const(None), const(None), Some(value)) + def description: String = fold(fmtMf("Task: %s"), fmtMf("Input task: %s"), + "Setting: %s = %s" format (key.manifest.toString, value.toString)) + def fold[A](targ: OptManifest[_] => A, itarg: OptManifest[_] => A, s: => A): A = + if (key.manifest.erasure == classOf[Task[_]]) targ(key.manifest.typeArguments.head) + else if (key.manifest.erasure == classOf[InputTask[_]]) itarg(key.manifest.typeArguments.head) + else s + def fmtMf(s: String): OptManifest[_] => String = s format _ +} diff --git a/main/SessionSettings.scala b/main/SessionSettings.scala index 5e787c80b..3450c1db0 100755 --- a/main/SessionSettings.scala +++ b/main/SessionSettings.scala @@ -5,6 +5,7 @@ package sbt import java.io.File import java.net.URI + import Def.{ScopedKey,Setting} import Project._ import Types.Endo import compiler.Eval @@ -89,7 +90,7 @@ object SessionSettings val newSession = session.copy(append = newAppend.toMap, original = newOriginal.flatten.toSeq) reapply(newSession.copy(original = newSession.mergeSettings, append = Map.empty), s) } - def writeSettings(pref: ProjectRef, settings: List[SessionSetting], original: Seq[Setting[_]], structure: Load.BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = + def writeSettings(pref: ProjectRef, settings: List[SessionSetting], original: Seq[Setting[_]], structure: BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = { val project = Project.getProject(pref, structure).getOrElse(error("Invalid project reference " + pref)) val writeTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt")) @@ -140,7 +141,7 @@ object SessionSettings def printAllSettings(s: State): State = withSettings(s){ session => for( (ref, settings) <- session.append if !settings.isEmpty) { - println("In " + Project.display(ref)) + println("In " + Reference.display(ref)) printSettings(settings) } s diff --git a/main/SessionVar.scala b/main/SessionVar.scala new file mode 100644 index 000000000..00ab686e2 --- /dev/null +++ b/main/SessionVar.scala @@ -0,0 +1,71 @@ +package sbt + + import Def.ScopedKey + import Types.Id + import Keys.sessionVars + import sbinary.{Format, Operations} + +object SessionVar +{ + val DefaultDataID = "data" + + // these are required because of inference+manifest limitations + final case class Key[T](key: ScopedKey[Task[T]]) + final case class Map(map: IMap[Key, Id]) { + def get[T](k: ScopedKey[Task[T]]): Option[T] = map get Key(k) + def put[T](k: ScopedKey[Task[T]], v: T): Map = Map(map put (Key(k), v)) + } + def emptyMap = Map(IMap.empty) + + def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): State = + { + persist(key, state, value)(f) + set(key, state, value) + } + + def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): Unit = + Project.structure(state).streams(state).use(key)( s => + Operations.write(s.binary(DefaultDataID), value)(f) + ) + + def clear(s: State): State = s.put(sessionVars, SessionVar.emptyMap) + + def get[T](key: ScopedKey[Task[T]], state: State): Option[T] = orEmpty(state get sessionVars) get key + + def set[T](key: ScopedKey[Task[T]], state: State, value: T): State = state.update(sessionVars)(om => orEmpty(om) put (key, value)) + + def orEmpty(opt: Option[Map]) = opt getOrElse emptyMap + + def transform[S](task: Task[S], f: (State, S) => State): Task[S] = + { + val g = (s: S, map: AttributeMap) => map.put(Keys.transformState, (state: State) => f(state, s)) + task.copy(info = task.info.postTransform(g)) + } + + def resolveContext[T](key: ScopedKey[Task[T]], context: Scope, state: State): ScopedKey[Task[T]] = + { + val subScope = Scope.replaceThis(context)(key.scope) + val scope = Project.structure(state).data.definingScope(subScope, key.key) getOrElse subScope + ScopedKey(scope, key.key) + } + + def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] = + Project.structure(state).streams(state).use(key) { s => + try { Some(Operations.read(s.readBinary(key, DefaultDataID))) } + catch { case e: Exception => None } + } + + def load[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] = + get(key, state) orElse read(key, state)(f) + + def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)(implicit f: Format[T]): (State, Option[T]) = + get(key, state) match { + case s: Some[T] => (state, s) + case None => read(key, state)(f) match { + case s @ Some(t) => + val newState = if(setIfUnset && get(key, state).isDefined) state else set(key, state, t) + (newState, s) + case None => (state, None) + } + } +} diff --git a/main/SettingCompletions.scala b/main/SettingCompletions.scala index 53c0da4f1..53d96081f 100644 --- a/main/SettingCompletions.scala +++ b/main/SettingCompletions.scala @@ -3,8 +3,8 @@ package sbt import java.io.File import java.net.URI import Project._ + import Def.{ScopedKey, Setting} import Scope.{GlobalScope,ThisScope} - import Load.BuildStructure import Types.{const, idFun, Id} import complete._ import DefaultParsers._ @@ -31,7 +31,7 @@ private[sbt] object SettingCompletions { val akey = setting.key.key val global = ScopedKey(Global, akey) - val globalSetting = resolve( Project.setting(global, setting.init, setting.pos) ) + val globalSetting = resolve( Def.setting(global, setting.init, setting.pos) ) globalSetting ++ allDefs.flatMap { d => if(d.key == akey) Seq( SettingKey(akey) in d.scope <<= global) @@ -45,7 +45,7 @@ private[sbt] object SettingCompletions } /** Implementation of the `set` command that will reload the current project with `settings` appended to the current settings. */ - def setThis(s: State, extracted: Extracted, settings: Seq[Project.Setting[_]], arg: String): SetResult = + def setThis(s: State, extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String): SetResult = { import extracted._ val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) diff --git a/main/SettingGraph.scala b/main/SettingGraph.scala index 8b0e7fbc9..2fffb4e6b 100644 --- a/main/SettingGraph.scala +++ b/main/SettingGraph.scala @@ -5,8 +5,7 @@ package sbt import java.net.URI import java.io.File - import Project.{ScopedKey, flattenLocals, compiled} - import Load.BuildStructure + import Def.{compiled,flattenLocals,ScopedKey} import Predef.{any2stringadd => _, _} object SettingGraph diff --git a/main/TaskData.scala b/main/TaskData.scala deleted file mode 100644 index 999af6f7c..000000000 --- a/main/TaskData.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - - import Load.BuildStructure - import Project.{Initialize, ScopedKey} - import Keys.{resolvedScoped, streams, TaskStreams} - import std.TaskExtra._ - import Types.{:+:, idFun} - - import sbinary.{Format, Operations} - -@deprecated("Superseded by task state system.", "0.11.1") -object TaskData -{ - val DefaultDataID = "data" - - def apply[I,O](readFrom: Scoped, id: String = DefaultDataID)(f: (State, I) => O)(default: => I)(implicit fmt: Format[I]): Initialize[State => O] = - resolvedScoped { resolved => - s => f(s, readData(Project structure s, resolved, readFrom.key, id) getOrElse default) - } - - def readData[T](structure: BuildStructure, reader: ScopedKey[_], readFrom: AttributeKey[_], id: String)(implicit f: Format[T]): Option[T] = - try { - dataStreams(structure, reader, readFrom) { (ts,key) => - Operations.read( ts.readBinary(key, id) )(f) - } - } catch { case e: Exception => None } - - def dataStreams[T](structure: BuildStructure, reader: ScopedKey[_], readFrom: AttributeKey[_])(f: (TaskStreams, ScopedKey[_]) => T): Option[T] = - structure.data.definingScope(reader.scope, readFrom) map { defined => - val key = ScopedKey(Scope.fillTaskAxis(defined, readFrom), readFrom) - structure.streams(fakeState(structure)).use(reader)(ts => f(ts, key)) - } - def write[T](i: Initialize[Task[T]], id: String = DefaultDataID)(implicit f: Format[T]): Initialize[Task[T]] = writeRelated(i, id)(idFun[T])(f) - - def writeRelated[T, S](i: Initialize[Task[T]], id: String = DefaultDataID)(convert: T => S)(implicit f: Format[S]): Initialize[Task[T]] = - (streams zipWith i) { (sTask, iTask) => - (sTask,iTask) map { case s :+: value :+: HNil => - Operations.write( s.binary(id), convert(value) )(f) - value - } - } - // exists to keep the method signatures the same (since this object is potentially used but deprecated), - // but allow the BuildStructure Streams to be constructed from State, which isn't actually needed here - private[this] def fakeState(structure: BuildStructure): State = - { - val config = Keys.appConfiguration in Scope.GlobalScope get structure.data - State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, StandardMain.initialGlobalLogging, State.Continue) - } -} diff --git a/main/Append.scala b/main/settings/Append.scala similarity index 98% rename from main/Append.scala rename to main/settings/Append.scala index 1e42b5ebf..92ccb9404 100644 --- a/main/Append.scala +++ b/main/settings/Append.scala @@ -1,11 +1,11 @@ package sbt import java.io.File - import Keys.Classpath + import Def.Classpath import scala.annotation.implicitNotFound object Append -{ +{ @implicitNotFound(msg = "No implicit for Append.Value[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}") sealed trait Value[A,B] { diff --git a/main/settings/ConfigKey.scala b/main/settings/ConfigKey.scala new file mode 100644 index 000000000..1398c86c9 --- /dev/null +++ b/main/settings/ConfigKey.scala @@ -0,0 +1,7 @@ +package sbt + +final case class ConfigKey(name: String) +object ConfigKey +{ + implicit def configurationToKey(c: Configuration): ConfigKey = ConfigKey(c.name) +} diff --git a/main/settings/Def.scala b/main/settings/Def.scala new file mode 100644 index 000000000..cd4d5a50e --- /dev/null +++ b/main/settings/Def.scala @@ -0,0 +1,37 @@ +package sbt + + import java.io.File + +object Def extends Init[Scope] +{ + type Classpath = Seq[Attributed[File]] + + val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by") + val runBefore = AttributeKey[Seq[Task[_]]]("run-before") + private[sbt] val parseResult: TaskKey[Any] = TaskKey("$parse-result", "Internal: used to implement input tasks.", KeyRanks.Invisible) + // TODO: move back to Keys + val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", KeyRanks.DSetting) + + lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None) + def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] = + new Show[ScopedKey[_]] { def apply(key: ScopedKey[_]) = displayFull(key, keyNameColor) } + + def showRelativeKey(current: ProjectRef, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = new Show[ScopedKey[_]] { + def apply(key: ScopedKey[_]) = + Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayRelative(current, multi, ref)) + } + def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match { + case BuildRef(current.build) => "{.}/" + case `current` => if(multi) current.project + "/" else "" + case ProjectRef(current.build, x) => x + "/" + case _ => Reference.display(project) + "/" + } + def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None) + def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor)) + def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask) + + def colored(s: String, color: Option[String]): String = color match { + case Some(c) => c + s + scala.Console.RESET + case None => s + } +} \ No newline at end of file diff --git a/main/settings/DelegateIndex.scala b/main/settings/DelegateIndex.scala new file mode 100644 index 000000000..422146c46 --- /dev/null +++ b/main/settings/DelegateIndex.scala @@ -0,0 +1,19 @@ +package sbt + +sealed trait DelegateIndex +{ + def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] + def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] +// def task(ref: ProjectRef, task: ScopedKey[_]): Seq[ScopeAxis[ScopedKey[_]]] +// def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]] +} +private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex +{ + def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { case Some(pd) => pd.refs; case None => Nil } + def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] = + refs.get(ref) match { + case Some(pd) => pd.confs.get(conf) match { case Some(cs) => cs; case None => Select(conf) :: Global :: Nil } + case None => Select(conf) :: Global :: Nil + } +} +private final class ProjectDelegates(val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]]) \ No newline at end of file diff --git a/main/settings/KeyRanks.scala b/main/settings/KeyRanks.scala new file mode 100644 index 000000000..3daeb4975 --- /dev/null +++ b/main/settings/KeyRanks.scala @@ -0,0 +1,45 @@ +package sbt + +object KeyRanks +{ + // task and setting ranks, used to prioritize displaying information + // main tasks + final val APlusTask = 4 + final val ATask = 5 + final val AMinusTask = 6 + + // main settings + final val APlusSetting = 9 + final val ASetting = 10 + final val AMinusSetting = 11 + + // less major tasks or tasks that print useful information + final val BPlusTask = 29 + final val BTask = 30 + final val BMinusTask = 31 + + // secondary settings + final val BPlusSetting = 39 + final val BSetting = 40 + final val BMinusSetting = 41 + + // advanced settings + final val CSetting = 100 + // advanced tasks + final val CTask = 200 + // explicit settings + final val DSetting = 10000 + // explicit tasks + final val DTask = 20000 + + final val MainTaskCutoff = AMinusTask + final val MainSettingCutoff = AMinusSetting + final val MainCutoff = math.max(AMinusTask, AMinusSetting) + + final val DefaultTaskRank = (ATask + BTask)/2 + final val DefaultInputRank = ATask // input tasks are likely a main task + final val DefaultSettingRank = (ASetting + BSetting) / 2 + + // implementation details + val Invisible = Int.MaxValue +} diff --git a/main/Reference.scala b/main/settings/Reference.scala similarity index 84% rename from main/Reference.scala rename to main/settings/Reference.scala index c05a173a9..21d1b195e 100644 --- a/main/Reference.scala +++ b/main/settings/Reference.scala @@ -64,6 +64,29 @@ object Reference } } + def display(ref: Reference): String = + ref match + { + case pr: ProjectReference => display(pr) + case br: BuildReference => display(br) + } + + def display(ref: BuildReference): String = + ref match + { + case ThisBuild => "{}" + case BuildRef(uri) => "{" + uri + "}" + } + def display(ref: ProjectReference): String = + ref match + { + case ThisProject => "{}" + case LocalRootProject => "{}" + case LocalProject(id) => "{}" + id + case RootProject(uri) => "{" + uri + " }" + case ProjectRef(uri, id) => "{" + uri + "}" + id + } + def buildURI(ref: ResolvedReference): URI = ref match { case BuildRef(b) => b case ProjectRef(b, _) => b @@ -78,5 +101,4 @@ object Reference implicit def uriToRef(u: URI): ProjectReference = RootProject(u) implicit def fileToRef(f: File): ProjectReference = RootProject(f) implicit def stringToReference(s: String): ProjectReference = LocalProject(s) - implicit def projectToRef(p: Project): ProjectReference = LocalProject(p.id) } \ No newline at end of file diff --git a/main/Scope.scala b/main/settings/Scope.scala similarity index 80% rename from main/Scope.scala rename to main/settings/Scope.scala index 73022d5c3..2d8741a59 100644 --- a/main/Scope.scala +++ b/main/settings/Scope.scala @@ -5,7 +5,6 @@ package sbt import java.io.File import java.net.URI - import Types.some final case class Scope(project: ScopeAxis[Reference], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap]) { @@ -121,7 +120,7 @@ object Scope (!mask.extra || a.extra == b.extra) def projectPrefix(project: ScopeAxis[Reference], show: Reference => String = showProject): String = project.foldStrict(show, "*/", "./") - def showProject = (ref: Reference) => Project.display(ref) + "/" + def showProject = (ref: Reference) => Reference.display(ref) + "/" def parseScopedKey(command: String): (Scope, String) = { @@ -231,68 +230,3 @@ object Scope else for( c <- withGlobalAxis(scope.config); t <- withGlobalAxis(scope.task); e <- withGlobalAxis(scope.extra) ) yield Scope(Global, c, t, e) } - - -sealed trait ScopeAxis[+S] { - def foldStrict[T](f: S => T, ifGlobal: T, ifThis: T): T = fold(f, ifGlobal, ifThis) - def fold[T](f: S => T, ifGlobal: => T, ifThis: => T): T = this match { - case This => ifThis - case Global => ifGlobal - case Select(s) => f(s) - } - def toOption: Option[S] = foldStrict(some.fn, None, None) - def map[T](f: S => T): ScopeAxis[T] = foldStrict(s => Select(f(s)), Global, This) - def isSelect: Boolean = false -} -case object This extends ScopeAxis[Nothing] -case object Global extends ScopeAxis[Nothing] -final case class Select[S](s: S) extends ScopeAxis[S] { - override def isSelect = true -} -object ScopeAxis -{ - implicit def scopeAxisToScope(axis: ScopeAxis[Nothing]): Scope = - Scope(axis, axis, axis, axis) - def fromOption[T](o: Option[T]): ScopeAxis[T] = o match { - case Some(v) => Select(v) - case None => Global - } -} -/** Specifies the Scope axes that should be used for an operation. `true` indicates an axis should be used. */ -final case class ScopeMask(project: Boolean = true, config: Boolean = true, task: Boolean = true, extra: Boolean = true) -{ - def concatShow(p: String, c: String, t: String, sep: String, x: String): String = - { - val sb = new StringBuilder - if(project) sb.append(p) - if(config) sb.append(c) - if(task) sb.append(t) - sb.append(sep) - if(extra) sb.append(x) - sb.toString - } -} - -final case class ConfigKey(name: String) -object ConfigKey -{ - implicit def configurationToKey(c: Configuration): ConfigKey = ConfigKey(c.name) -} - -sealed trait DelegateIndex -{ - def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] - def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] -// def task(ref: ProjectRef, task: ScopedKey[_]): Seq[ScopeAxis[ScopedKey[_]]] -// def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]] -} -private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex -{ - def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { case Some(pd) => pd.refs; case None => Nil } - def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] = - refs.get(ref) match { - case Some(pd) => pd.confs.get(conf) match { case Some(cs) => cs; case None => Select(conf) :: Global :: Nil } - case None => Select(conf) :: Global :: Nil - } -} -private final class ProjectDelegates(val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]]) \ No newline at end of file diff --git a/main/settings/ScopeAxis.scala b/main/settings/ScopeAxis.scala new file mode 100644 index 000000000..3706f02f7 --- /dev/null +++ b/main/settings/ScopeAxis.scala @@ -0,0 +1,29 @@ +package sbt + + import Types.some + +sealed trait ScopeAxis[+S] { + def foldStrict[T](f: S => T, ifGlobal: T, ifThis: T): T = fold(f, ifGlobal, ifThis) + def fold[T](f: S => T, ifGlobal: => T, ifThis: => T): T = this match { + case This => ifThis + case Global => ifGlobal + case Select(s) => f(s) + } + def toOption: Option[S] = foldStrict(some.fn, None, None) + def map[T](f: S => T): ScopeAxis[T] = foldStrict(s => Select(f(s)), Global, This) + def isSelect: Boolean = false +} +case object This extends ScopeAxis[Nothing] +case object Global extends ScopeAxis[Nothing] +final case class Select[S](s: S) extends ScopeAxis[S] { + override def isSelect = true +} +object ScopeAxis +{ + implicit def scopeAxisToScope(axis: ScopeAxis[Nothing]): Scope = + Scope(axis, axis, axis, axis) + def fromOption[T](o: Option[T]): ScopeAxis[T] = o match { + case Some(v) => Select(v) + case None => Global + } +} diff --git a/main/settings/ScopeMask.scala b/main/settings/ScopeMask.scala new file mode 100644 index 000000000..6b75513ec --- /dev/null +++ b/main/settings/ScopeMask.scala @@ -0,0 +1,16 @@ +package sbt + +/** Specifies the Scope axes that should be used for an operation. `true` indicates an axis should be used. */ +final case class ScopeMask(project: Boolean = true, config: Boolean = true, task: Boolean = true, extra: Boolean = true) +{ + def concatShow(p: String, c: String, t: String, sep: String, x: String): String = + { + val sb = new StringBuilder + if(project) sb.append(p) + if(config) sb.append(c) + if(task) sb.append(t) + sb.append(sep) + if(extra) sb.append(x) + sb.toString + } +} diff --git a/main/Structure.scala b/main/settings/Structure.scala similarity index 93% rename from main/Structure.scala rename to main/settings/Structure.scala index 349dbd311..8734ba47f 100644 --- a/main/Structure.scala +++ b/main/settings/Structure.scala @@ -5,14 +5,16 @@ package sbt /** An abstraction on top of Settings for build configuration and task definition. */ - import Types._ - import std.TaskExtra.{task => mktask, _} - import Task._ - import Project.{Initialize, KeyedInitialize, ScopedKey, Setting, setting} - import complete.Parser import java.io.File import java.net.URI + + import complete.Parser + import ConcurrentRestrictions.Tag + import Def.{Initialize, KeyedInitialize, ScopedKey, Setting, setting} import Path._ + import std.TaskExtra.{task => mktask, _} + import Task._ + import Types._ /** Parses input and produces a task to run. Constructed using the companion object. */ sealed trait InputTask[T] { @@ -43,7 +45,7 @@ object InputTask def free[I,T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c) def separate[I,T](p: State => Parser[I])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = - separate(Project value p)(action) + separate(Def value p)(action) def separate[I,T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = p.zipWith(action)((parser, act) => free(parser)(act)) @@ -56,8 +58,8 @@ object InputTask // However, this results in a minimal interface to the full capabilities of an InputTask for users def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = { - val key: TaskKey[I] = Keys.parseResult.asInstanceOf[TaskKey[I]] - (p zip Keys.resolvedScoped zipWith action(key)) { case ((parserF, scoped), act) => + val key: TaskKey[I] = Def.parseResult.asInstanceOf[TaskKey[I]] + (p zip Def.resolvedScoped zipWith action(key)) { case ((parserF, scoped), act) => new InputDynamic[T] { type Result = I @@ -68,7 +70,7 @@ object InputTask } } def apply[I,T](p: State => Parser[I])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = - apply(Project.value(p))(action) + apply(Def.value(p))(action) } sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] } @@ -153,13 +155,13 @@ object Scoped def scopedKey: ScopedKey[S] private[sbt] final def :==(value: S): Setting[S] = :=(value) - final def := (value: => S): Setting[S] = setting(scopedKey, Project.value(value)) - final def ~= (f: S => S): Setting[S] = Project.update(scopedKey)(f) + final def := (value: => S): Setting[S] = setting(scopedKey, Def.value(value)) + final def ~= (f: S => S): Setting[S] = Def.update(scopedKey)(f) final def <<= (app: Initialize[S]): Setting[S] = setting(scopedKey, app) final def get(settings: Settings[Scope]): Option[S] = settings.get(scopedKey.scope, scopedKey.key) - final def ? : Initialize[Option[S]] = Project.optional(scopedKey)(idFun) + final def ? : Initialize[Option[S]] = Def.optional(scopedKey)(idFun) final def or[T >: S](i: Initialize[T]): Initialize[T] = (this.?, i)(_ getOrElse _ ) - final def ??[T >: S](or: => T): Initialize[T] = Project.optional(scopedKey)(_ getOrElse or ) + final def ??[T >: S](or: => T): Initialize[T] = Def.optional(scopedKey)(_ getOrElse or ) } final class RichInitialize[S](init: Initialize[S]) { @@ -172,12 +174,12 @@ object Scoped { self: TaskKey[S] => private[sbt] def :==(value: S): Setting[Task[S]] = :=(value) - private[sbt] def ::=(value: Task[S]): Setting[Task[S]] = Project.setting(scopedKey, Project.value( value )) + private[sbt] def ::=(value: Task[S]): Setting[Task[S]] = Def.setting(scopedKey, Def.value( value )) def := (value: => S): Setting[Task[S]] = ::=(mktask(value)) private[sbt] def :== (v: SettingKey[S]): Setting[Task[S]] = <<=( v(constant)) - def ~= (f: S => S): Setting[Task[S]] = Project.update(scopedKey)( _ map f ) + def ~= (f: S => S): Setting[Task[S]] = Def.update(scopedKey)( _ map f ) - def <<= (app: Initialize[Task[S]]): Setting[Task[S]] = Project.setting(scopedKey, app) + def <<= (app: Initialize[Task[S]]): Setting[Task[S]] = Def.setting(scopedKey, app) def task: SettingKey[Task[S]] = scopedSetting(scope, key) def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key) @@ -185,8 +187,8 @@ object Scoped @deprecated("A call to 'identity' is no longer necessary and can be removed.", "0.11.0") def identity: Initialize[Task[S]] = this - def ? : Initialize[Task[Option[S]]] = Project.optional(scopedKey) { case None => mktask { None }; case Some(t) => t map some.fn } - def ??[T >: S](or: => T): Initialize[Task[T]] = Project.optional(scopedKey)( _ getOrElse mktask(or) ) + def ? : Initialize[Task[Option[S]]] = Def.optional(scopedKey) { case None => mktask { None }; case Some(t) => t map some.fn } + def ??[T >: S](or: => T): Initialize[Task[T]] = Def.optional(scopedKey)( _ getOrElse mktask(or) ) def or[T >: S](i: Initialize[Task[T]]): Initialize[Task[T]] = (this.? zipWith i)( (x,y) => (x :^: y :^: KNil) map hf2( _ getOrElse _ )) } final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] @@ -195,17 +197,8 @@ object Scoped def dependsOn(tasks: AnyInitTask*): Initialize[Task[S]] = (i, Initialize.joinAny(tasks)) { (thisTask, deps) => thisTask.dependsOn(deps : _*) } - import SessionVar.{persistAndSet, resolveContext, set, transform} - - def updateState(f: (State, S) => State): Initialize[Task[S]] = onTask(t => transform(t, f)) - def storeAs(key: TaskKey[S])(implicit f: sbinary.Format[S]): Initialize[Task[S]] = (Keys.resolvedScoped, i) { (scoped, task) => - transform(task, (state, value) => persistAndSet( resolveContext(key, scoped.scope, state), state, value)(f)) - } - def keepAs(key: TaskKey[S]): Initialize[Task[S]] = - (i, Keys.resolvedScoped)( (t,scoped) => transform(t, (state,value) => set(resolveContext(key, scoped.scope, state), state, value) ) ) - - def triggeredBy(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Keys.triggeredBy) - def runBefore(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Keys.runBefore) + def triggeredBy(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Def.triggeredBy) + def runBefore(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Def.runBefore) private[this] def nonLocal(tasks: Seq[AnyInitTask], key: AttributeKey[Seq[Task[_]]]): Initialize[Task[S]] = (Initialize.joinAny(tasks), i) { (ts, i) => i.copy(info = i.info.set(key, ts)) } } @@ -231,8 +224,8 @@ object Scoped def || [T >: S](alt: Task[T]): Initialize[R[T]] = onTask(_ || alt) def && [T](alt: Task[T]): Initialize[R[T]] = onTask(_ && alt) - def tag(tags: Tags.Tag*): Initialize[R[S]] = onTask(_.tag(tags: _*)) - def tagw(tags: (Tags.Tag, Int)*): Initialize[R[S]] = onTask(_.tagw(tags : _*)) + def tag(tags: Tag*): Initialize[R[S]] = onTask(_.tag(tags: _*)) + def tagw(tags: (Tag, Int)*): Initialize[R[S]] = onTask(_.tagw(tags : _*)) } type AnyInitTask = Initialize[Task[T]] forSome { type T } @@ -308,7 +301,7 @@ object Scoped } def combine[D[_],S](c: Combine[D], f: Results[HLv] => D[S]): Initialize[Task[S]] = - Project.app(settings)(hls => c(tasks(hls))(hlt => f(expand(hls, hlt))) ) + Def.app(settings)(hls => c(tasks(hls))(hlt => f(expand(hls, hlt))) ) } type RedHL[HL <: HList] = Reduced[_,_,HL] def reduced[HL <: HList](settings: KList[ScopedTaskable, HL]): Reduced[_,_,HL] = @@ -519,59 +512,59 @@ object Scoped def mkTuple15[A,B,C,D,E,F,G,H,I,J,K,L,N,O,P] = (a:A,b:B,c:C,d:D,e:E,f:F,g:G,h:H,i:I,j:J,k:K,l:L,n:N,o:O,p:P) => (a,b,c,d,e,f,g,h,i,j,k,l,n,o,p) final class Apply2[A,B](t2: (Initialize[A], Initialize[B])) { - def apply[T](z: (A,B) => T) = Project.app( k2(t2) )( hf2(z) ) + def apply[T](z: (A,B) => T) = Def.app( k2(t2) )( hf2(z) ) def identity = apply(mkTuple2) } final class Apply3[A,B,C](t3: (Initialize[A], Initialize[B], Initialize[C])) { - def apply[T](z: (A,B,C) => T) = Project.app( k3(t3) )( hf3(z) ) + def apply[T](z: (A,B,C) => T) = Def.app( k3(t3) )( hf3(z) ) def identity = apply(mkTuple3) } final class Apply4[A,B,C,D](t4: (Initialize[A], Initialize[B], Initialize[C], Initialize[D])) { - def apply[T](z: (A,B,C,D) => T) = Project.app( k4(t4) )( hf4(z) ) + def apply[T](z: (A,B,C,D) => T) = Def.app( k4(t4) )( hf4(z) ) def identity = apply(mkTuple4) } final class Apply5[A,B,C,D,E](t5: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E])) { - def apply[T](z: (A,B,C,D,E) => T) = Project.app( k5(t5) )( hf5(z) ) + def apply[T](z: (A,B,C,D,E) => T) = Def.app( k5(t5) )( hf5(z) ) def identity = apply(mkTuple5) } final class Apply6[A,B,C,D,E,F](t6: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F])) { - def apply[T](z: (A,B,C,D,E,F) => T) = Project.app( k6(t6) )( hf6(z) ) + def apply[T](z: (A,B,C,D,E,F) => T) = Def.app( k6(t6) )( hf6(z) ) def identity = apply(mkTuple6) } final class Apply7[A,B,C,D,E,F,G](t7: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G])) { - def apply[T](z: (A,B,C,D,E,F,G) => T) = Project.app( k7(t7) )( hf7(z) ) + def apply[T](z: (A,B,C,D,E,F,G) => T) = Def.app( k7(t7) )( hf7(z) ) def identity = apply(mkTuple7) } final class Apply8[A,B,C,D,E,F,G,H](t8: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H])) { - def apply[T](z: (A,B,C,D,E,F,G,H) => T) = Project.app( k8(t8) )( hf8(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H) => T) = Def.app( k8(t8) )( hf8(z) ) def identity = apply(mkTuple8) } final class Apply9[A,B,C,D,E,F,G,H,I](t9: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I) => T) = Project.app( k9(t9) )( hf9(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I) => T) = Def.app( k9(t9) )( hf9(z) ) def identity = apply(mkTuple9) } final class Apply10[A,B,C,D,E,F,G,H,I,J](t10: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J) => T) = Project.app( k10(t10) )( hf10(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J) => T) = Def.app( k10(t10) )( hf10(z) ) def identity = apply(mkTuple10) } final class Apply11[A,B,C,D,E,F,G,H,I,J,K](t11: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K) => T) = Project.app( k11(t11) )( hf11(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K) => T) = Def.app( k11(t11) )( hf11(z) ) def identity = apply(mkTuple11) } final class Apply12[A,B,C,D,E,F,G,H,I,J,K,L](t12: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K], Initialize[L])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L) => T) = Project.app( k12(t12) )( hf12(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L) => T) = Def.app( k12(t12) )( hf12(z) ) def identity = apply(mkTuple12) } final class Apply13[A,B,C,D,E,F,G,H,I,J,K,L,N](t13: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K], Initialize[L], Initialize[N])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N) => T) = Project.app( k13(t13) )( hf13(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N) => T) = Def.app( k13(t13) )( hf13(z) ) def identity = apply(mkTuple13) } final class Apply14[A,B,C,D,E,F,G,H,I,J,K,L,N,O](t14: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K], Initialize[L], Initialize[N], Initialize[O])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N,O) => T) = Project.app( k14(t14) )( hf14(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N,O) => T) = Def.app( k14(t14) )( hf14(z) ) def identity = apply(mkTuple14) } final class Apply15[A,B,C,D,E,F,G,H,I,J,K,L,N,O,P](t15: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K], Initialize[L], Initialize[N], Initialize[O], Initialize[P])) { - def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N,O,P) => T) = Project.app( k15(t15) )( hf15(z) ) + def apply[T](z: (A,B,C,D,E,F,G,H,I,J,K,L,N,O,P) => T) = Def.app( k15(t15) )( hf15(z) ) def identity = apply(mkTuple15) } diff --git a/project/Sbt.scala b/project/Sbt.scala index a4ab48f0b..c9fd2ef7e 100644 --- a/project/Sbt.scala +++ b/project/Sbt.scala @@ -120,10 +120,14 @@ object Sbt extends Build classpathSub, completeSub, apiSub, compilerIntegrationSub, compilerIvySub, interfaceSub, ioSub, ivySub, logSub, processSub, runSub, relationSub, stdTaskSub, taskSub, trackingSub, testingSub) + // General command support and core commands not specific to a build system lazy val commandSub = testedBaseProject(commandPath, "Command") dependsOn(interfaceSub, ioSub, launchInterfaceSub, logSub, completeSub, classpathSub) + // Fixes scope=Scope for Setting (core defined in collectionSub) to define the settings system used in build definitions + lazy val settingsSub = testedBaseProject(settingsPath, "Settings") dependsOn(interfaceSub, ivySub, relationSub, logSub, ioSub, commandSub, completeSub, + classpathSub, stdTaskSub, processSub) settings( sbinary ) // The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions. - lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub, commandSub) + lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, settingsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub, commandSub) // Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object // technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project @@ -139,6 +143,7 @@ object Sbt extends Build def compilePath = file("compile") def mainPath = file("main") def commandPath = mainPath / "command" + def settingsPath = mainPath / "settings" def scriptedPath = file("scripted") def sbtSettings = Seq( diff --git a/util/collection/Attributes.scala b/util/collection/Attributes.scala index f5ea9dcd4..0376ca76c 100644 --- a/util/collection/Attributes.scala +++ b/util/collection/Attributes.scala @@ -125,6 +125,7 @@ final case class Attributed[D](data: D)(val metadata: AttributeMap) } object Attributed { + def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data) def blankSeq[T](in: Seq[T]): Seq[Attributed[T]] = in map blank def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty) } \ No newline at end of file