Skip to content

Commit

Permalink
Merge branch 'develop' into wip/testquick
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n authored Sep 12, 2024
2 parents e9c0410 + 941ae3d commit a0597e9
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 68 deletions.
2 changes: 1 addition & 1 deletion main-command/src/main/scala/sbt/BasicKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ object BasicKeys {

val serverIdleTimeout =
AttributeKey[Option[FiniteDuration]](
"serverIdleTimeOut",
"serverIdleTimeout",
"If set to a defined value, sbt server will exit if it goes at least the specified duration without receiving any commands.",
10000
)
Expand Down
1 change: 1 addition & 0 deletions main/src/main/scala/sbt/Defaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ object Defaults extends BuildCommon {
outputStrategy :== None, // TODO - This might belong elsewhere.
buildStructure := Project.structure(state.value),
settingsData := buildStructure.value.data,
allScopes := ScopeFilter.allScopes.value,
checkBuildSources / aggregate :== false,
checkBuildSources / changedInputFiles / aggregate := false,
checkBuildSources / Continuous.dynamicInputs := None,
Expand Down
1 change: 1 addition & 0 deletions main/src/main/scala/sbt/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ object Keys {
val forcegc = settingKey[Boolean]("Enables (true) or disables (false) forcing garbage collection after task run when needed.").withRank(BMinusSetting)
val minForcegcInterval = settingKey[Duration]("Minimal interval to check for forcing garbage collection.")
val settingsData = std.FullInstance.settingsData
private[sbt] val allScopes = settingKey[ScopeFilter.AllScopes]("Internal use: a view of all scopes for filtering")
@cacheLevel(include = Array.empty)
val streams = taskKey[TaskStreams]("Provides streams for logging and persisting data.").withRank(DTask)
val taskDefinitionKey = Def.taskDefinitionKey
Expand Down
154 changes: 88 additions & 66 deletions main/src/main/scala/sbt/ScopeFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,54 @@
package sbt

import sbt.internal.{ Load, LoadedBuildUnit }
import sbt.internal.util.{ AttributeKey, Dag, Types }
import sbt.internal.util.{ AttributeKey, Dag }
import sbt.librarymanagement.{ ConfigRef, Configuration }
import Types.const
import sbt.internal.util.Types.const
import Def.Initialize
import sbt.Project.inScope
import java.net.URI

sealed abstract class ScopeFilter { self =>

/** Implements this filter. */
private[ScopeFilter] def apply(data: ScopeFilter.Data): Set[Scope]

/** Constructs a filter that selects values that match this filter but not `other`. */
def --(other: ScopeFilter): ScopeFilter = this && -other

/** Constructs a filter that selects values that match this filter and `other`. */
def &&(other: ScopeFilter): ScopeFilter = new ScopeFilter:
def apply(data: ScopeFilter.Data): Set[Scope] = self(data).intersect(other(data))

/** Constructs a filter that selects values that match this filter or `other`. */
def ||(other: ScopeFilter): ScopeFilter = new ScopeFilter:
def apply(data: ScopeFilter.Data): Set[Scope] = self(data) ++ other(data)

/** Constructs a filter that selects values that do not match this filter. */
def unary_- : ScopeFilter = new ScopeFilter:
def apply(data: ScopeFilter.Data): Set[Scope] = data.allScopes.set -- self(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[_]]

private type ScopeMap = Map[
ScopeAxis[Reference],
Map[
ScopeAxis[ConfigKey],
Map[ScopeAxis[AttributeKey[_]], Set[Scope]]
]
]

/**
* Construct a Scope filter from a sequence of individual scopes.
*/
def in(scopes: Seq[Scope]): ScopeFilter = {
def in(scopes: Seq[Scope]): ScopeFilter =
val scopeSet = scopes.toSet
new ScopeFilter {
override private[ScopeFilter] def apply(data: Data): Scope => Boolean = scopeSet.contains
}
}
new ScopeFilter:
def apply(data: Data): Set[Scope] = data.allScopes.set.intersect(scopeSet)

/**
* Constructs a Scope filter from filters for the individual axes.
Expand All @@ -45,64 +70,49 @@ object ScopeFilter {
configurations: ConfigurationFilter = zeroAxis,
tasks: TaskFilter = zeroAxis
): ScopeFilter =
new ScopeFilter {
private[sbt] def apply(data: Data): Scope => Boolean = {
new ScopeFilter:
def apply(data: Data): Set[Scope] =
val pf = projects(data)
val cf = configurations(data)
val tf = tasks(data)
s => pf(s.project) && cf(s.config) && tf(s.task)
}
}
val res =
for {
(project, configs) <- data.allScopes.grouped.iterator if pf(project)
(config, tasks) <- configs.iterator if cf(config)
(task, scopes) <- tasks.iterator if tf(task)
scope <- scopes
} yield scope
res.toSet

def debug(delegate: ScopeFilter): ScopeFilter =
new ScopeFilter {
private[sbt] def apply(data: Data): Scope => Boolean = {
val d = delegate(data)
scope => {
val accept = d(scope)
println((if (accept) "ACCEPT " else "reject ") + scope)
accept
}
}
}
new ScopeFilter:
def apply(data: Data): Set[Scope] =
val res = delegate(data)
println(s"ACCEPT $res")
res

final class SettingKeyAll[A] private[sbt] (i: Initialize[A]):

/**
* 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[A]] =
Def.flatMap(getData) { data =>
data.allScopes.toSeq
.withFilter(sfilter(data))
.map(s => Project.inScope(s, i))
.join
}
end SettingKeyAll
def all(sfilter: => ScopeFilter): Initialize[Seq[A]] = Def.flatMap(getData) { data =>
sfilter(data).toSeq.map(s => Project.inScope(s, i)).join
}

final class TaskKeyAll[A] private[sbt] (i: Initialize[Task[A]]):

/**
* 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[A]]] =
Def.flatMap(getData) { data =>
import std.TaskExtra._
data.allScopes.toSeq
.withFilter(sfilter(data))
.map(s => Project.inScope(s, i))
.join(_.join)
}
end TaskKeyAll
def all(sfilter: => ScopeFilter): Initialize[Task[Seq[A]]] = Def.flatMap(getData) { data =>
import std.TaskExtra._
sfilter(data).toSeq.map(s => Project.inScope(s, i)).join(_.join)
}

private[sbt] val Make = new Make {}
trait Make {

/** Selects the Scopes used in `<key>.all(<ScopeFilter>)`. */
type ScopeFilter = Base[Scope]

/** Selects Scopes with a Zero task axis. */
def inZeroTask: TaskFilter = zeroAxis[AttributeKey[_]]

Expand Down Expand Up @@ -191,25 +201,39 @@ object ScopeFilter {
selectAxis[ConfigKey](const(cs))
}

implicit def settingKeyAll[T](key: Initialize[T]): SettingKeyAll[T] = new SettingKeyAll[T](key)
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)
}

private[sbt] final class AllScopes(val set: Set[Scope], val grouped: ScopeMap)

/**
* Information provided to Scope filters. These provide project relationships,
* project reference resolution, and the list of all static Scopes.
*/
private[sbt] final class Data(
val units: Map[URI, LoadedBuildUnit],
val resolve: ProjectReference => ProjectRef,
val allScopes: Set[Scope]
val allScopes: AllScopes
)

private[sbt] val allScopes: Initialize[AllScopes] = Def.setting {
val scopes = Def.StaticScopes.value
val grouped: ScopeMap =
scopes
.groupBy(_.project)
.map { case (k, v) =>
k -> v.groupBy(_.config).map { case (k, v) => k -> v.groupBy(_.task) }
}
new AllScopes(scopes, grouped)
}

/** 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 scopes = Keys.allScopes.value
val thisRef = Keys.thisProjectRef.?.value
val current = thisRef match {
case Some(ProjectRef(uri, _)) => uri
Expand Down Expand Up @@ -262,51 +286,49 @@ object ScopeFilter {
selectAxis(data => projects(data).toSet)

private[this] def zeroAxis[T]: AxisFilter[T] = new AxisFilter[T] {
private[sbt] def apply(data: Data): ScopeAxis[T] => Boolean =
_ == Zero
private[sbt] def apply(data: Data): ScopeAxis[T] => Boolean = _ == Zero
}
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
}
_ 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 =>
/** Base functionality for filters on axis of type `In` that need access to build data. */
sealed abstract class AxisFilter[In] { self =>

/** Implements this filter. */
private[ScopeFilter] def apply(data: Data): In => Boolean
private[ScopeFilter] def apply(data: Data): ScopeAxis[In] => Boolean

/** Constructs a filter that selects values that match this filter but not `other`. */
def --(other: Base[In]): Base[In] = this && -other
def --(other: AxisFilter[In]): AxisFilter[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 = {
def &&(other: AxisFilter[In]): AxisFilter[In] = new AxisFilter[In] {
private[sbt] def apply(data: Data): ScopeAxis[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 = {
def ||(other: AxisFilter[In]): AxisFilter[In] = new AxisFilter[In] {
private[sbt] def apply(data: Data): ScopeAxis[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 = {
def unary_- : AxisFilter[In] = new AxisFilter[In] {
private[sbt] def apply(data: Data): ScopeAxis[In] => Boolean = {
val a = self(data)
s => !a(s)
}
Expand Down
2 changes: 1 addition & 1 deletion sbt-app/src/sbt-test/actions/multi-scope/build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
lazy val taskX = taskKey[Set[Int]]("numbers")
lazy val filterX = ScopeFilter( inDependencies(ThisProject, transitive=false, includeRoot=false) )

lazy val filterA: ScopeFilter.ScopeFilter = ScopeFilter(
lazy val filterA: ScopeFilter = ScopeFilter(
inAggregates( LocalProject(e.id) ),
inConfigurations(Compile,Test) || inZeroConfiguration,
inTasks(console) || inZeroTask
Expand Down

0 comments on commit a0597e9

Please sign in to comment.