/* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ package sbt import java.io.File import java.net.URI final case class Scope(project: ScopeAxis[Reference], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap]) object Scope { val ThisScope = Scope(This, This, This, This) val GlobalScope = Scope(Global, Global, Global, Global) def resolveScope(thisScope: Scope, current: URI, rootProject: URI => String): Scope => Scope = resolveProject(current, rootProject) compose replaceThis(thisScope) def replaceThis(thisScope: Scope): Scope => Scope = (scope: Scope) => Scope(subThis(thisScope.project, scope.project), subThis(thisScope.config, scope.config), subThis(thisScope.task, scope.task), subThis(thisScope.extra, scope.extra)) def subThis[T](sub: ScopeAxis[T], into: ScopeAxis[T]): ScopeAxis[T] = if(into == This) sub else into def fillTaskAxis(scope: Scope, key: AttributeKey[_]): Scope = scope.task match { case _: Select[_] => scope case _ => scope.copy(task = Select(key)) } def resolveProject(uri: URI, rootProject: URI => String): Scope => Scope = { case Scope(Select(ref), a,b,c) => Scope(Select(resolveReference(uri, rootProject, ref)), a,b,c) case x => x } def resolveBuildOnly(current: URI, ref: Reference): Reference = ref match { case br: BuildReference => resolveBuild(current, br) case pr: ProjectReference => resolveProjectBuild(current, pr) } def resolveBuild(current: URI, ref: BuildReference): BuildReference = ref match { case ThisBuild => BuildRef(current) case BuildRef(uri) => BuildRef(resolveBuild(current, uri)) } def resolveProjectBuild(current: URI, ref: ProjectReference): ProjectReference = ref match { case ThisProject => RootProject(current) case LocalProject(id) => ProjectRef(current, id) case RootProject(uri) => RootProject(resolveBuild(current, uri)) case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id) } def resolveBuild(current: URI, uri: URI): URI = IO.directoryURI(current resolve uri) def resolveReference(current: URI, rootProject: URI => String, ref: Reference): ResolvedReference = ref match { case br: BuildReference => resolveBuildRef(current, br) case pr: ProjectReference => resolveProjectRef(current, rootProject, pr) } def resolveProjectRef(current: URI, rootProject: URI => String, ref: ProjectReference): ProjectRef = ref match { case ThisProject => ProjectRef(current, rootProject(current)) case LocalProject(id) => ProjectRef(current, id) case RootProject(uri) => val res = resolveBuild(current, uri); ProjectRef(res, rootProject(res)) case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id) } def resolveBuildRef(current: URI, ref: BuildReference): BuildRef = ref match { case ThisBuild => BuildRef(current) case BuildRef(uri) => BuildRef(resolveBuild(current, uri)) } def display(config: ConfigKey): String = if(config.name == "compile") "" else config.name + ":" def display(scope: Scope, sep: String): String = { import scope.{project, config, task, extra} val projectPrefix = project.foldStrict(Project.display, "*", ".") val configPrefix = config.foldStrict(display, "*:", ".:") val taskPostfix = task.foldStrict(x => ("for " + x.label) :: Nil, Nil, Nil) val extraPostfix = extra.foldStrict(_.entries.map( _.toString ).toList, Nil, Nil) val extras = taskPostfix ::: extraPostfix val postfix = if(extras.isEmpty) "" else extras.mkString("(", ", ", ")") projectPrefix + "/" + configPrefix + sep + postfix } def parseScopedKey(command: String): (Scope, String) = { val ScopedKeyRegex(_, projectID, _, config, key) = command val pref = if(projectID eq null) This else Select(LocalProject(projectID)) val conf = if(config eq null) This else Select(ConfigKey(config)) (Scope(pref, conf, This, This), transformTaskName(key)) } val ScopedKeyRegex = """((\w+)\/)?((\w+)\:)?([\w\-]+)""".r def transformTaskName(s: String) = { val parts = s.split("-+") (parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString } // *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method def delegates(projectInherit: Reference => Seq[Reference], configInherit: (Reference, ConfigKey) => Seq[ConfigKey], taskInherit: (Reference, AttributeKey[_]) => Seq[AttributeKey[_]], extraInherit: (Reference, AttributeMap) => Seq[AttributeMap])(rawScope: Scope): Seq[Scope] = { val scope = Scope.replaceThis(GlobalScope)(rawScope) scope.project match { case Global => scope :: GlobalScope :: Nil case This => scope.copy(project = Global) :: GlobalScope :: Nil case Select(proj) => val projI = withRawBuilds(linearize(scope.project, Nil)(projectInherit)).distinct val prod = for { px <- projI p = px.toOption getOrElse proj c <- linearize(scope.config)(configInherit(p, _)) t <- linearize(scope.task)(taskInherit(p,_)) e <- linearize(scope.extra)(extraInherit(p,_)) } yield Scope(px,c,t,e) (prod :+ GlobalScope).distinct } } def withRawBuilds(ps: Seq[ScopeAxis[Reference]]): Seq[ScopeAxis[Reference]] = (ps ++ (ps flatMap rawBuilds).map(Select.apply) :+ Global).distinct def rawBuilds(ps: ScopeAxis[Reference]): Seq[Reference] = ps match { case Select(ref) => rawBuilds(ref); case _ => Nil } def rawBuilds(ps: Reference): Seq[Reference] = (Reference.uri(ps) map BuildRef.apply).toList def linearize[T](axis: ScopeAxis[T], append: Seq[ScopeAxis[T]] = Global :: Nil)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] = axis match { case Select(x) => (Dag.topologicalSort(x)(inherit).map(Select.apply).reverse ++ append).distinct case Global | This => append } } 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.apply, None, None) def map[T](f: S => T): ScopeAxis[T] = foldStrict(s => Select(f(s)), Global, This) } case object This extends ScopeAxis[Nothing] case object Global extends ScopeAxis[Nothing] final case class Select[S](s: S) extends ScopeAxis[S] object ScopeAxis { implicit def scopeAxisToScope(axis: ScopeAxis[Nothing]): Scope = Scope(axis, axis, axis, axis) } final case class ConfigKey(name: String) object ConfigKey { implicit def configurationToKey(c: Configuration): ConfigKey = ConfigKey(c.name) }