mirror of https://github.com/sbt/sbt.git
API for evaluating a setting or task in multiple scopes
This commit is contained in:
parent
2f13b7a8c7
commit
df5e79e3be
|
|
@ -0,0 +1,186 @@
|
|||
package sbt
|
||||
|
||||
import Types.{const, idFun}
|
||||
import Def.Initialize
|
||||
import java.net.URI
|
||||
import ScopeFilter.Data
|
||||
|
||||
object ScopeFilter
|
||||
{
|
||||
type ScopeFilter = Base[Scope]
|
||||
type AxisFilter[T] = Base[ScopeAxis[T]]
|
||||
type ProjectFilter = AxisFilter[Reference]
|
||||
type ConfigurationFilter = AxisFilter[ConfigKey]
|
||||
type TaskFilter = AxisFilter[AttributeKey[_]]
|
||||
|
||||
/** Constructs a Scope filter from filters for the individual axes.
|
||||
* If a project filter is not supplied, the enclosing project is selected.
|
||||
* If a configuration filter is not supplied, global is selected.
|
||||
* If a task filter is not supplied, global is selected.
|
||||
* Generally, always specify the project axis.*/
|
||||
def apply(projects: ProjectFilter = inProjects(ThisProject), configurations: ConfigurationFilter = globalAxis, tasks: TaskFilter = globalAxis): ScopeFilter =
|
||||
new ScopeFilter {
|
||||
private[sbt] def apply(data: Data): Scope => Boolean =
|
||||
{
|
||||
val pf = projects(data)
|
||||
val cf = configurations(data)
|
||||
val tf = tasks(data)
|
||||
s => pf(s.project) && cf(s.config) && tf(s.task)
|
||||
}
|
||||
}
|
||||
|
||||
final class SettingKeyAll[T] private[sbt](i: Initialize[T]) {
|
||||
/** Evaluates the initialization in all scopes selected by the filter. These are dynamic dependencies, so
|
||||
* static inspections will not show them. */
|
||||
def all(sfilter: => ScopeFilter): Initialize[Seq[T]] = Def.bind(getData) { data =>
|
||||
data.allScopes.toSeq.filter(sfilter(data)).map(s => Project.inScope(s, i)).join
|
||||
}
|
||||
}
|
||||
final class TaskKeyAll[T] private[sbt](i: Initialize[Task[T]]) {
|
||||
/** Evaluates the task in all scopes selected by the filter. These are dynamic dependencies, so
|
||||
* static inspections will not show them. */
|
||||
def all(sfilter: => ScopeFilter): Initialize[Task[Seq[T]]] = Def.bind(getData) { data =>
|
||||
import std.TaskExtra._
|
||||
data.allScopes.toSeq.filter(sfilter(data)).map(s => Project.inScope(s, i)).join(_.join)
|
||||
}
|
||||
}
|
||||
|
||||
trait Make
|
||||
{
|
||||
/** Selects Scopes with a global task axis. */
|
||||
def inGlobalTask: TaskFilter = globalAxis[AttributeKey[_]]
|
||||
/** Selects Scopes with a global project axis. */
|
||||
def inGlobalProject: ProjectFilter = globalAxis[Reference]
|
||||
/** Selects Scopes with a global configuration axis. */
|
||||
def inGlobalConfiguration: ConfigurationFilter = globalAxis[ConfigKey]
|
||||
|
||||
/** Selects all scopes that apply to a single project. Global and build-level scopes are excluded. */
|
||||
def inAnyProject: ProjectFilter = selectAxis(const { case p: ProjectRef => true; case _ => false })
|
||||
/** Accepts all values for the task axis except Global. */
|
||||
def inAnyTask: TaskFilter = selectAny[AttributeKey[_]]
|
||||
/** Accepts all values for the configuration axis except Global. */
|
||||
def inAnyConfiguration: ConfigurationFilter = selectAny[ConfigKey]
|
||||
|
||||
/** Selects Scopes that have a project axis that is aggregated by `ref`, transitively if `transitive` is true.
|
||||
* If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected. */
|
||||
def inAggregates(ref: ProjectReference, transitive: Boolean=true, includeRoot: Boolean=true): ProjectFilter =
|
||||
byDeps(ref, transitive=transitive, includeRoot=includeRoot, aggregate=true, classpath=false)
|
||||
|
||||
/** Selects Scopes that have a project axis that is a dependency of `ref`, transitively if `transitive` is true.
|
||||
* If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected. */
|
||||
def inDependencies(ref: ProjectReference, transitive: Boolean=true, includeRoot: Boolean=true): ProjectFilter =
|
||||
byDeps(ref, transitive=transitive, includeRoot=includeRoot, aggregate=false, classpath=true)
|
||||
|
||||
/** Selects Scopes that have a project axis with one of the provided values.*/
|
||||
def inProjects(projects: ProjectReference*): ProjectFilter = ScopeFilter.inProjects(projects : _*)
|
||||
|
||||
/** Selects Scopes that have a task axis with one of the provided values.*/
|
||||
def inTasks(tasks: Scoped*): TaskFilter = {
|
||||
val ts = tasks.map(_.key).toSet
|
||||
selectAxis[AttributeKey[_]](const(ts))
|
||||
}
|
||||
|
||||
/** Selects Scopes that have a task axis with one of the provided values.*/
|
||||
def inConfigurations(configs: Configuration*): ConfigurationFilter = {
|
||||
val cs = configs.map(_.name).toSet
|
||||
selectAxis[ConfigKey](const(c => cs(c.name)))
|
||||
}
|
||||
|
||||
implicit def settingKeyAll[T](key: Initialize[T]): SettingKeyAll[T] = new SettingKeyAll[T](key)
|
||||
implicit def taskKeyAll[T](key: Initialize[Task[T]]): TaskKeyAll[T] = new TaskKeyAll[T](key)
|
||||
}
|
||||
|
||||
/** Information provided to Scope filters. These provide project relationships,
|
||||
* project reference resolution, and the list of all static Scopes.*/
|
||||
private final class Data(val units: Map[URI, LoadedBuildUnit], val resolve: ProjectReference => ProjectRef, val allScopes: Set[Scope])
|
||||
|
||||
/** Constructs a Data instance from the list of static scopes and the project relationships.*/
|
||||
private[this] val getData: Initialize[Data] =
|
||||
Def.setting {
|
||||
val build = Keys.loadedBuild.value
|
||||
val scopes = Def.StaticScopes.value
|
||||
val current = Keys.thisProjectRef.?.value match {
|
||||
case Some(ProjectRef(uri, _)) => uri
|
||||
case None => build.root
|
||||
}
|
||||
val rootProject = Load.getRootProject(build.units)
|
||||
val resolve: ProjectReference => ProjectRef = p => Scope.resolveProjectRef(current, rootProject, p)
|
||||
new Data(build.units, resolve, scopes)
|
||||
}
|
||||
|
||||
private[this] def getDependencies(structure: Map[URI,LoadedBuildUnit], classpath: Boolean, aggregate: Boolean): ProjectRef => Seq[ProjectRef] =
|
||||
ref => Project.getProject(ref, structure).toList flatMap { p =>
|
||||
(if(classpath) p.dependencies.map(_.project) else Nil) ++
|
||||
(if(aggregate) p.aggregate else Nil)
|
||||
}
|
||||
|
||||
private[this] def byDeps(ref: ProjectReference, transitive: Boolean, includeRoot: Boolean, aggregate: Boolean, classpath: Boolean): ProjectFilter =
|
||||
inResolvedProjects { data =>
|
||||
val resolvedRef = data.resolve(ref)
|
||||
val direct = getDependencies(data.units, classpath=classpath, aggregate=aggregate)
|
||||
if(transitive) {
|
||||
val full = Dag.topologicalSort(resolvedRef)(direct)
|
||||
if(includeRoot) full else full dropRight 1
|
||||
} else {
|
||||
val directDeps = direct(resolvedRef)
|
||||
if(includeRoot) resolvedRef +: directDeps else directDeps
|
||||
}
|
||||
}
|
||||
|
||||
private def inProjects(projects: ProjectReference*): ProjectFilter =
|
||||
inResolvedProjects( data => projects.map(data.resolve) )
|
||||
|
||||
private[this] def inResolvedProjects(projects: Data => Seq[ProjectRef]): ProjectFilter =
|
||||
selectAxis(data => projects(data).toSet)
|
||||
|
||||
private[this] def globalAxis[T]: AxisFilter[T] = new AxisFilter[T] {
|
||||
private[sbt] def apply(data: Data): ScopeAxis[T] => Boolean =
|
||||
_ == Global
|
||||
}
|
||||
private[this] def selectAny[T]: AxisFilter[T] = selectAxis(const(const(true)))
|
||||
private[this] def selectAxis[T](f: Data => T => Boolean): AxisFilter[T] = new AxisFilter[T] {
|
||||
private[sbt] def apply(data: Data): ScopeAxis[T] => Boolean = {
|
||||
val g = f(data)
|
||||
s => s match {
|
||||
case Select(t) => g(t)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Base functionality for filters on values of type `In` that need access to build data.*/
|
||||
sealed abstract class Base[In]
|
||||
{ self =>
|
||||
/** Implements this filter. */
|
||||
private[sbt] def apply(data: Data): In => Boolean
|
||||
|
||||
/** Constructs a filter that selects values that match this filter but not `other`.*/
|
||||
def --(other: Base[In]): Base[In] = this && -other
|
||||
|
||||
/** Constructs a filter that selects values that match this filter and `other`.*/
|
||||
def &&(other: Base[In]): Base[In] = new Base[In] {
|
||||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
val b = other(data)
|
||||
s => a(s) && b(s)
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs a filter that selects values that match this filter or `other`.*/
|
||||
def ||(other: Base[In]): Base[In] = new Base[In] {
|
||||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
val b = other(data)
|
||||
s => a(s) || b(s)
|
||||
}
|
||||
}
|
||||
/** Constructs a filter that selects values that do not match this filter.*/
|
||||
def unary_- : Base[In] = new Base[In] {
|
||||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
s => !a(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders
|
||||
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra with sbt.TaskMacroExtra
|
||||
with sbt.ScopeFilter.Make
|
||||
{
|
||||
@deprecated("Renamed to CommandStrings.", "0.12.0")
|
||||
val CommandSupport = CommandStrings
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ abstract class EvaluateSettings[Scope]
|
|||
|
||||
private[this] val complete = new LinkedBlockingQueue[Option[Throwable]]
|
||||
private[this] val static = PMap.empty[ScopedKey, INode]
|
||||
private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet
|
||||
private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key)
|
||||
|
||||
private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) { def apply[T](i: Initialize[T]): INode[T] = i match {
|
||||
case k: Keyed[s, T] => single(getStatic(k.scopedKey), k.transform)
|
||||
case a: Apply[k,T] => new MixedNode[k,T]( a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist)
|
||||
case b: Bind[s,T] => new BindNode[s,T]( transform(b.in), x => transform(b.f(x)))
|
||||
case init.StaticScopes => constant(() => allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope]
|
||||
case v: Value[T] => constant(v.value)
|
||||
case t: TransformCapture => constant(() => t.f)
|
||||
case o: Optional[s,T] => o.a match {
|
||||
|
|
@ -33,7 +35,7 @@ abstract class EvaluateSettings[Scope]
|
|||
case Some(i) => single[s,T](transform(i), x => o.f(Some(x)))
|
||||
}
|
||||
}}
|
||||
private[this] val roots: Seq[INode[_]] = compiledSettings flatMap { cs =>
|
||||
private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs =>
|
||||
(cs.settings map { s =>
|
||||
val t = transform(s.init)
|
||||
static(s.key) = t
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ trait Init[Scope]
|
|||
def mapConstant(g: MapConstant) = this
|
||||
def evaluate(map: Settings[Scope]): T = value()
|
||||
}
|
||||
private[sbt] final val StaticScopes: Initialize[Set[Scope]] = new Value(() => error("internal sbt error: GetScopes not substituted"))
|
||||
private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T]
|
||||
{
|
||||
def dependencies = deps(alist.toList(inputs))
|
||||
|
|
|
|||
Loading…
Reference in New Issue